@absolutejs/voice 0.0.22-beta.527 → 0.0.22-beta.529
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 +3 -1
- package/dist/client/htmxBootstrap.js +3 -1
- package/dist/client/index.js +290 -304
- package/dist/embed/index.js +3 -1
- package/dist/embed/voice-widget.js +7 -7
- package/dist/index.d.ts +4 -6
- package/dist/index.js +525 -905
- package/dist/internal/html.d.ts +6 -0
- package/dist/internal/status.d.ts +9 -0
- package/dist/react/index.js +287 -300
- package/dist/svelte/index.js +181 -198
- package/dist/testing/index.js +69 -69
- package/dist/vue/index.d.ts +1 -1
- package/dist/vue/index.js +231 -239
- package/package.json +2 -3
- package/dist/generated/htmxBootstrapBundle.d.ts +0 -1
- package/fixtures/README.md +0 -57
- package/fixtures/manifest.json +0 -358
- package/fixtures/pcm/dialogue-three-clean.pcm +0 -0
- package/fixtures/pcm/dialogue-three-mixed.pcm +0 -0
- package/fixtures/pcm/dialogue-two-clean.pcm +0 -0
- package/fixtures/pcm/dialogue-two-noisy.pcm +0 -0
- package/fixtures/pcm/multiturn-three-mixed.pcm +0 -0
- package/fixtures/pcm/multiturn-two-clean.pcm +0 -0
- package/fixtures/pcm/quietly-alone-clean.pcm +0 -0
- package/fixtures/pcm/rainstorms-noisy.pcm +0 -0
- package/fixtures/pcm/stella-bulgaria-bulgarian20.pcm +0 -0
- package/fixtures/pcm/stella-ghana-english507.pcm +0 -0
- package/fixtures/pcm/stella-india-english37.pcm +0 -0
- package/fixtures/pcm/stella-jamaica-jamaican-creole-english1.pcm +0 -0
- package/fixtures/pcm/stella-liberia-liberian-pidgin-english2.pcm +0 -0
- package/fixtures/pcm/stella-pakistan-english519.pcm +0 -0
- package/fixtures/pcm/stella-sierra-leone-krio5.pcm +0 -0
- package/fixtures/pcm/stella-singapore-english655.pcm +0 -0
- package/fixtures/pcm/traveled-back-route-clean.pcm +0 -0
package/dist/index.js
CHANGED
|
@@ -251,6 +251,9 @@ var conditionAudioChunk = (audio, config) => {
|
|
|
251
251
|
import { Elysia as Elysia70 } from "elysia";
|
|
252
252
|
import { resolve } from "path";
|
|
253
253
|
|
|
254
|
+
// src/internal/html.ts
|
|
255
|
+
var escapeHtml = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
256
|
+
|
|
254
257
|
// src/htmx.ts
|
|
255
258
|
var DEFAULT_HTMX_TARGETS = {
|
|
256
259
|
assistant: "voice-htmx-assistant",
|
|
@@ -259,7 +262,6 @@ var DEFAULT_HTMX_TARGETS = {
|
|
|
259
262
|
status: "voice-htmx-status",
|
|
260
263
|
turns: "voice-htmx-turns"
|
|
261
264
|
};
|
|
262
|
-
var escapeHtml = (text) => text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
263
265
|
var stringifyResult = (result) => {
|
|
264
266
|
if (result === undefined) {
|
|
265
267
|
return "";
|
|
@@ -2724,10 +2726,9 @@ var renderVoiceCallReviewMarkdown = (artifact) => {
|
|
|
2724
2726
|
].filter((value) => typeof value === "string").join(`
|
|
2725
2727
|
`);
|
|
2726
2728
|
};
|
|
2727
|
-
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2728
2729
|
var renderVoiceCallReviewHTML = (artifact) => {
|
|
2729
|
-
const notes = artifact.notes.map((note) => `<li>${
|
|
2730
|
-
const latency = artifact.latencyBreakdown.map((entry) => `<li><strong>${
|
|
2730
|
+
const notes = artifact.notes.map((note) => `<li>${escapeHtml(note)}</li>`).join("");
|
|
2731
|
+
const latency = artifact.latencyBreakdown.map((entry) => `<li><strong>${escapeHtml(entry.label)}:</strong> ${roundMetric(entry.valueMs)}ms</li>`).join("");
|
|
2731
2732
|
const transport = summarizeTimelineTraffic(artifact.timeline).map((summary) => {
|
|
2732
2733
|
const parts = [`${summary.count}`, "events"];
|
|
2733
2734
|
if (summary.bytes > 0) {
|
|
@@ -2736,15 +2737,15 @@ var renderVoiceCallReviewHTML = (artifact) => {
|
|
|
2736
2737
|
if ((summary.audioMs ?? 0) > 0) {
|
|
2737
2738
|
parts.push(`${roundMetric(summary.audioMs)}ms audio`);
|
|
2738
2739
|
}
|
|
2739
|
-
return `<li><strong>${
|
|
2740
|
+
return `<li><strong>${escapeHtml(summary.label)}:</strong> ${escapeHtml(parts.join(", "))}</li>`;
|
|
2740
2741
|
}).join("");
|
|
2741
|
-
const timeline = compactTimeline(artifact.timeline.filter((entry) => !isLowSignalTimelineEvent(entry))).map((line) => `<li>${
|
|
2742
|
+
const timeline = compactTimeline(artifact.timeline.filter((entry) => !isLowSignalTimelineEvent(entry))).map((line) => `<li>${escapeHtml(line.replace(/^- /u, ""))}</li>`).join("");
|
|
2742
2743
|
return `<!doctype html>
|
|
2743
2744
|
<html lang="en">
|
|
2744
2745
|
<head>
|
|
2745
2746
|
<meta charset="utf-8" />
|
|
2746
2747
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
2747
|
-
<title>${
|
|
2748
|
+
<title>${escapeHtml(artifact.title)}</title>
|
|
2748
2749
|
<style>
|
|
2749
2750
|
:root { color-scheme: dark; }
|
|
2750
2751
|
body { font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; background: #0b0d10; color: #f4f4f5; }
|
|
@@ -2763,7 +2764,7 @@ var renderVoiceCallReviewHTML = (artifact) => {
|
|
|
2763
2764
|
<body>
|
|
2764
2765
|
<main>
|
|
2765
2766
|
<section>
|
|
2766
|
-
<h1>${
|
|
2767
|
+
<h1>${escapeHtml(artifact.title)}</h1>
|
|
2767
2768
|
<div class="grid">
|
|
2768
2769
|
<div class="metric"><div class="label">Pass</div><div class="value">${artifact.summary.pass ? "yes" : "no"}</div></div>
|
|
2769
2770
|
<div class="metric"><div class="label">First Turn</div><div class="value">${artifact.summary.firstTurnLatencyMs ?? "n/a"}ms</div></div>
|
|
@@ -2774,8 +2775,8 @@ var renderVoiceCallReviewHTML = (artifact) => {
|
|
|
2774
2775
|
<section>
|
|
2775
2776
|
<h2>Transcript</h2>
|
|
2776
2777
|
<ul>
|
|
2777
|
-
<li><strong>Expected:</strong> ${
|
|
2778
|
-
<li><strong>Actual:</strong> ${
|
|
2778
|
+
<li><strong>Expected:</strong> ${escapeHtml(artifact.transcript.expected ?? "n/a")}</li>
|
|
2779
|
+
<li><strong>Actual:</strong> ${escapeHtml(artifact.transcript.actual || "n/a")}</li>
|
|
2779
2780
|
</ul>
|
|
2780
2781
|
</section>
|
|
2781
2782
|
<section>
|
|
@@ -2796,7 +2797,7 @@ var renderVoiceCallReviewHTML = (artifact) => {
|
|
|
2796
2797
|
</section>
|
|
2797
2798
|
<section>
|
|
2798
2799
|
<h2>Config</h2>
|
|
2799
|
-
<pre>${
|
|
2800
|
+
<pre>${escapeHtml(JSON.stringify(artifact.config ?? {}, null, 2))}</pre>
|
|
2800
2801
|
</section>
|
|
2801
2802
|
</main>
|
|
2802
2803
|
</body>
|
|
@@ -5748,9 +5749,6 @@ var createVoiceSession = (options) => {
|
|
|
5748
5749
|
return api;
|
|
5749
5750
|
};
|
|
5750
5751
|
|
|
5751
|
-
// src/generated/htmxBootstrapBundle.ts
|
|
5752
|
-
var HTMX_BOOTSTRAP_BUNDLE = 'var Hn=(n)=>{if(typeof n!=="string")return n;return document.querySelector(n)},Gn=(n,c,o,e)=>{let i=c??n.getAttribute("hx-get")??"";if(!i)return"";let l=new URL(i,window.location.origin);if(e)l.searchParams.set(o,e);else l.searchParams.delete(o);return`${l.pathname}${l.search}${l.hash}`},gn=(n,c)=>{if(typeof window>"u"||typeof document>"u")return()=>{};let o=Hn(c.element);if(!o)return()=>{};let e=c.eventName??"voice-refresh",i=c.sessionQueryParam??"sessionId",l=()=>{let d=window,g=Gn(o,c.route,i,n.sessionId);if(g)o.setAttribute("hx-get",g);d.htmx?.process?.(o),d.htmx?.trigger?.(o,e)},t=n.subscribe(l);return l(),()=>{t()}};var Bn=(n)=>Math.max(-1,Math.min(1,n)),Wn=(n)=>{let c=new Int16Array(n.length);for(let o=0;o<n.length;o+=1){let e=Bn(n[o]??0);c[o]=e<0?e*32768:e*32767}return new Uint8Array(c.buffer)},$n=(n)=>{let c=n instanceof Uint8Array?n:new Uint8Array(n);if(c.byteLength<2)return 0;let o=new Int16Array(c.buffer,c.byteOffset,Math.floor(c.byteLength/2));if(o.length===0)return 0;let e=0;for(let i of o){let l=i/32768;e+=l*l}return Math.min(1,Math.max(0,Math.sqrt(e/o.length)*5.5))},qn=(n,c,o)=>{if(c===o)return n;let e=c/o,i=Math.round(n.length/e),l=new Float32Array(i),t=0,d=0;while(t<l.length){let g=Math.round((t+1)*e),y=0,a=0;for(let h=d;h<g&&h<n.length;h+=1)y+=n[h]??0,a+=1;l[t]=a>0?y/a:0,t+=1,d=g}return l},An=(n)=>{let c=null,o=null,e=null,i=null;return{start:async()=>{if(typeof navigator>"u"||!navigator.mediaDevices?.getUserMedia)throw Error("Browser microphone capture requires navigator.mediaDevices.getUserMedia.");let d=(typeof window<"u"?window.AudioContext??window.webkitAudioContext:void 0)??AudioContext;if(!d)throw Error("Browser microphone capture requires AudioContext support.");i=await navigator.mediaDevices.getUserMedia({audio:{channelCount:n.channelCount??1}}),c=new d,o=c.createMediaStreamSource(i),e=c.createScriptProcessor(4096,1,1),e.onaudioprocess=(g)=>{let y=g.inputBuffer.getChannelData(0),a=qn(y,c?.sampleRate??48000,n.sampleRateHz??16000),h=Wn(a);n.onLevel?.($n(h)),n.onAudio(h)},o.connect(e),e.connect(c.destination)},stop:()=>{e?.disconnect(),o?.disconnect(),i?.getTracks().forEach((d)=>d.stop()),c?.close(),n.onLevel?.(0),c=null,i=null,e=null,o=null}}};var nn=(n)=>{if(typeof n==="string"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n==="object"){let c=n;for(let o of["message","reason","description"]){let e=c[o];if(typeof e==="string"&&e.trim())return e}if("error"in c)return nn(c.error);if("cause"in c)return nn(c.cause);try{return JSON.stringify(n)}catch{}}return"Unexpected error"},hn=(n)=>{switch(n.type){case"audio":return{chunk:Uint8Array.from(atob(n.chunkBase64),(c)=>c.charCodeAt(0)),format:n.format,receivedAt:n.receivedAt,turnId:n.turnId,type:"audio"};case"assistant":return{text:n.text,type:"assistant"};case"complete":return{sessionId:n.sessionId,type:"complete"};case"connection":return{reconnect:n.reconnect,type:"connection"};case"call_lifecycle":return{event:n.event,sessionId:n.sessionId,type:"call_lifecycle"};case"error":return{message:nn(n.message),type:"error"};case"final":return{transcript:n.transcript,type:"final"};case"partial":return{transcript:n.transcript,type:"partial"};case"replay":return{assistantTexts:n.assistantTexts,call:n.call,partial:n.partial,scenarioId:n.scenarioId,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,status:n.status,turns:n.turns,type:"replay"};case"session":return{sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,type:"session"};case"turn":return{turn:n.turn,type:"turn"};default:return null}};var Hc=Math.PI*2;var G=(n,c,o,e)=>{n.push({code:o,message:e,severity:c})};var Xn=(n)=>n.length===0?void 0:n.reduce((c,o)=>c+o,0)/n.length,K=(n)=>n.length===0?void 0:Math.max(...n);var b=(n,c)=>{let o=n[c];return typeof o==="number"&&Number.isFinite(o)?o:void 0},Z=(n,c)=>{let o=n[c];return typeof o==="boolean"?o:void 0},O=(n,c)=>{let o=n[c];return typeof o==="string"?o:void 0},cn=(n)=>String(n.id??O(n,"ssrc")??b(n,"ssrc")??O(n,"trackIdentifier")??O(n,"mid")??"unknown"),yn=(n)=>n===void 0?void 0:n*1000;var un=(n)=>{let c={};for(let[o,e]of Object.entries(n))if(e===null||typeof e==="boolean"||typeof e==="number"||typeof e==="string")c[o]=e;return c};var Cn=(n={})=>{let c=n.stats??[],o=[],e=c.filter((s)=>s.type==="inbound-rtp"&&O(s,"kind")!=="video"),i=c.filter((s)=>s.type==="outbound-rtp"&&O(s,"kind")!=="video"),l=c.filter((s)=>s.type==="candidate-pair"),t=c.filter((s)=>(s.type==="track"||s.type==="media-source")&&O(s,"kind")==="audio"),d=l.filter((s)=>Z(s,"selected")===!0||Z(s,"nominated")===!0||O(s,"state")==="succeeded").length,g=t.filter((s)=>O(s,"readyState")!=="ended"&&O(s,"trackState")!=="ended"&&Z(s,"ended")!==!0).length,y=t.filter((s)=>O(s,"readyState")==="ended"||O(s,"trackState")==="ended"||Z(s,"ended")===!0).length,a=e.reduce((s,V)=>s+(b(V,"packetsReceived")??0),0),h=i.reduce((s,V)=>s+(b(V,"packetsSent")??0),0),r=[...e,...i].reduce((s,V)=>s+Math.max(0,b(V,"packetsLost")??0),0),M=a+r,C=M===0?0:r/M,S=e.reduce((s,V)=>s+(b(V,"bytesReceived")??0),0),w=i.reduce((s,V)=>s+(b(V,"bytesSent")??0),0),E=K(l.map((s)=>yn(b(s,"currentRoundTripTime")??b(s,"roundTripTime"))).filter((s)=>s!==void 0)),_=K([...e,...i].map((s)=>yn(b(s,"jitter"))).filter((s)=>s!==void 0)),U=K(e.map((s)=>{let V=b(s,"jitterBufferDelay"),R=b(s,"jitterBufferEmittedCount");return V!==void 0&&R!==void 0&&R>0?V/R*1000:void 0}).filter((s)=>s!==void 0)),N=t.map((s)=>b(s,"audioLevel")).filter((s)=>s!==void 0);if(n.requireConnectedCandidatePair&&l.length>0&&d===0)G(o,"error","media.webrtc_candidate_pair_missing","No active WebRTC candidate pair was observed.");if(n.requireLiveAudioTrack&&g===0)G(o,"error","media.webrtc_audio_track_missing","No live WebRTC audio track was observed.");if(n.maxPacketLossRatio!==void 0&&C>n.maxPacketLossRatio)G(o,"warning","media.webrtc_packet_loss",`Observed WebRTC packet loss ratio ${String(C)} above ${String(n.maxPacketLossRatio)}.`);if(n.maxRoundTripTimeMs!==void 0&&E!==void 0&&E>n.maxRoundTripTimeMs)G(o,"warning","media.webrtc_round_trip_time",`Observed WebRTC RTT ${String(E)}ms above ${String(n.maxRoundTripTimeMs)}ms.`);if(n.maxJitterMs!==void 0&&_!==void 0&&_>n.maxJitterMs)G(o,"warning","media.webrtc_jitter",`Observed WebRTC jitter ${String(_)}ms above ${String(n.maxJitterMs)}ms.`);return{activeCandidatePairs:d,audioLevelAverage:Xn(N),bytesReceived:S,bytesSent:w,checkedAt:Date.now(),endedAudioTracks:y,inboundPackets:a,issues:o,jitterBufferDelayMs:U,jitterMs:_,liveAudioTracks:g,outboundPackets:h,packetLossRatio:C,packetsLost:r,roundTripTimeMs:E,status:o.some((s)=>s.severity==="error")?"fail":o.length>0?"warn":"pass",totalStats:c.length}},Tn=async(n)=>{return[...(await n.peerConnection.getStats(n.selector??null)).values()].map(un)};var In=(n={})=>{let c=n.stats??[],o=n.previousStats??[],e=[],i=new Map(o.map((r)=>[cn(r),r])),t=c.filter((r)=>(r.type==="inbound-rtp"||r.type==="outbound-rtp")&&O(r,"kind")!=="video"&&O(r,"mediaType")!=="video").map((r)=>{let M=r.type==="outbound-rtp"?"outbound":"inbound",C=M==="outbound"?"packetsSent":"packetsReceived",S=M==="outbound"?"bytesSent":"bytesReceived",w=i.get(cn(r)),E=b(r,C),_=w?b(w,C):void 0,U=b(r,S),N=w?b(w,S):void 0,s=r.timestamp!==void 0&&w?.timestamp!==void 0?r.timestamp-w.timestamp:void 0;return{bytesDelta:U!==void 0&&N!==void 0?U-N:void 0,currentPackets:E,direction:M,id:cn(r),packetDelta:E!==void 0&&_!==void 0?E-_:void 0,previousPackets:_,timeDeltaMs:s}}),d=t.filter((r)=>r.direction==="inbound"),g=t.filter((r)=>r.direction==="outbound"),y=K(t.map((r)=>r.timeDeltaMs).filter((r)=>r!==void 0)),a=d.filter((r)=>n.maxInboundPacketStallMs!==void 0&&r.timeDeltaMs!==void 0&&r.timeDeltaMs>=n.maxInboundPacketStallMs&&r.packetDelta!==void 0&&r.packetDelta<=0).length,h=g.filter((r)=>n.maxOutboundPacketStallMs!==void 0&&r.timeDeltaMs!==void 0&&r.timeDeltaMs>=n.maxOutboundPacketStallMs&&r.packetDelta!==void 0&&r.packetDelta<=0).length;if(n.requireInboundAudio&&d.length===0)G(e,"error","media.webrtc_inbound_audio_missing","No inbound WebRTC audio RTP stream was observed.");if(n.requireOutboundAudio&&g.length===0)G(e,"error","media.webrtc_outbound_audio_missing","No outbound WebRTC audio RTP stream was observed.");if(n.maxGapMs!==void 0&&y!==void 0&&y>n.maxGapMs)G(e,"warning","media.webrtc_stream_gap",`Observed WebRTC stream sample gap ${String(y)}ms above ${String(n.maxGapMs)}ms.`);if(a>0)G(e,"error","media.webrtc_inbound_stalled",`${String(a)} inbound WebRTC audio stream(s) stopped receiving packets.`);if(h>0)G(e,"error","media.webrtc_outbound_stalled",`${String(h)} outbound WebRTC audio stream(s) stopped sending packets.`);return{checkedAt:Date.now(),inboundAudioStreams:d.length,issues:e,maxObservedGapMs:y,outboundAudioStreams:g.length,stalledInboundStreams:a,stalledOutboundStreams:h,status:e.some((r)=>r.severity==="error")?"fail":e.length>0?"warn":"pass",streams:t,totalStats:c.length}};var Yn="/api/voice/browser-media",Jn=5000,Qn=async(n)=>n.peerConnection??await n.getPeerConnection?.()??null,kn=async(n,c)=>{let o=c.fetch??globalThis.fetch;if(!o)return;await o(c.path??Yn,{body:JSON.stringify(n),headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"})},Vn=(n)=>{let c=null,o=[],e=async()=>{let t=await Qn(n);if(!t)return;let d=await Tn({peerConnection:t}),g=Cn({...n,stats:d}),y=n.continuity===!1?void 0:In({...n.continuity,previousStats:o,stats:d}),a={at:Date.now(),continuity:y,report:g,scenarioId:n.getScenarioId?.()??null,sessionId:n.getSessionId?.()??null};return o=d,n.onReport?.(a),await kn(a,n),a},i=()=>{e().catch((t)=>{n.onError?.(t)})},l=()=>{if(c)clearInterval(c),c=null};return{close:l,reportOnce:e,start:()=>{if(c)return;i(),c=setInterval(i,n.intervalMs??Jn)},stop:l}};var W=()=>{},zn=()=>W,Zn={callControl:W,close:W,endTurn:W,getReadyState:()=>3,getScenarioId:()=>"",getSessionId:()=>"",send:W,sendAudio:W,simulateDisconnect:W,start:()=>{},subscribe:zn},Kn=()=>crypto.randomUUID(),jn=(n,c,o)=>{let{hostname:e,port:i,protocol:l}=window.location,t=l==="https:"?"wss:":"ws:",d=i?`:${i}`:"",g=new URL(`${t}//${e}${d}${n}`);if(g.searchParams.set("sessionId",c),o)g.searchParams.set("scenarioId",o);return g.toString()},Fn=(n)=>{if(!n||typeof n!=="object"||!("type"in n))return!1;switch(n.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}},vn=(n)=>{if(typeof n.data!=="string")return null;try{let c=JSON.parse(n.data);return Fn(c)?c:null}catch{return null}},Mn=(n,c={})=>{if(typeof window>"u")return Zn;let o=new Set,e=c.reconnect!==!1,i=c.maxReconnectAttempts??10,l=c.pingInterval??30000,t={isConnected:!1,pendingMessages:[],scenarioId:c.scenarioId??null,pingInterval:null,reconnectAttempts:0,reconnectTimeout:null,sessionId:c.sessionId??Kn(),ws:null},d=(s)=>{o.forEach((V)=>V(s))},g=()=>{if(t.pingInterval)clearInterval(t.pingInterval),t.pingInterval=null;if(t.reconnectTimeout)clearTimeout(t.reconnectTimeout),t.reconnectTimeout=null},y=()=>{if(t.ws?.readyState!==1)return;while(t.pendingMessages.length>0){let s=t.pendingMessages.shift();if(s!==void 0)t.ws.send(s)}},a=()=>{let s=Date.now()+500;t.reconnectAttempts+=1,d({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,nextAttemptAt:s,status:"reconnecting"},type:"connection"}),t.reconnectTimeout=setTimeout(()=>{if(t.reconnectAttempts>i){d({reconnect:{attempts:t.reconnectAttempts,maxAttempts:i,status:"exhausted"},type:"connection"});return}h()},500)},h=()=>{let s=new WebSocket(jn(n,t.sessionId,t.scenarioId));s.binaryType="arraybuffer",s.onopen=()=>{let V=t.reconnectAttempts>0;if(t.isConnected=!0,y(),V)d({reconnect:{attempts:t.reconnectAttempts,lastResumedAt:Date.now(),maxAttempts:i,status:"resumed"},type:"connection"}),t.reconnectAttempts=0;o.forEach((R)=>R({scenarioId:t.scenarioId??void 0,sessionId:t.sessionId,status:"active",type:"session"})),t.pingInterval=setInterval(()=>{if(s.readyState===1)s.send(JSON.stringify({type:"ping"}))},l)},s.onmessage=(V)=>{let R=vn(V);if(!R)return;if(R.type==="session")t.sessionId=R.sessionId,t.scenarioId=R.scenarioId??t.scenarioId;o.forEach((Y)=>Y(R))},s.onclose=(V)=>{if(t.isConnected=!1,g(),e&&V.code!==1000&&t.reconnectAttempts<i)a();else if(e&&V.code!==1000)d({reconnect:{attempts:t.reconnectAttempts,lastDisconnectAt:Date.now(),maxAttempts:i,status:"exhausted"},type:"connection"})},t.ws=s},r=(s)=>{if(t.ws?.readyState===1){t.ws.send(s);return}t.pendingMessages.push(s)},M=(s)=>{r(JSON.stringify(s))},C=(s={})=>{if(s.sessionId)t.sessionId=s.sessionId;if(s.scenarioId)t.scenarioId=s.scenarioId;M({type:"start",sessionId:t.sessionId,scenarioId:t.scenarioId??void 0})},S=(s)=>{r(s)},w=()=>{M({type:"end_turn"})},E=(s)=>{M({...s,type:"call_control"})},_=()=>{if(g(),t.ws)t.ws.close(1000),t.ws=null;t.isConnected=!1,o.clear()},U=()=>{if(t.ws?.readyState===1)t.ws.close(4000,"absolutejs-voice-reconnect-proof")},N=(s)=>{return o.add(s),()=>{o.delete(s)}};return h(),{callControl:E,close:_,endTurn:w,getReadyState:()=>t.ws?.readyState??3,getScenarioId:()=>t.scenarioId??"",getSessionId:()=>t.sessionId,send:M,sendAudio:S,simulateDisconnect:U,start:C,subscribe:N}};var mn=()=>({attempts:0,maxAttempts:0,status:"idle"}),pn=()=>({assistantAudio:[],assistantTexts:[],call:null,error:null,isConnected:!1,sessionMetadata:null,scenarioId:null,partial:"",reconnect:mn(),sessionId:null,status:"idle",turns:[]}),Sn=()=>{let n=pn(),c=new Set,o=()=>{c.forEach((i)=>i())};return{dispatch:(i)=>{switch(i.type){case"audio":n={...n,assistantAudio:[...n.assistantAudio,{chunk:i.chunk,format:i.format,receivedAt:i.receivedAt,turnId:i.turnId}]};break;case"assistant":n={...n,assistantTexts:[...n.assistantTexts,i.text]};break;case"complete":n={...n,sessionId:i.sessionId,status:"completed"};break;case"call_lifecycle":n={...n,call:{...n.call,disposition:i.event.type==="end"?i.event.disposition:n.call?.disposition,endedAt:i.event.type==="end"?i.event.at:n.call?.endedAt,events:[...n.call?.events??[],i.event],lastEventAt:i.event.at,startedAt:n.call?.startedAt??i.event.at},sessionId:i.sessionId};break;case"connected":n={...n,isConnected:!0,reconnect:n.reconnect.status==="reconnecting"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:n.reconnect};break;case"connection":n={...n,reconnect:i.reconnect};break;case"disconnected":n={...n,isConnected:!1};break;case"error":n={...n,error:i.message};break;case"final":n={...n,partial:i.transcript.text,turns:n.turns.map((l)=>l)};break;case"partial":n={...n,partial:i.transcript.text};break;case"replay":n={...n,assistantTexts:[...i.assistantTexts],call:i.call??null,error:null,isConnected:i.status==="active",partial:i.partial,reconnect:n.reconnect.status==="reconnecting"?{...n.reconnect,lastResumedAt:Date.now(),nextAttemptAt:void 0,status:"resumed"}:n.reconnect,scenarioId:i.scenarioId??n.scenarioId,sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status,turns:[...i.turns]};break;case"session":n={...n,error:null,scenarioId:i.scenarioId??n.scenarioId,isConnected:i.status==="active",sessionId:i.sessionId,sessionMetadata:i.sessionMetadata??n.sessionMetadata,status:i.status};break;case"turn":n={...n,partial:"",turns:[...n.turns,i.turn]};break}o()},getServerSnapshot:()=>n,getSnapshot:()=>n,subscribe:(i)=>{return c.add(i),()=>{c.delete(i)}}}};var Ln=(n,c={})=>{let o=Mn(n,c),e=Sn(),i=c.browserMedia&&typeof window<"u"?Vn({...c.browserMedia,getScenarioId:()=>c.browserMedia?c.browserMedia.getScenarioId?.()??o.getScenarioId():o.getScenarioId(),getSessionId:()=>c.browserMedia?c.browserMedia.getSessionId?.()??o.getSessionId():o.getSessionId()}):null,l=new Set,t=(a)=>Promise.resolve().then(()=>{if(!a?.sessionId&&!a?.scenarioId)return;o.start(a),i?.start()}),d=()=>{l.forEach((a)=>a())},g=()=>{if(!c.reconnectReportPath||typeof fetch>"u")return;let a=e.getSnapshot(),h=JSON.stringify({at:Date.now(),reconnect:a.reconnect,scenarioId:a.scenarioId,sessionId:o.getSessionId(),turnIds:a.turns.map((r)=>r.id)});fetch(c.reconnectReportPath,{body:h,headers:{"Content-Type":"application/json"},keepalive:!0,method:"POST"}).catch(()=>{})},y=o.subscribe((a)=>{let h=hn(a);if(h){if(e.dispatch(h),a.type==="connection")g();d()}});return{callControl(a){o.callControl(a)},close(){y(),i?.close(),o.close(),e.dispatch({type:"disconnected"}),d()},endTurn(){o.endTurn()},get error(){return e.getSnapshot().error},getServerSnapshot(){return e.getServerSnapshot()},getSnapshot(){return e.getSnapshot()},get isConnected(){return e.getSnapshot().isConnected},get scenarioId(){return e.getSnapshot().scenarioId},get sessionMetadata(){return e.getSnapshot().sessionMetadata},start:t,get partial(){return e.getSnapshot().partial},get reconnect(){return e.getSnapshot().reconnect},get sessionId(){return o.getSessionId()},get status(){return e.getSnapshot().status},get turns(){return e.getSnapshot().turns},get assistantTexts(){return e.getSnapshot().assistantTexts},get assistantAudio(){return e.getSnapshot().assistantAudio},get call(){return e.getSnapshot().call},sendAudio(a){o.sendAudio(a)},simulateDisconnect(){o.simulateDisconnect()},subscribe(a){return l.add(a),()=>{l.delete(a)}}}};var wn=(n)=>{if(!n||n.enabled===!1)return;return{enabled:!0,maxGain:n.maxGain??3,noiseGateAttenuation:n.noiseGateAttenuation??0.15,noiseGateThreshold:n.noiseGateThreshold??0.006,targetLevel:n.targetLevel??0.08}};var nc={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}},cc={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 Rn=(n)=>{let c=n?.profile??"fast",o=n?.qualityProfile??"general",e=nc[c],i=cc[o];return{profile:c,qualityProfile:o,silenceMs:n?.silenceMs??i.silenceMs??e.silenceMs,speechThreshold:n?.speechThreshold??i.speechThreshold??e.speechThreshold,transcriptStabilityMs:n?.transcriptStabilityMs??i.transcriptStabilityMs??e.transcriptStabilityMs}};var ec={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"}}},_n=(n="default")=>{let c=ec[n];return{audioConditioning:wn(c.audioConditioning),capture:{channelCount:c.capture?.channelCount??1,sampleRateHz:c.capture?.sampleRateHz??16000},connection:{...c.connection},name:n,sttLifecycle:c.sttLifecycle??"continuous",turnDetection:Rn(c.turnDetection)}};var oc=(n)=>({assistantAudio:[...n.assistantAudio],assistantTexts:[...n.assistantTexts],call:n.call,error:n.error,isConnected:n.isConnected,isRecording:!1,partial:n.partial,reconnect:n.reconnect,recordingError:null,sessionId:n.sessionId,sessionMetadata:n.sessionMetadata,scenarioId:n.scenarioId,status:n.status,turns:[...n.turns]}),j=(n,c={})=>{let o=_n(c.preset),e=Ln(n,{...o.connection,...c.connection}),i=null,l=oc(e),t=new Set,d=()=>{for(let C of t)C()},g=()=>{if(l={...l,assistantAudio:[...e.assistantAudio],assistantTexts:[...e.assistantTexts],call:e.call,error:e.error,isConnected:e.isConnected,partial:e.partial,reconnect:e.reconnect,sessionId:e.sessionId,sessionMetadata:e.sessionMetadata,scenarioId:e.scenarioId,status:e.status,turns:[...e.turns]},c.autoStopOnComplete!==!1&&l.status==="completed"&&l.isRecording)i?.stop(),i=null,l={...l,isRecording:!1};d()},y=e.subscribe(g);g();let a=()=>{if(i)return i;return i=An({channelCount:c.capture?.channelCount??o.capture.channelCount,onLevel:c.capture?.onLevel,onAudio:(C)=>{if(c.capture?.onAudio){c.capture.onAudio(C,e.sendAudio);return}e.sendAudio(C)},sampleRateHz:c.capture?.sampleRateHz??o.capture.sampleRateHz}),i},h=()=>{i?.stop(),i=null,l={...l,isRecording:!1},d()},r=async()=>{if(l.isRecording)return;try{l={...l,recordingError:null},d(),await a().start(),l={...l,isRecording:!0},d()}catch(C){throw i=null,l={...l,isRecording:!1,recordingError:C instanceof Error?C.message:String(C)},d(),C}};return{bindHTMX(C){return gn(e,C)},callControl:(C)=>e.callControl(C),close:()=>{y(),h(),e.close()},endTurn:()=>e.endTurn(),get error(){return l.error},getServerSnapshot:()=>l,getSnapshot:()=>l,get isConnected(){return l.isConnected},get isRecording(){return l.isRecording},get partial(){return l.partial},get recordingError(){return l.recordingError},get reconnect(){return l.reconnect},sendAudio:(C)=>e.sendAudio(C),simulateDisconnect:()=>e.simulateDisconnect(),get sessionId(){return l.sessionId},get sessionMetadata(){return l.sessionMetadata},get scenarioId(){return l.scenarioId},startRecording:r,get status(){return l.status},stopRecording:h,subscribe:(C)=>{return t.add(C),()=>{t.delete(C)}},toggleRecording:async()=>{if(l.isRecording){h();return}await r()},get turns(){return l.turns},get assistantTexts(){return l.assistantTexts},get assistantAudio(){return l.assistantAudio},get call(){return l.call}}};var tc=()=>({activeSourceCount:0,error:null,isActive:!1,isPlaying:!1,lastInterruptLatencyMs:void 0,lastPlaybackStopLatencyMs:void 0,processedChunkCount:0,queuedChunkCount:0}),ic=()=>{if(typeof window>"u")return typeof AudioContext>"u"?void 0:AudioContext;return window.AudioContext??window.webkitAudioContext},lc=(n,c)=>{let o=c.format;if(o.container!=="raw"||o.encoding!=="pcm_s16le")throw Error(`Unsupported assistant audio format: ${o.container}/${o.encoding}`);let e=c.chunk,i=Math.max(1,o.channels),l=Math.floor(e.byteLength/2),t=Math.max(1,Math.floor(l/i)),d=n.createBuffer(i,t,o.sampleRateHz),g=new DataView(e.buffer,e.byteOffset,e.byteLength);for(let y=0;y<i;y+=1){let a=d.getChannelData(y);for(let h=0;h<t;h+=1){let M=(h*i+y)*2;if(M+1>=e.byteLength){a[h]=0;continue}a[h]=g.getInt16(M,!0)/32768}}return d},F=(n,c={})=>{let o=new Set,e=new Set,i=(c.lookaheadMs??15)/1000,l=tc(),t=null,d=null,g=0,y=Promise.resolve(),a=null,h=null,r=null,M=null,C=()=>{for(let A of o)A()},S=(A)=>{l={...l,...A},C()},w=()=>{if(l.error!==null)S({error:null})},E=()=>{if(M!==null)clearTimeout(M),M=null},_=(A)=>{E(),a=null,S({activeSourceCount:e.size,isPlaying:!1,lastInterruptLatencyMs:A,lastPlaybackStopLatencyMs:l.lastPlaybackStopLatencyMs??A}),r?.(),r=null,h=null},U=(A)=>{if(!A)return 0;return Math.max(0,((A.baseLatency??0)+(A.outputLatency??0))*1000)},N=(A)=>{if(!d)return;let T=1;if(d.gain.setValueAtTime){d.gain.setValueAtTime(T,A?.currentTime??0);return}d.gain.value=T},s=(A)=>{if(!d)return;let T=0;if(d.gain.setValueAtTime){d.gain.setValueAtTime(T,A?.currentTime??0);return}d.gain.value=T},V=()=>{if(a===null||e.size>0)return;_(Date.now()-a)},R=async()=>{if(t)return t;if(c.createAudioContext)t=c.createAudioContext();else{let A=ic();if(!A)throw Error("Assistant audio playback requires AudioContext support.");t=new A}if(t.createGain)d=t.createGain(),d.connect?.(t.destination);return g=t.currentTime,t},Y=async(A)=>{let T=await R(),P=lc(T,A),L=T.createBufferSource();L.buffer=P,L.connect(d??T.destination),L.onended=()=>{e.delete(L),L.disconnect?.(),S({activeSourceCount:e.size,isPlaying:e.size>0&&l.isActive}),V()};let Q=Math.max(T.currentTime+i,g);g=Q+P.duration,e.add(L),S({activeSourceCount:e.size,isPlaying:!0}),L.start(Q)},f=(A)=>{for(let T of[...e])T.stop?.();if(g=t?t.currentTime:0,A?.forceClear){for(let T of e)T.disconnect?.();e.clear(),V()}},$=async()=>{if(!l.isActive)return;let A=n.assistantAudio.slice(l.processedChunkCount);if(A.length===0)return;try{w();for(let T of A)await Y(T);S({processedChunkCount:n.assistantAudio.length,queuedChunkCount:l.queuedChunkCount+A.length})}catch(T){S({error:T instanceof Error?T.message:String(T)})}},D=()=>{return y=y.then(()=>$(),()=>$()),y},q=n.subscribe(()=>{if(c.autoStart&&!l.isActive&&n.assistantAudio.length>0){H.start();return}if(l.isActive)D()}),H={close:async()=>{if(q(),f({forceClear:!0}),E(),r?.(),r=null,h=null,a=null,t&&t.state!=="closed")await t.close();t=null,d?.disconnect?.(),d=null,g=0,S({activeSourceCount:0,isActive:!1,isPlaying:!1})},get activeSourceCount(){return l.activeSourceCount},get error(){return l.error},getSnapshot:()=>l,get isActive(){return l.isActive},get isPlaying(){return l.isPlaying},interrupt:async()=>{let A=Date.now(),T=await R();a=A,s(T);let P=Date.now()-A+U(T);if(S({isActive:!1,isPlaying:e.size>0,lastPlaybackStopLatencyMs:P}),e.size===0){_(P);return}if(!h)h=new Promise((L)=>{r=L});E(),M=setTimeout(()=>{for(let L of e)L.disconnect?.();e.clear(),_(Date.now()-A)},250),f(),await h},get lastInterruptLatencyMs(){return l.lastInterruptLatencyMs},get lastPlaybackStopLatencyMs(){return l.lastPlaybackStopLatencyMs},pause:async()=>{if(!t){S({activeSourceCount:0,isActive:!1,isPlaying:!1});return}await t.suspend(),S({activeSourceCount:e.size,isActive:!1,isPlaying:!1})},get processedChunkCount(){return l.processedChunkCount},get queuedChunkCount(){return l.queuedChunkCount},start:async()=>{try{w();let A=await R();if(N(A),A.state==="suspended")await A.resume();S({activeSourceCount:e.size,isActive:!0,isPlaying:A.state==="running"}),await D()}catch(A){throw S({error:A instanceof Error?A.message:String(A),isActive:!1,isPlaying:!1}),A}},subscribe:(A)=>{return o.add(A),()=>{o.delete(A)}}};return H};var sc=()=>`barge-in:${Date.now()}:${crypto.randomUUID?.()??Math.random().toString(36).slice(2)}`,dc=(n,c)=>{let o=n.filter((t)=>t.status==="stopped"),e=o.map((t)=>t.latencyMs).filter((t)=>typeof t==="number"),i=o.filter((t)=>typeof t.latencyMs==="number"&&t.latencyMs>c).length,l=o.length-i;return{averageLatencyMs:e.length>0?Math.round(e.reduce((t,d)=>t+d,0)/e.length):void 0,events:[...n],failed:i,lastEvent:n.at(-1),passed:l,status:n.length===0?"empty":i>0?"fail":o.length===0?"warn":"pass",thresholdMs:c,total:o.length}},En=(n={})=>{let c=new Set,o=n.thresholdMs??250,e=n.fetch??globalThis.fetch,i=[],l=()=>{for(let g of c)g()},t=(g)=>{if(!n.path||typeof e!=="function")return;e(n.path,{body:JSON.stringify(g),headers:{"Content-Type":"application/json"},method:"POST"}).catch(()=>{})},d=(g,y)=>{let a={at:Date.now(),id:sc(),latencyMs:y.latencyMs,playbackStopLatencyMs:y.playbackStopLatencyMs,reason:y.reason,sessionId:y.sessionId,status:g,thresholdMs:o};return i.push(a),t(a),l(),a};return{getSnapshot:()=>dc(i,o),recordRequested:(g)=>d("requested",g),recordSkipped:(g)=>d("skipped",g),recordStopped:(g)=>d("stopped",g),subscribe:(g)=>{return c.add(g),()=>{c.delete(g)}}}};var rc=0.08,ac=(n,c={})=>(c.enabled??!0)&&n>=(c.interruptThreshold??rc),en=(n,c,o={})=>{let e=n.partial,i=(t)=>{if(!c.isPlaying||o.enabled===!1){o.monitor?.recordSkipped({reason:t,sessionId:n.sessionId});return}o.monitor?.recordRequested({reason:t,sessionId:n.sessionId}),c.interrupt().then(()=>{o.monitor?.recordStopped({latencyMs:c.lastInterruptLatencyMs,playbackStopLatencyMs:c.lastPlaybackStopLatencyMs,reason:t,sessionId:n.sessionId})})},l=n.subscribe(()=>{if(o.interruptOnPartial===!1){e=n.partial;return}if(!e&&n.partial)i("partial-transcript");e=n.partial});return{close:()=>{l()},handleLevel:(t)=>{if(ac(t,o))i("input-level")},sendAudio:(t)=>{i("manual-audio"),n.sendAudio(t)}}};var dn=48,gc=320,Ac=88,hc="Guided test",yc="General recording",Cc="Pick a scenario to begin the demo.",Tc="I can walk you through a short guided voice test.",Ic="I can capture one freeform recording and confirm that it landed.",Vc="Choose a scenario to begin. Guided test asks follow-up prompts. General recording just captures what you say.",Mc="Click Start general recording to capture one freeform answer.",Pn="Speak freely. When you pause, the recording will be captured.",ln="Recording saved. Start again if you want another capture.",Dn="Guided test complete. Review the saved summary below.",On="All prompts are covered. You can stop the microphone or keep speaking for extra detail.",Sc="Ready. Start guided test or general recording to begin.",Lc="Live. Answer the prompt, then click Stop microphone when finished.",bn=["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."],xn=(n,c,o)=>Math.min(o,Math.max(c,n)),u=(n)=>n.replaceAll("&","&").replaceAll("<","<").replaceAll(">",">").replaceAll(\'"\',""").replaceAll("\'","'"),on=(n,c)=>{let o=n[c];if(typeof o==="string"&&o.trim())return o;return null},sn=(n)=>{if(typeof n==="string"&&n.trim())return n;if(n instanceof Error&&n.message.trim())return n.message;if(n&&typeof n==="object"){let c=n,o=on(c,"message")??on(c,"reason")??on(c,"description");if(o)return o;if("error"in c)return sn(c.error);if("cause"in c)return sn(c.cause);try{return JSON.stringify(n)}catch{}}return"Unexpected error"},wc=(n)=>{let c=[n.status];if(n.attempts>0||n.maxAttempts>0)c.push(`${n.attempts}/${n.maxAttempts} attempts`);if(n.nextAttemptAt){let o=Math.max(0,n.nextAttemptAt-Date.now());c.push(`retry in ${Math.ceil(o/100)/10}s`)}return c.join(" \xB7 ")},v=(n=dn)=>Array.from({length:n},()=>0),fn=(n,c,o=dn)=>{let e=n.slice(-(o-1));e.push(xn(c,0,1));while(e.length<o)e.unshift(0);return e},Rc=(n,c=gc,o=Ac)=>{let e=n.length>1?n:v(dn),i=c/(e.length-1),l=o/2,t=o*0.34;if(Math.max(...e,0)<=0.015)return`M 0 ${l} L ${c} ${l}`;let g=e.map((a,h)=>{let r=h*0.76,M=Math.sin(r)*0.78+Math.sin(r*0.41)*0.22,C=a*t,S=i*h,w=xn(l+M*C,8,o-8);return{x:S,y:w}});if(g.length===0)return`M 0 ${l} L ${c} ${l}`;let y=`M ${g[0]?.x??0} ${g[0]?.y??l}`;for(let a=1;a<g.length;a+=1){let h=g[a-1],r=g[a];if(!h||!r)continue;let M=(h.x+r.x)/2;y+=` Q ${M} ${h.y} ${r.x} ${r.y}`}return y},_c=(n)=>{if(!n)return bn;try{let c=JSON.parse(n);if(Array.isArray(c)){let o=c.filter((e)=>typeof e==="string").map((e)=>e.trim()).filter(Boolean);if(o.length>0)return o}}catch{}return bn},tn=(n)=>{if(!n)return;let c=Number(n);return Number.isFinite(c)?c:void 0},Ec=(n,c,o)=>{if(!c)return null;let e=document.querySelector(c);return e instanceof o?e:null},x=(n,c,o,e)=>{let i=c?document.querySelector(c):null;if(i instanceof o)return i;let l=n.querySelector(`#${e}`);if(l instanceof o)return l;throw Error(`Voice HTMX bootstrap could not find the required element "${e}".`)},bc=(n)=>{if(!n.mode)return Cc;if(!n.hasStarted)return n.mode==="guided"?Tc:Ic;if(n.status==="completed")return n.mode==="guided"?Dn:ln;if(n.mode==="general")return Pn;return n.guidedPrompts[n.turnCount]??On},fc=(n)=>{if(!n.mode)return Vc;if(n.status==="completed")return n.mode==="guided"?Dn:ln;if(!n.hasStarted)return n.mode==="guided"?`Click Start guided test to begin. First prompt: ${n.guidedPrompts[0]??"Answer the first prompt."}`:Mc;if(n.mode==="general")return n.turnCount===0?Pn:ln;return n.guidedPrompts[n.turnCount]??On},Pc=(n)=>{let c=n.dataset.voiceGuidedPath,o=n.dataset.voiceGeneralPath;if(!c||!o)throw Error("Voice HTMX bootstrap requires data-voice-guided-path and data-voice-general-path.");let e=_c(n.dataset.voiceGuidedPrompts),i=n.dataset.voiceGuidedLabel??hc,l=n.dataset.voiceGeneralLabel??yc,t=n.dataset.voiceReconnectReportPath,d=n.dataset.voiceBargeInPath,g=d?En({path:d,thresholdMs:tn(n.dataset.voiceBargeInThresholdMs)}):null,y=tn(n.dataset.voiceBargeInRecentWindowMs)??4000,a=tn(n.dataset.voiceBargeInSpeechThreshold)??0.04,h=x(document,n.dataset.voiceSync,HTMLElement,"voice-htmx-sync"),r=x(n,n.dataset.voiceConnection,HTMLElement,"metric-connection"),M=x(n,n.dataset.voiceError,HTMLElement,"status-error"),C=x(n,n.dataset.voiceMicrophone,HTMLElement,"status-mic"),S=x(n,n.dataset.voicePrompt,HTMLElement,"status-prompt"),w=Ec(n,n.dataset.voiceReconnect,HTMLElement),E=x(n,n.dataset.voiceChat,HTMLElement,"chat-list"),_=x(n,n.dataset.voiceStartGuided,HTMLButtonElement,"start-guided"),U=x(n,n.dataset.voiceStartGeneral,HTMLButtonElement,"start-general"),N=x(n,n.dataset.voiceStop,HTMLButtonElement,"stop-mic"),s=x(n,n.dataset.voiceMonitor,HTMLElement,"voice-monitor"),V=x(n,n.dataset.voiceMonitorCopy,HTMLElement,"voice-monitor-copy"),R=x(n,n.dataset.voiceWaveGlow,SVGPathElement,"voice-wave-glow"),Y=x(n,n.dataset.voiceWavePath,SVGPathElement,"voice-wave-path"),f=null,$={general:!1,guided:!1},D=!1,q=null,H=v(),A=null,T=null,P=j(c,{capture:{onAudio:(I,B)=>{if(A){A.sendAudio(I);return}B(I)},onLevel:(I)=>{A?.handleLevel(I),H=fn(H,I),m()}},connection:{reconnectReportPath:t},preset:"guided-intake"}),L=j(o,{capture:{onAudio:(I,B)=>{if(T){T.sendAudio(I);return}B(I)},onLevel:(I)=>{T?.handleLevel(I),H=fn(H,I),m()}},connection:{reconnectReportPath:t},preset:"dictation"}),Q=P.bindHTMX({element:h}),Un=L.bindHTMX({element:h}),k=F(P),z=F(L);A=en(P,k,{interruptThreshold:a,monitor:g??void 0}),T=en(L,z,{interruptThreshold:a,monitor:g??void 0});let J=()=>f==="general"?L:P,Oc=()=>f==="general"?z:k,m=()=>{let I=Rc(H);R.setAttribute("d",I),Y.setAttribute("d",I),V.innerHTML=`<span class="voice-live-dot"></span>${D?"Microphone live":"Microphone idle"}`,V.classList.toggle("is-live",D),s.classList.toggle("is-live",D)},X=()=>{let I=J(),B=(f?$[f]:!1)||I.turns.length>0,an=I.status;if(r.textContent=I.isConnected?"Connected":"Waiting",M.textContent=q||I.error||"None",w)w.textContent=wc(I.reconnect);C.textContent=D?Lc:Sc,S.textContent=fc({guidedPrompts:e,hasStarted:B,mode:f,status:an,turnCount:I.turns.length}),_.hidden=D,U.hidden=D,N.hidden=!D,E.innerHTML=`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${u(f==="general"?l:f==="guided"?i:"Voice demo")}</div>\n <p class="voice-turn-text">${u(bc({generalLabel:l,guidedLabel:i,guidedPrompts:e,hasStarted:B,mode:f,status:an,turnCount:I.turns.length}))}</p>\n</article>${I.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">${u(p.text)}</p>\n </article>\n ${p.assistantText?`<article class="voice-chat-message assistant">\n <div class="voice-chat-role">${u(f==="general"?l:f==="guided"?i:"Guide")}</div>\n <p class="voice-turn-text">${u(p.assistantText)}</p>\n </article>`:""}\n</div>`).join("")}${I.partial?`<article class="voice-chat-message user pending">\n <div class="voice-chat-role">Speaking</div>\n <p class="voice-turn-text">${u(I.partial)}</p>\n</article>`:""}`,m()},Nn=()=>{J().stopRecording(),D=!1,q=null,H=v(),X()},rn=async(I)=>{f=I,$={...$,[I]:!0};try{await J().startRecording(),q=null,D=!0,X()}catch(B){J().stopRecording(),D=!1,H=v(),q=sn(B),X()}};P.subscribe(()=>{if(P.assistantAudio.length>0)k.start().catch(()=>{});X()}),L.subscribe(()=>{if(L.assistantAudio.length>0)z.start().catch(()=>{});X()}),_.addEventListener("click",()=>{rn("guided")}),U.addEventListener("click",()=>{rn("general")}),N.addEventListener("click",()=>{Nn()}),n.addEventListener("absolute-voice-simulate-disconnect",()=>{J().simulateDisconnect()}),window.addEventListener("beforeunload",()=>{P.stopRecording(),L.stopRecording(),A?.close(),T?.close(),k.close(),z.close(),Q(),Un(),P.close(),L.close()}),X()},Dc=()=>{if(typeof window>"u"||typeof document>"u")return;let n=Array.from(document.querySelectorAll("[data-voice-htmx]"));for(let c of n)if(c instanceof HTMLElement)Pc(c)};Dc();export{Dc as initVoiceHTMX};\n';
|
|
5753
|
-
|
|
5754
5752
|
// src/profileSwitchRecommendation.ts
|
|
5755
5753
|
import { Elysia } from "elysia";
|
|
5756
5754
|
|
|
@@ -5957,8 +5955,7 @@ var createVoiceAuditLogger = (store) => ({
|
|
|
5957
5955
|
|
|
5958
5956
|
// src/profileSwitchRecommendation.ts
|
|
5959
5957
|
var readDefaults = (input) => ("defaults" in input) ? input.defaults : input;
|
|
5960
|
-
var
|
|
5961
|
-
var stringifyForHtml = (value) => escapeHtml3(JSON.stringify(value, null, 2) ?? "");
|
|
5958
|
+
var stringifyForHtml = (value) => escapeHtml(JSON.stringify(value, null, 2) ?? "");
|
|
5962
5959
|
var isNumber = (value) => typeof value === "number" && Number.isFinite(value);
|
|
5963
5960
|
var exceeds = (observed, budget) => isNumber(observed) && isNumber(budget) && observed > budget;
|
|
5964
5961
|
var scoreProfile = (profile, observed) => {
|
|
@@ -6234,15 +6231,15 @@ var runVoiceProfileSwitchPolicyProof = async (options) => {
|
|
|
6234
6231
|
var renderVoiceProfileSwitchPolicyProofHTML = (report, options = {}) => {
|
|
6235
6232
|
const title = options.title ?? "Voice Profile Switch Policy Proof";
|
|
6236
6233
|
const rows = report.results.map((result) => `<tr>
|
|
6237
|
-
<td><strong>${
|
|
6238
|
-
<td>${
|
|
6239
|
-
<td>${
|
|
6240
|
-
<td>${
|
|
6241
|
-
<td>${
|
|
6234
|
+
<td><strong>${escapeHtml(result.label ?? result.id)}</strong><p>${escapeHtml(result.description ?? "")}</p></td>
|
|
6235
|
+
<td>${escapeHtml(result.expectedAction ?? "n/a")}</td>
|
|
6236
|
+
<td>${escapeHtml(result.decision.action)}</td>
|
|
6237
|
+
<td>${escapeHtml(result.decision.selectedProfileId ?? "none")}</td>
|
|
6238
|
+
<td>${escapeHtml(result.decision.blockedByPolicy ?? "none")}</td>
|
|
6242
6239
|
<td>${Math.round(result.decision.confidence * 100)}%</td>
|
|
6243
6240
|
<td><span class="status ${result.ok ? "pass" : "fail"}">${result.ok ? "PASS" : "FAIL"}</span></td>
|
|
6244
6241
|
</tr>`).join("");
|
|
6245
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
6242
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>:root{color-scheme:dark;background:#07110e;color:#e8fff5;font-family:ui-sans-serif,system-ui,sans-serif}body{margin:0;padding:32px;background:radial-gradient(circle at top left,rgba(45,212,191,.2),transparent 34%),#07110e}main{max-width:1120px;margin:0 auto}a{color:#67e8f9}.hero,.card{border:1px solid rgba(148,163,184,.24);border-radius:24px;background:rgba(15,23,42,.72);box-shadow:0 24px 90px rgba(0,0,0,.24);padding:24px;margin-bottom:18px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}.metric{border:1px solid rgba(148,163,184,.2);border-radius:18px;padding:16px;background:rgba(8,47,73,.34)}.metric span{display:block;color:#9ca3af;font-size:.78rem;text-transform:uppercase;letter-spacing:.08em}.metric strong{display:block;margin-top:8px;font-size:1.6rem}table{width:100%;border-collapse:collapse;overflow:hidden;border-radius:18px}th,td{padding:14px;text-align:left;border-bottom:1px solid rgba(148,163,184,.18);vertical-align:top}th{color:#a7f3d0;text-transform:uppercase;font-size:.75rem;letter-spacing:.08em}td p{margin:.4rem 0 0;color:#a7b5ae}pre{white-space:pre-wrap;overflow:auto;border-radius:18px;background:#020617;padding:18px;color:#d1fae5}.status{display:inline-flex;border-radius:999px;padding:5px 10px;font-weight:800;font-size:.75rem}.pass{background:rgba(34,197,94,.18);color:#bbf7d0}.fail{background:rgba(239,68,68,.18);color:#fecaca}</style></head><body><main><section class="hero"><p><a href="#raw-report">Raw report</a></p><h1>${escapeHtml(title)}</h1><p>This page proves profile switching is production-bounded: teams can disable it, run recommend-only, auto-apply with confidence, restrict allowed targets, block unsafe targets, and cap automatic switches per session.</p><div class="metric-grid"><div class="metric"><span>Status</span><strong>${report.ok ? "PASS" : "FAIL"}</strong></div><div class="metric"><span>Cases</span><strong>${report.summary.passed}/${report.summary.total}</strong></div><div class="metric"><span>Generated</span><strong>${escapeHtml(report.generatedAt)}</strong></div></div></section><section class="card"><h2>Policy Cases</h2><table><thead><tr><th>Case</th><th>Expected</th><th>Actual</th><th>Selected</th><th>Blocked by</th><th>Confidence</th><th>Status</th></tr></thead><tbody>${rows}</tbody></table></section><section class="card" id="raw-report"><h2>Raw Report</h2><pre>${stringifyForHtml(report)}</pre></section></main></body></html>`;
|
|
6246
6243
|
};
|
|
6247
6244
|
var createVoiceProfileSwitchPolicyProofRoutes = (options) => {
|
|
6248
6245
|
const path = options.path ?? "/api/voice/profile-switch-policy-proof";
|
|
@@ -6387,15 +6384,15 @@ var buildVoiceProfileSwitchLiveDecisionReport = async (options) => {
|
|
|
6387
6384
|
var renderVoiceProfileSwitchLiveDecisionHTML = (report, options = {}) => {
|
|
6388
6385
|
const title = options.title ?? "Voice Profile Switch Live Decisions";
|
|
6389
6386
|
const rows = report.sessions.flatMap((session) => session.decisions.map((decision) => `<tr>
|
|
6390
|
-
<td><strong>${
|
|
6391
|
-
<td>${
|
|
6392
|
-
<td>${
|
|
6393
|
-
<td>${
|
|
6394
|
-
<td>${
|
|
6387
|
+
<td><strong>${escapeHtml(decision.sessionId)}</strong><p>${escapeHtml(new Date(decision.at).toISOString())}</p></td>
|
|
6388
|
+
<td>${escapeHtml(decision.source)}</td>
|
|
6389
|
+
<td>${escapeHtml(decision.action ?? "unknown")}</td>
|
|
6390
|
+
<td>${escapeHtml(decision.selectedProfileId ?? "none")}</td>
|
|
6391
|
+
<td>${escapeHtml(decision.blockedByPolicy ?? "none")}</td>
|
|
6395
6392
|
<td>${typeof decision.confidence === "number" ? `${Math.round(decision.confidence * 100)}%` : "n/a"}</td>
|
|
6396
|
-
<td>${
|
|
6393
|
+
<td>${escapeHtml(decision.reason ?? decision.outcome ?? "recorded")}</td>
|
|
6397
6394
|
</tr>`)).join("");
|
|
6398
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
6395
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>:root{color-scheme:dark;background:#11100b;color:#fff7ed;font-family:ui-sans-serif,system-ui,sans-serif}body{margin:0;padding:32px;background:radial-gradient(circle at top right,rgba(251,146,60,.18),transparent 34%),#11100b}main{max-width:1180px;margin:0 auto}.hero,.card{border:1px solid rgba(251,191,36,.24);border-radius:24px;background:rgba(28,25,23,.78);box-shadow:0 24px 90px rgba(0,0,0,.24);padding:24px;margin-bottom:18px}.metric-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px}.metric{border:1px solid rgba(251,191,36,.22);border-radius:18px;padding:16px;background:rgba(120,53,15,.26)}.metric span{display:block;color:#fed7aa;font-size:.75rem;text-transform:uppercase;letter-spacing:.08em}.metric strong{display:block;margin-top:8px;font-size:1.7rem}table{width:100%;border-collapse:collapse}th,td{padding:13px;text-align:left;border-bottom:1px solid rgba(251,191,36,.18);vertical-align:top}th{color:#fde68a;text-transform:uppercase;font-size:.74rem;letter-spacing:.08em}td p{margin:.35rem 0 0;color:#fdba74}pre{white-space:pre-wrap;overflow:auto;border-radius:18px;background:#020617;padding:18px;color:#ffedd5}.empty{color:#fdba74}</style></head><body><main><section class="hero"><h1>${escapeHtml(title)}</h1><p>This page summarizes real profile switch guard evidence from audit and trace stores. Use it beside policy proof to show bounded policy plus actual session decisions.</p><div class="metric-grid"><div class="metric"><span>Sessions</span><strong>${String(report.summary.sessions)}</strong></div><div class="metric"><span>Decisions</span><strong>${String(report.summary.decisions)}</strong></div><div class="metric"><span>Switches</span><strong>${String(report.summary.switches)}</strong></div><div class="metric"><span>Blocked</span><strong>${String(report.summary.blocked)}</strong></div><div class="metric"><span>Auto applied</span><strong>${String(report.summary.autoApplied)}</strong></div></div></section><section class="card"><h2>Live Guard Decisions</h2>${rows ? `<table><thead><tr><th>Session</th><th>Source</th><th>Action</th><th>Selected</th><th>Blocked by</th><th>Confidence</th><th>Reason</th></tr></thead><tbody>${rows}</tbody></table>` : '<p class="empty">No profile switch guard decisions recorded yet. Start a voice session with profileSwitchGuard enabled.</p>'}</section><section class="card"><h2>Raw Report</h2><pre>${stringifyForHtml(report)}</pre></section></main></body></html>`;
|
|
6399
6396
|
};
|
|
6400
6397
|
var createVoiceProfileSwitchLiveDecisionRoutes = (options) => {
|
|
6401
6398
|
const path = options.path ?? "/api/voice/profile-switch-live-decisions";
|
|
@@ -6517,9 +6514,9 @@ var buildVoiceProfileSwitchReadinessReport = async (options) => {
|
|
|
6517
6514
|
};
|
|
6518
6515
|
var renderVoiceProfileSwitchReadinessHTML = (report, options = {}) => {
|
|
6519
6516
|
const title = options.title ?? "Voice Profile Switch Readiness";
|
|
6520
|
-
const issues = report.issues.map((issue) => `<li class="${
|
|
6521
|
-
const sessions = report.live.sessions.map((session) => `<tr><td>${
|
|
6522
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
6517
|
+
const issues = report.issues.map((issue) => `<li class="${escapeHtml(issue.status)}"><strong>${escapeHtml(issue.code)}</strong><p>${escapeHtml(issue.message)}</p></li>`).join("");
|
|
6518
|
+
const sessions = report.live.sessions.map((session) => `<tr><td>${escapeHtml(session.sessionId)}</td><td>${escapeHtml(session.actions.join(", ") || "none")}</td><td>${String(session.decisions.length)}</td><td>${String(session.autoApplied)}</td><td>${escapeHtml(session.blockedByPolicy.join(", ") || "none")}</td></tr>`).join("");
|
|
6519
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>:root{color-scheme:dark;background:#0b1020;color:#eff6ff;font-family:ui-sans-serif,system-ui,sans-serif}body{margin:0;padding:32px;background:radial-gradient(circle at top left,rgba(59,130,246,.2),transparent 34%),#0b1020}main{max-width:1140px;margin:0 auto}.hero,.card{background:rgba(15,23,42,.82);border:1px solid rgba(147,197,253,.24);border-radius:24px;margin-bottom:18px;padding:24px}.status{border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{background:rgba(34,197,94,.18);color:#bbf7d0}.warn{background:rgba(245,158,11,.18);color:#fde68a}.fail{background:rgba(239,68,68,.18);color:#fecaca}.grid{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin-top:16px}.metric{background:#0f172a;border:1px solid rgba(147,197,253,.2);border-radius:18px;padding:16px}.metric span{color:#93c5fd;display:block;font-size:.75rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.metric strong{display:block;font-size:1.8rem;margin-top:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{border-radius:16px;padding:14px}li p{margin:.35rem 0 0}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid rgba(147,197,253,.18);padding:12px;text-align:left}pre{background:#020617;border-radius:18px;color:#dbeafe;overflow:auto;padding:18px}</style></head><body><main><section class="hero"><h1>${escapeHtml(title)}</h1><p>Deploy-facing readiness for profile switching: policy proof, audit evidence, trace evidence, and live session behavior.</p><p class="status ${escapeHtml(report.status)}">${escapeHtml(report.status.toUpperCase())}</p><div class="grid"><div class="metric"><span>Sessions</span><strong>${String(report.summary.sessions)}</strong></div><div class="metric"><span>Decisions</span><strong>${String(report.summary.decisions)}</strong></div><div class="metric"><span>Policy cases</span><strong>${String(report.summary.policyCases ?? 0)}</strong></div><div class="metric"><span>Audit</span><strong>${String(report.summary.auditEvents)}</strong></div><div class="metric"><span>Trace</span><strong>${String(report.summary.traceEvents)}</strong></div></div></section><section class="card"><h2>Issues</h2>${issues ? `<ul>${issues}</ul>` : '<p class="pass status">No readiness issues.</p>'}</section><section class="card"><h2>Live Sessions</h2>${sessions ? `<table><thead><tr><th>Session</th><th>Actions</th><th>Decisions</th><th>Auto applied</th><th>Blocked by</th></tr></thead><tbody>${sessions}</tbody></table>` : "<p>No live guard sessions found.</p>"}</section><section class="card"><h2>Raw Report</h2><pre>${stringifyForHtml(report)}</pre></section></main></body></html>`;
|
|
6523
6520
|
};
|
|
6524
6521
|
var createVoiceProfileSwitchReadinessRoutes = (options) => {
|
|
6525
6522
|
const path = options.path ?? "/api/voice/profile-switch-readiness";
|
|
@@ -6599,10 +6596,7 @@ var buildDefaultCitationMessage = (citations, args, maxChunkChars) => {
|
|
|
6599
6596
|
const text = truncate(citation.chunkText, maxChunkChars);
|
|
6600
6597
|
return `${String(index + 1)}. ${label} (score ${formatScore(citation.score)}): ${text}`;
|
|
6601
6598
|
});
|
|
6602
|
-
return [
|
|
6603
|
-
`Knowledge base results for "${args.query}":`,
|
|
6604
|
-
...lines
|
|
6605
|
-
].join(`
|
|
6599
|
+
return [`Knowledge base results for "${args.query}":`, ...lines].join(`
|
|
6606
6600
|
`);
|
|
6607
6601
|
};
|
|
6608
6602
|
var filterAllowedFilterKeys = (filter, allowedKeys) => {
|
|
@@ -8263,16 +8257,15 @@ var summarizeVoiceProviderHealth = async (input) => {
|
|
|
8263
8257
|
}
|
|
8264
8258
|
return summaries;
|
|
8265
8259
|
};
|
|
8266
|
-
var escapeHtml4 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
8267
8260
|
var renderVoiceProviderHealthHTML = (providers) => providers.length === 0 ? '<p class="voice-provider-empty">No provider status yet.</p>' : [
|
|
8268
8261
|
'<div class="voice-provider-health">',
|
|
8269
8262
|
...providers.map((provider) => {
|
|
8270
8263
|
const suppressionSeconds = typeof provider.suppressionRemainingMs === "number" ? Math.ceil(provider.suppressionRemainingMs / 1000) : undefined;
|
|
8271
8264
|
return [
|
|
8272
|
-
`<article class="voice-provider-card ${
|
|
8265
|
+
`<article class="voice-provider-card ${escapeHtml(provider.status)}">`,
|
|
8273
8266
|
'<div class="voice-provider-card-header">',
|
|
8274
|
-
`<strong>${
|
|
8275
|
-
`<span>${
|
|
8267
|
+
`<strong>${escapeHtml(provider.provider)}</strong>`,
|
|
8268
|
+
`<span>${escapeHtml(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>`,
|
|
8276
8269
|
"</div>",
|
|
8277
8270
|
"<dl>",
|
|
8278
8271
|
`<div><dt>Runs</dt><dd>${String(provider.runCount)}</dd></div>`,
|
|
@@ -8282,7 +8275,7 @@ var renderVoiceProviderHealthHTML = (providers) => providers.length === 0 ? '<p
|
|
|
8282
8275
|
`<div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div>`,
|
|
8283
8276
|
"</dl>",
|
|
8284
8277
|
suppressionSeconds ? `<p>Temporarily suppressed for ${String(suppressionSeconds)}s.</p>` : "",
|
|
8285
|
-
provider.lastError ? `<p>${
|
|
8278
|
+
provider.lastError ? `<p>${escapeHtml(provider.lastError)}</p>` : "",
|
|
8286
8279
|
"</article>"
|
|
8287
8280
|
].join("");
|
|
8288
8281
|
}),
|
|
@@ -8313,7 +8306,6 @@ var createVoiceProviderHealthRoutes = (options) => {
|
|
|
8313
8306
|
};
|
|
8314
8307
|
|
|
8315
8308
|
// src/assistantHealth.ts
|
|
8316
|
-
var escapeHtml5 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
8317
8309
|
var renderCountMap = (values) => {
|
|
8318
8310
|
const entries = Object.entries(values).sort((left, right) => right[1] - left[1]);
|
|
8319
8311
|
if (entries.length === 0) {
|
|
@@ -8321,7 +8313,7 @@ var renderCountMap = (values) => {
|
|
|
8321
8313
|
}
|
|
8322
8314
|
return [
|
|
8323
8315
|
'<div class="voice-assistant-health-metrics">',
|
|
8324
|
-
...entries.map(([label, value]) => `<div><span>${
|
|
8316
|
+
...entries.map(([label, value]) => `<div><span>${escapeHtml(label)}</span><strong>${String(value)}</strong></div>`),
|
|
8325
8317
|
"</div>"
|
|
8326
8318
|
].join("");
|
|
8327
8319
|
};
|
|
@@ -8382,11 +8374,11 @@ var renderVoiceAssistantHealthHTML = (summary) => {
|
|
|
8382
8374
|
'<div class="voice-assistant-health-failures">',
|
|
8383
8375
|
...failures.map((failure) => [
|
|
8384
8376
|
"<article>",
|
|
8385
|
-
`<strong>${
|
|
8386
|
-
`<span>${
|
|
8387
|
-
failure.error ? `<p>${
|
|
8388
|
-
`<small>${
|
|
8389
|
-
failure.replayHref ? `<p><a href="${
|
|
8377
|
+
`<strong>${escapeHtml(failure.provider ?? failure.assistantId ?? failure.type)}</strong>`,
|
|
8378
|
+
`<span>${escapeHtml(failure.status ?? (failure.rateLimited ? "rate-limited" : "error"))}</span>`,
|
|
8379
|
+
failure.error ? `<p>${escapeHtml(failure.error)}</p>` : "",
|
|
8380
|
+
`<small>${escapeHtml(failure.sessionId)}${failure.turnId ? ` / ${escapeHtml(failure.turnId)}` : ""}</small>`,
|
|
8381
|
+
failure.replayHref ? `<p><a href="${escapeHtml(failure.replayHref)}">Open replay</a></p>` : "",
|
|
8390
8382
|
"</article>"
|
|
8391
8383
|
].join("")),
|
|
8392
8384
|
"</div>"
|
|
@@ -8896,7 +8888,6 @@ var exportVoiceTrace = async (input) => {
|
|
|
8896
8888
|
};
|
|
8897
8889
|
};
|
|
8898
8890
|
var toNumber = (value) => typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
8899
|
-
var escapeHtml6 = (value) => value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
8900
8891
|
var formatTraceValue = (value) => {
|
|
8901
8892
|
if (value === undefined || value === null) {
|
|
8902
8893
|
return "";
|
|
@@ -9180,10 +9171,10 @@ var renderVoiceTraceHTML = (events, options = {}) => {
|
|
|
9180
9171
|
const offset = summary.startedAt === undefined ? event.at : Math.max(0, event.at - summary.startedAt);
|
|
9181
9172
|
return [
|
|
9182
9173
|
"<tr>",
|
|
9183
|
-
`<td>${
|
|
9184
|
-
`<td>${
|
|
9185
|
-
`<td>${
|
|
9186
|
-
`<td><code>${
|
|
9174
|
+
`<td>${escapeHtml(String(offset))}</td>`,
|
|
9175
|
+
`<td>${escapeHtml(event.type)}</td>`,
|
|
9176
|
+
`<td>${escapeHtml(event.turnId ?? "")}</td>`,
|
|
9177
|
+
`<td><code>${escapeHtml(JSON.stringify(event.payload))}</code></td>`,
|
|
9187
9178
|
"</tr>"
|
|
9188
9179
|
].join("");
|
|
9189
9180
|
}).join(`
|
|
@@ -9194,7 +9185,7 @@ var renderVoiceTraceHTML = (events, options = {}) => {
|
|
|
9194
9185
|
"<head>",
|
|
9195
9186
|
'<meta charset="utf-8" />',
|
|
9196
9187
|
'<meta name="viewport" content="width=device-width, initial-scale=1" />',
|
|
9197
|
-
`<title>${
|
|
9188
|
+
`<title>${escapeHtml(options.title ?? "Voice Trace")}</title>`,
|
|
9198
9189
|
"<style>",
|
|
9199
9190
|
"body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;line-height:1.45;background:#f8f7f2;color:#181713}",
|
|
9200
9191
|
"main{max-width:1100px;margin:auto}",
|
|
@@ -9208,7 +9199,7 @@ var renderVoiceTraceHTML = (events, options = {}) => {
|
|
|
9208
9199
|
"</style>",
|
|
9209
9200
|
"</head>",
|
|
9210
9201
|
"<body><main>",
|
|
9211
|
-
`<h1>${
|
|
9202
|
+
`<h1>${escapeHtml(options.title ?? `Voice Trace ${summary.sessionId ?? ""}`.trim())}</h1>`,
|
|
9212
9203
|
`<p class="${evaluation.pass ? "pass" : "fail"}">QA: ${evaluation.pass ? "pass" : "fail"}</p>`,
|
|
9213
9204
|
'<section class="summary">',
|
|
9214
9205
|
`<div class="card"><strong>Events</strong><br>${summary.eventCount}</div>`,
|
|
@@ -9222,7 +9213,7 @@ var renderVoiceTraceHTML = (events, options = {}) => {
|
|
|
9222
9213
|
eventRows,
|
|
9223
9214
|
"</tbody></table>",
|
|
9224
9215
|
"<h2>Markdown Export</h2>",
|
|
9225
|
-
`<pre>${
|
|
9216
|
+
`<pre>${escapeHtml(markdown)}</pre>`,
|
|
9226
9217
|
"</main></body></html>"
|
|
9227
9218
|
].join(`
|
|
9228
9219
|
`);
|
|
@@ -9236,7 +9227,6 @@ var buildVoiceTraceReplay = (events, options = {}) => ({
|
|
|
9236
9227
|
|
|
9237
9228
|
// src/auditRoutes.ts
|
|
9238
9229
|
import { Elysia as Elysia4 } from "elysia";
|
|
9239
|
-
var escapeHtml7 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
9240
9230
|
var getString3 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
9241
9231
|
var getNumber2 = (value) => {
|
|
9242
9232
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -9334,14 +9324,14 @@ var buildVoiceAuditTrailReport = async (options) => {
|
|
|
9334
9324
|
};
|
|
9335
9325
|
var renderVoiceAuditTrailHTML = (report, options = {}) => {
|
|
9336
9326
|
const title = options.title ?? "AbsoluteJS Voice Audit Trail";
|
|
9337
|
-
const chips = report.summary.byType.map(([type, count]) => `<span>${
|
|
9327
|
+
const chips = report.summary.byType.map(([type, count]) => `<span>${escapeHtml(type)} <strong>${count}</strong></span>`).join("");
|
|
9338
9328
|
const rows = report.events.map((event) => {
|
|
9339
9329
|
const actor = event.actor ? `${event.actor.kind}:${event.actor.id}` : "unknown";
|
|
9340
9330
|
const resource = event.resource ? `${event.resource.type}${event.resource.id ? `:${event.resource.id}` : ""}` : "";
|
|
9341
9331
|
const payload = event.payload ? JSON.stringify(event.payload, null, 2) : "";
|
|
9342
|
-
return `<article class="event ${
|
|
9332
|
+
return `<article class="event ${escapeHtml(event.outcome ?? "unknown")}"><div><span>${escapeHtml(event.type)}</span><h2>${escapeHtml(event.action)}</h2><p>${escapeHtml(new Date(event.at).toLocaleString())}</p><p>Actor: ${escapeHtml(actor)}${resource ? ` \xB7 Resource: ${escapeHtml(resource)}` : ""}</p>${event.sessionId ? `<p>Session: ${escapeHtml(event.sessionId)}</p>` : ""}</div><strong>${escapeHtml(event.outcome ?? "recorded")}</strong>${payload ? `<pre>${escapeHtml(payload)}</pre>` : ""}</article>`;
|
|
9343
9333
|
}).join("");
|
|
9344
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
9334
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#11140f;color:#f7f1df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(245,158,11,.12));border:1px solid #2c3327;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#facc15;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}.chips{display:flex;flex-wrap:wrap;gap:10px}.chips span{border:1px solid #46513b;border-radius:999px;padding:8px 12px}.events{display:grid;gap:14px}.event{background:#181d15;border:1px solid #2c3327;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto;padding:18px}.event.error{border-color:rgba(239,68,68,.75)}.event.skipped{border-color:rgba(245,158,11,.7)}.event.success{border-color:rgba(34,197,94,.55)}.event span{color:#facc15;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.event h2{margin:.2rem 0}.event p{color:#c8ccb8;margin:.2rem 0}.event strong{text-transform:uppercase}pre{background:#0c0f0a;border-radius:14px;grid-column:1/-1;overflow:auto;padding:14px;white-space:pre-wrap}@media(max-width:760px){main{padding:20px}.event{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted evidence</p><h1>${escapeHtml(title)}</h1><p>${report.summary.total} event(s), ${report.summary.errors} error(s). Latest ${report.summary.latestAt ? escapeHtml(new Date(report.summary.latestAt).toLocaleString()) : "never"}.</p><div class="chips">${chips}</div></section><section class="events">${rows || "<p>No audit events match this filter.</p>"}</section></main></body></html>`;
|
|
9345
9335
|
};
|
|
9346
9336
|
var createVoiceAuditTrailRoutes = (options) => {
|
|
9347
9337
|
const path = options.path ?? "/api/voice-audit";
|
|
@@ -9433,7 +9423,6 @@ var createVoiceAuditTrailRoutes = (options) => {
|
|
|
9433
9423
|
};
|
|
9434
9424
|
|
|
9435
9425
|
// src/auditExport.ts
|
|
9436
|
-
var escapeHtml8 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
9437
9426
|
var normalizeRedactionKey2 = (key) => key.trim().toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
9438
9427
|
var resolveReplacement2 = (input) => typeof input.options.replacement === "function" ? input.options.replacement({
|
|
9439
9428
|
key: input.key,
|
|
@@ -9551,8 +9540,8 @@ var renderVoiceAuditHTML = (events, options = {}) => {
|
|
|
9551
9540
|
const markdown = renderVoiceAuditMarkdown(events, options);
|
|
9552
9541
|
const renderEvents = options.redact ? redactVoiceAuditEvents(events, options.redact) : events;
|
|
9553
9542
|
const summary = summarizeVoiceAuditTrail(renderEvents);
|
|
9554
|
-
const rows = renderEvents.map((event) => `<tr><td>${
|
|
9555
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
9543
|
+
const rows = renderEvents.map((event) => `<tr><td>${escapeHtml(new Date(event.at).toISOString())}</td><td>${escapeHtml(event.type)}</td><td>${escapeHtml(event.action)}</td><td>${escapeHtml(event.outcome ?? "")}</td><td><code>${escapeHtml(JSON.stringify(event.payload ?? {}))}</code></td></tr>`).join("");
|
|
9544
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;line-height:1.45;background:#f8f7f2;color:#181713}main{max-width:1100px;margin:auto}.summary{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:.75rem;margin:1rem 0}.card{background:white;border:1px solid #ded9cc;border-radius:12px;padding:1rem}table{border-collapse:collapse;width:100%;background:white;border:1px solid #ded9cc}th,td{border-bottom:1px solid #eee8dc;padding:.65rem;text-align:left;vertical-align:top}code{white-space:pre-wrap;word-break:break-word}pre{background:#181713;color:#f8f7f2;padding:1rem;border-radius:12px;overflow:auto}</style></head><body><main><h1>${escapeHtml(title)}</h1><section class="summary"><div class="card"><strong>Events</strong><br>${summary.total}</div><div class="card"><strong>Errors</strong><br>${summary.errors}</div><div class="card"><strong>Latest</strong><br>${summary.latestAt ? escapeHtml(new Date(summary.latestAt).toLocaleString()) : "never"}</div></section><table><thead><tr><th>At</th><th>Type</th><th>Action</th><th>Outcome</th><th>Payload</th></tr></thead><tbody>${rows}</tbody></table><h2>Markdown Export</h2><pre>${escapeHtml(markdown)}</pre></main></body></html>`;
|
|
9556
9545
|
};
|
|
9557
9546
|
var buildVoiceAuditExport = (events, options = {}) => {
|
|
9558
9547
|
const exportEvents = options.redact ? redactVoiceAuditEvents(events, options.redact) : events;
|
|
@@ -10002,7 +9991,6 @@ var createVoiceAuditSinkDeliveryWorkerLoop = (options) => {
|
|
|
10002
9991
|
};
|
|
10003
9992
|
|
|
10004
9993
|
// src/auditDeliveryRoutes.ts
|
|
10005
|
-
var escapeHtml9 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10006
9994
|
var getString4 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
10007
9995
|
var getNumber3 = (value) => {
|
|
10008
9996
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -10083,14 +10071,14 @@ var renderSinkResults = (delivery) => {
|
|
|
10083
10071
|
if (entries.length === 0) {
|
|
10084
10072
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
10085
10073
|
}
|
|
10086
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
10074
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml(sinkId)}</strong>: ${escapeHtml(result.status)}${result.deliveredTo ? ` to ${escapeHtml(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
10087
10075
|
};
|
|
10088
|
-
var renderEventList = (delivery) => delivery.events.length === 0 ? "<p>No audit events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
10076
|
+
var renderEventList = (delivery) => delivery.events.length === 0 ? "<p>No audit events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml(event.type)} ${escapeHtml(event.action)} <small>${escapeHtml(event.id)}</small></li>`).join("")}</ul>`;
|
|
10089
10077
|
var renderVoiceAuditDeliveryHTML = (report, options = {}) => {
|
|
10090
10078
|
const title = options.title ?? "AbsoluteJS Voice Audit Deliveries";
|
|
10091
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
10092
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
10093
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
10079
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml(options.workerPath ?? "/api/voice-audit-deliveries/drain")}"><button type="submit">Drain audit deliveries</button></form>`;
|
|
10080
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml(delivery.deliveryStatus)}</span><h2>${escapeHtml(delivery.id)}</h2><p>${escapeHtml(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults(delivery)}<h3>Events</h3>${renderEventList(delivery)}</article>`).join("");
|
|
10081
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101418;color:#f4efe1;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.17),rgba(245,158,11,.13));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#38bdf8;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.grid{display:grid;gap:12px;grid-template-columns:repeat(4,1fr);margin-bottom:16px}.grid article,.delivery{background:#151b22;border:1px solid #26313d;border-radius:22px;padding:18px}.grid span,.delivery span{color:#93c5fd;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.grid strong{display:block;font-size:2rem}.deliveries{display:grid;gap:14px}.delivery.failed{border-color:rgba(239,68,68,.75)}.delivery.pending{border-color:rgba(245,158,11,.7)}.delivery.delivered{border-color:rgba(34,197,94,.55)}.delivery.skipped{border-color:rgba(148,163,184,.6)}.head{align-items:start;display:flex;gap:14px;justify-content:space-between}.delivery h2{font-size:1.05rem;margin:.3rem 0;overflow-wrap:anywhere}.delivery h3{margin:1rem 0 .3rem}.delivery p,.delivery li{color:#c8d0d8}.error{color:#fecaca!important}button{background:#38bdf8;border:0;border-radius:999px;color:#07111f;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}@media(max-width:760px){main{padding:20px}.grid{grid-template-columns:1fr 1fr}.head{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Compliance export health</p><h1>${escapeHtml(title)}</h1><p>Checked ${escapeHtml(new Date(report.checkedAt).toLocaleString())}. Showing ${String(report.deliveries.length)} delivery item(s).</p>${drainAction}</section>${renderMetricGrid(report)}<section class="deliveries">${rows || "<p>No audit deliveries match this filter.</p>"}</section></main></body></html>`;
|
|
10094
10082
|
};
|
|
10095
10083
|
var createVoiceAuditDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceAuditDeliveryReport(options, resolveVoiceAuditDeliveryFilter(query, options.filter));
|
|
10096
10084
|
var createVoiceAuditDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -10130,7 +10118,6 @@ var createVoiceAuditDeliveryRoutes = (options) => {
|
|
|
10130
10118
|
|
|
10131
10119
|
// src/bargeInRoutes.ts
|
|
10132
10120
|
import { Elysia as Elysia6 } from "elysia";
|
|
10133
|
-
var escapeHtml10 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10134
10121
|
var isBargeInPayload = (value) => !!value && typeof value === "object" && typeof value.at === "number" && typeof value.id === "string" && typeof value.reason === "string" && typeof value.status === "string";
|
|
10135
10122
|
var toBargeInEvent = (event) => event.type === "client.barge_in" && isBargeInPayload(event.payload) ? event.payload : undefined;
|
|
10136
10123
|
var summarizeVoiceBargeIn = (events, options = {}) => {
|
|
@@ -10186,8 +10173,8 @@ const bargeInMonitor = createVoiceBargeInMonitor({
|
|
|
10186
10173
|
path: '/api/voice-barge-in',
|
|
10187
10174
|
sessionId
|
|
10188
10175
|
});`;
|
|
10189
|
-
const sessions = report.sessions.length ? report.sessions.map((session) => `<tr><td>${
|
|
10190
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
10176
|
+
const sessions = report.sessions.length ? report.sessions.map((session) => `<tr><td>${escapeHtml(session.sessionId)}</td><td>${String(session.total)}</td><td>${String(session.passed)}</td><td>${String(session.failed)}</td><td>${String(session.averageLatencyMs ?? 0)}ms</td></tr>`).join("") : '<tr><td colspan="5">No barge-in events yet.</td></tr>';
|
|
10177
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101316;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.pass{color:#86efac}.warn{color:#fbbf24}.fail{color:#fca5a5}.empty{color:#cbd5e1}.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:20px 0}.metrics article,.primitive{background:#181f27;border:1px solid #2b3642;border-radius:20px;padding:16px}.metrics span{color:#a8b0b8}.metrics strong{display:block;font-size:2rem}.primitive{margin:0 0 20px}.primitive h2{margin:.2rem 0 .5rem}.primitive p{color:#cbd5e1}.primitive pre{background:#0a0d10;border:1px solid #2b3642;border-radius:16px;color:#d9fff7;overflow:auto;padding:16px}table{background:#181f27;border-collapse:collapse;border-radius:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #2b3642;padding:12px;text-align:left}</style></head><body><main><p class="eyebrow">Interruption quality</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">Status: ${escapeHtml(report.status)}</p><section class="metrics"><article><span>Interruptions</span><strong>${String(report.total)}</strong></article><article><span>Avg latency</span><strong>${String(report.averageLatencyMs ?? 0)}ms</strong></article><article><span>Passed</span><strong>${String(report.passed)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceBargeInRoutes(...)</code> proves interruption quality</h2><p>Use the shared trace store for browser interrupts, readiness gates, trace timelines, and production evidence instead of trusting a black-box hosted dashboard.</p><pre><code>${escapeHtml(snippet)}</code></pre></section><table><thead><tr><th>Session</th><th>Total</th><th>Passed</th><th>Failed</th><th>Avg latency</th></tr></thead><tbody>${sessions}</tbody></table></main></body></html>`;
|
|
10191
10178
|
};
|
|
10192
10179
|
var createVoiceBargeInRoutes = (options) => {
|
|
10193
10180
|
const path = options.path ?? "/api/voice-barge-in";
|
|
@@ -10228,7 +10215,6 @@ var createVoiceBargeInRoutes = (options) => {
|
|
|
10228
10215
|
// src/browserCallProfiles.ts
|
|
10229
10216
|
import { Elysia as Elysia7 } from "elysia";
|
|
10230
10217
|
var DEFAULT_MAX_AGE_MS = 10 * 60 * 1000;
|
|
10231
|
-
var escapeHtml11 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10232
10218
|
var toTime = (value) => {
|
|
10233
10219
|
if (value === undefined) {
|
|
10234
10220
|
return;
|
|
@@ -10368,8 +10354,8 @@ var renderVoiceBrowserCallProfileMarkdown = (report, options = {}) => {
|
|
|
10368
10354
|
var renderVoiceBrowserCallProfileHTML = (report, options = {}) => {
|
|
10369
10355
|
const normalized = buildVoiceBrowserCallProfileReport(report);
|
|
10370
10356
|
const title = options.title ?? "Voice Browser Call Profiles";
|
|
10371
|
-
const rows = normalized.results.map((result) => `<tr><td>${
|
|
10372
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
10357
|
+
const rows = normalized.results.map((result) => `<tr><td>${escapeHtml(result.framework)}</td><td class="${result.ok ? "pass" : "fail"}">${result.ok ? "pass" : "fail"}</td><td>${String(result.summary?.openSockets ?? 0)}</td><td>${String(result.summary?.sentBytes ?? 0)}</td><td>${String(result.summary?.receivedBytes ?? 0)}</td><td>${String(result.summary?.messageCount ?? 0)}</td><td>${escapeHtml(result.error ?? "")}</td></tr>`).join("");
|
|
10358
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#11140f;color:#f4f0df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,.primitive,table{background:#191d15;border:1px solid #323a27;border-radius:22px;margin-bottom:16px}.hero,.primitive{padding:22px}.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}.pass{color:#bef264}.warn,.empty,.stale{color:#fde68a}.fail{color:#fecaca}.metrics{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin-top:18px}.metric{background:#10130d;border:1px solid #303827;border-radius:16px;padding:14px}.metric span{color:#b8c3a3}.metric strong{display:block;font-size:1.7rem;margin-top:4px}.primitive code{color:#d9f99d}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #323a27;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Real browser microphone proof</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(normalized.status)}">Status: ${escapeHtml(normalized.status)}</p><p>Framework parity proof from real browser pages opening the voice WebSocket and sending microphone audio bytes.</p><section class="metrics"><div class="metric"><span>Frameworks</span><strong>${String(normalized.summary.totalFrameworks)}</strong></div><div class="metric"><span>Passing</span><strong>${String(normalized.summary.passedFrameworks.length)}</strong></div><div class="metric"><span>Open sockets</span><strong>${String(normalized.summary.openSockets)}</strong></div><div class="metric"><span>Sent bytes</span><strong>${String(normalized.summary.sentBytes)}</strong></div><div class="metric"><span>Received bytes</span><strong>${String(normalized.summary.receivedBytes)}</strong></div><div class="metric"><span>Messages</span><strong>${String(normalized.summary.totalMessages)}</strong></div></section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><p><code>buildVoiceBrowserCallProfileReport(...)</code> normalizes browser-call evidence. <code>evaluateVoiceBrowserCallProfileEvidence(...)</code> gates required frameworks, WebSocket opens, sent bytes, and freshness. <code>createVoiceBrowserCallProfileRoutes(...)</code> serves JSON, HTML, and Markdown.</p></section><table><thead><tr><th>Framework</th><th>Status</th><th>WebSockets</th><th>Sent bytes</th><th>Received bytes</th><th>Messages</th><th>Error</th></tr></thead><tbody>${rows || '<tr><td colspan="7">No browser call profile evidence yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
10373
10359
|
};
|
|
10374
10360
|
var resolveSource = async (options) => {
|
|
10375
10361
|
const source = typeof options.source === "function" ? await options.source() : options.source;
|
|
@@ -10413,7 +10399,6 @@ var createVoiceBrowserCallProfileRoutes = (options = {}) => {
|
|
|
10413
10399
|
|
|
10414
10400
|
// src/browserMediaRoutes.ts
|
|
10415
10401
|
import { Elysia as Elysia8 } from "elysia";
|
|
10416
|
-
var escapeHtml12 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10417
10402
|
var isMediaWebRTCStatsReport = (value) => {
|
|
10418
10403
|
const report = value;
|
|
10419
10404
|
return !!report && typeof report === "object" && (report.status === "pass" || report.status === "warn" || report.status === "fail") && typeof report.activeCandidatePairs === "number" && typeof report.liveAudioTracks === "number" && typeof report.packetLossRatio === "number" && typeof report.bytesReceived === "number" && typeof report.bytesSent === "number" && Array.isArray(report.issues);
|
|
@@ -10483,9 +10468,9 @@ var renderVoiceBrowserMediaHTML = (report, options = {}) => {
|
|
|
10483
10468
|
const latestContinuity = report.latest?.continuity;
|
|
10484
10469
|
const rows = report.recent.slice(0, 20).map((sample) => {
|
|
10485
10470
|
const stalledStreams = (sample.continuity?.stalledInboundStreams ?? 0) + (sample.continuity?.stalledOutboundStreams ?? 0);
|
|
10486
|
-
return `<tr><td>${
|
|
10471
|
+
return `<tr><td>${escapeHtml(sample.sessionId)}</td><td>${escapeHtml(mergeIssues(sample.report, sample.continuity).status)}</td><td>${String(sample.report.activeCandidatePairs)}</td><td>${String(sample.report.liveAudioTracks)}</td><td>${String(sample.continuity?.inboundAudioStreams ?? "n/a")}</td><td>${String(sample.continuity?.outboundAudioStreams ?? "n/a")}</td><td>${String(stalledStreams)}</td><td>${String(sample.report.roundTripTimeMs ?? "n/a")}ms</td><td>${String(sample.report.jitterMs ?? "n/a")}ms</td><td>${String(sample.report.packetLossRatio)}</td><td>${escapeHtml(new Date(sample.at).toLocaleString())}</td></tr>`;
|
|
10487
10472
|
}).join("");
|
|
10488
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
10473
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0f172a;color:#e2e8f0;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,.primitive,table{background:#111827;border:1px solid #334155;border-radius:22px;margin-bottom:16px}.hero,.primitive{padding:22px}.eyebrow{color:#93c5fd;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}.pass{color:#86efac}.warn,.empty{color:#fde68a}.fail{color:#fecaca}.metrics{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin-top:18px}.metric{background:#0b1220;border:1px solid #263244;border-radius:16px;padding:14px}.metric span{color:#94a3b8}.metric strong{display:block;font-size:1.7rem;margin-top:4px}.primitive code{color:#bfdbfe}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Browser WebRTC media proof</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">Status: ${escapeHtml(report.status)}</p><p>Recent <code>client.browser_media</code> traces from browser <code>RTCPeerConnection.getStats()</code> reports, including aggregate transport stats and per-stream continuity.</p><section class="metrics"><div class="metric"><span>Reports</span><strong>${String(report.total)}</strong></div><div class="metric"><span>Candidate pairs</span><strong>${String(latest?.activeCandidatePairs ?? 0)}</strong></div><div class="metric"><span>Live audio tracks</span><strong>${String(latest?.liveAudioTracks ?? 0)}</strong></div><div class="metric"><span>Inbound streams</span><strong>${String(latestContinuity?.inboundAudioStreams ?? "n/a")}</strong></div><div class="metric"><span>Outbound streams</span><strong>${String(latestContinuity?.outboundAudioStreams ?? "n/a")}</strong></div><div class="metric"><span>Stalled streams</span><strong>${String((latestContinuity?.stalledInboundStreams ?? 0) + (latestContinuity?.stalledOutboundStreams ?? 0))}</strong></div><div class="metric"><span>RTT</span><strong>${String(latest?.roundTripTimeMs ?? "n/a")}ms</strong></div><div class="metric"><span>Jitter</span><strong>${String(latest?.jitterMs ?? "n/a")}ms</strong></div><div class="metric"><span>Loss</span><strong>${String(latest?.packetLossRatio ?? "n/a")}</strong></div></section></section><section class="primitive"><p class="eyebrow">Copy into your app</p><p><code>createVoiceBrowserMediaReporter({ peerConnection, continuity })</code> runs in the browser and posts reports here. <code>getLatestVoiceBrowserMediaReport(...)</code> can feed production readiness with aggregate and continuity issues merged.</p></section><table><thead><tr><th>Session</th><th>Status</th><th>Pairs</th><th>Tracks</th><th>Inbound</th><th>Outbound</th><th>Stalled</th><th>RTT</th><th>Jitter</th><th>Loss</th><th>Measured</th></tr></thead><tbody>${rows || '<tr><td colspan="11">No browser media reports yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
10489
10474
|
};
|
|
10490
10475
|
var createVoiceBrowserMediaRoutes = (options) => {
|
|
10491
10476
|
const path = options.path ?? "/api/voice/browser-media";
|
|
@@ -10538,7 +10523,6 @@ import {
|
|
|
10538
10523
|
// src/sessionReplay.ts
|
|
10539
10524
|
import { Elysia as Elysia9 } from "elysia";
|
|
10540
10525
|
var getString5 = (value) => typeof value === "string" ? value : undefined;
|
|
10541
|
-
var escapeHtml13 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10542
10526
|
var increment3 = (record, key) => {
|
|
10543
10527
|
record[key] = (record[key] ?? 0) + 1;
|
|
10544
10528
|
};
|
|
@@ -10735,10 +10719,10 @@ var summarizeVoiceSessions = async (options = {}) => {
|
|
|
10735
10719
|
var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="voice-sessions-empty">No voice sessions found.</p>' : [
|
|
10736
10720
|
'<div class="voice-sessions-list">',
|
|
10737
10721
|
...sessions.map((session) => [
|
|
10738
|
-
`<article class="voice-session-card ${
|
|
10722
|
+
`<article class="voice-session-card ${escapeHtml(session.status)}">`,
|
|
10739
10723
|
'<div class="voice-session-card-header">',
|
|
10740
|
-
`<strong>${
|
|
10741
|
-
`<span>${
|
|
10724
|
+
`<strong>${escapeHtml(session.sessionId)}</strong>`,
|
|
10725
|
+
`<span>${escapeHtml(session.status)}</span>`,
|
|
10742
10726
|
"</div>",
|
|
10743
10727
|
"<dl>",
|
|
10744
10728
|
`<div><dt>Events</dt><dd>${String(session.eventCount)}</dd></div>`,
|
|
@@ -10746,9 +10730,9 @@ var renderVoiceSessionsHTML = (sessions) => sessions.length === 0 ? '<p class="v
|
|
|
10746
10730
|
`<div><dt>Transcripts</dt><dd>${String(session.transcriptCount)}</dd></div>`,
|
|
10747
10731
|
`<div><dt>Errors</dt><dd>${String(session.errorCount)}</dd></div>`,
|
|
10748
10732
|
"</dl>",
|
|
10749
|
-
session.latestOutcome ? `<p>Outcome: ${
|
|
10750
|
-
session.providers.length ? `<p>Providers: ${session.providers.map(
|
|
10751
|
-
session.replayHref ? `<p>${session.operationsRecordHref ? `<a href="${
|
|
10733
|
+
session.latestOutcome ? `<p>Outcome: ${escapeHtml(session.latestOutcome)}</p>` : "",
|
|
10734
|
+
session.providers.length ? `<p>Providers: ${session.providers.map(escapeHtml).join(", ")}</p>` : "",
|
|
10735
|
+
session.replayHref ? `<p>${session.operationsRecordHref ? `<a href="${escapeHtml(session.operationsRecordHref)}">Open operations record</a> \xB7 ` : ""}<a href="${escapeHtml(session.replayHref)}">Open replay</a></p>` : "",
|
|
10752
10736
|
"</article>"
|
|
10753
10737
|
].join("")),
|
|
10754
10738
|
"</div>"
|
|
@@ -10818,7 +10802,6 @@ var createVoiceSessionReplayRoutes = (options) => {
|
|
|
10818
10802
|
|
|
10819
10803
|
// src/traceTimeline.ts
|
|
10820
10804
|
import { Elysia as Elysia10 } from "elysia";
|
|
10821
|
-
var escapeHtml14 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
10822
10805
|
var getString6 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
10823
10806
|
var getNumber4 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
10824
10807
|
var firstString = (payload, keys) => {
|
|
@@ -11004,17 +10987,17 @@ var summarizeVoiceTraceTimeline = (events, options = {}) => {
|
|
|
11004
10987
|
};
|
|
11005
10988
|
};
|
|
11006
10989
|
var formatMs = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
11007
|
-
var renderProviderCards = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${
|
|
10990
|
+
var renderProviderCards = (session) => session.providers.length === 0 ? '<p class="muted">No provider events recorded for this session.</p>' : `<div class="providers">${session.providers.map((provider) => `<article><strong>${escapeHtml(provider.provider)}</strong><dl><div><dt>Events</dt><dd>${String(provider.eventCount)}</dd></div><div><dt>Avg</dt><dd>${formatMs(provider.averageElapsedMs)}</dd></div><div><dt>Max</dt><dd>${formatMs(provider.maxElapsedMs)}</dd></div><div><dt>Errors</dt><dd>${String(provider.errorCount)}</dd></div><div><dt>Fallbacks</dt><dd>${String(provider.fallbackCount)}</dd></div><div><dt>Timeouts</dt><dd>${String(provider.timeoutCount)}</dd></div></dl></article>`).join("")}</div>`;
|
|
11008
10991
|
var renderVoiceTraceTimelineSessionHTML = (session, options = {}) => {
|
|
11009
|
-
const events = session.events.map((event) => `<tr class="${
|
|
11010
|
-
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${
|
|
11011
|
-
const supportLinks = session.operationsRecordHref ? `<p><a href="${
|
|
11012
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
10992
|
+
const events = session.events.map((event) => `<tr class="${escapeHtml(event.status ?? "")}"><td>+${String(event.offsetMs)}ms</td><td>${escapeHtml(event.type)}</td><td>${escapeHtml(event.label)}</td><td>${escapeHtml(event.provider ?? "")}</td><td>${escapeHtml(event.status ?? "")}</td><td>${formatMs(event.elapsedMs)}</td></tr>`).join("");
|
|
10993
|
+
const issues = session.evaluation.issues.length ? session.evaluation.issues.map((issue) => `<li class="${escapeHtml(issue.severity)}">${escapeHtml(issue.code)}: ${escapeHtml(issue.message)}</li>`).join("") : "<li>none</li>";
|
|
10994
|
+
const supportLinks = session.operationsRecordHref ? `<p><a href="${escapeHtml(session.operationsRecordHref)}">Open operations record</a></p>` : "";
|
|
10995
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(options.title ?? "Voice Trace Timeline")}</title><style>${timelineCSS}</style></head><body><main><a href="/traces">Back to traces</a><header><p class="eyebrow">Call timeline</p><h1>${escapeHtml(session.sessionId)}</h1><p class="status ${escapeHtml(session.status)}">${escapeHtml(session.status)}</p>${supportLinks}</header><section class="metrics"><article><span>Events</span><strong>${String(session.summary.eventCount)}</strong></article><article><span>Turns</span><strong>${String(session.summary.turnCount)}</strong></article><article><span>Errors</span><strong>${String(session.summary.errorCount)}</strong></article><article><span>Duration</span><strong>${formatMs(session.summary.callDurationMs)}</strong></article></section><section><h2>Providers</h2>${renderProviderCards(session)}</section><section><h2>Issues</h2><ul>${issues}</ul></section><section><h2>Timeline</h2><table><thead><tr><th>Offset</th><th>Type</th><th>Event</th><th>Provider</th><th>Status</th><th>Latency</th></tr></thead><tbody>${events}</tbody></table></section></main></body></html>`;
|
|
11013
10996
|
};
|
|
11014
|
-
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${
|
|
10997
|
+
var renderSessionRows = (report) => report.sessions.length === 0 ? '<tr><td colspan="7">No trace events recorded yet.</td></tr>' : report.sessions.map((session) => `<tr class="${escapeHtml(session.status)}"><td>${session.operationsRecordHref ? `<a href="${escapeHtml(session.operationsRecordHref)}">${escapeHtml(session.sessionId)}</a>` : `<a href="/traces/${encodeURIComponent(session.sessionId)}">${escapeHtml(session.sessionId)}</a>`}</td><td>${escapeHtml(session.status)}</td><td>${String(session.summary.eventCount)}</td><td>${String(session.summary.turnCount)}</td><td>${String(session.summary.errorCount)}</td><td>${formatMs(session.summary.callDurationMs)}</td><td>${session.providers.map((provider) => escapeHtml(provider.provider)).join(", ")}</td></tr>`).join("");
|
|
11015
10998
|
var timelineCSS = "body{background:#0f1318;color:#f6f2e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}a{color:#fbbf24}.eyebrow{color:#fbbf24;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.metrics,.providers{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:20px 0}.metrics article,.providers article{background:#181f27;border:1px solid #2b3642;border-radius:20px;padding:16px}.metrics span,dt,.muted{color:#a8b0b8}.metrics strong{display:block;font-size:2rem}dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:12px 0 0}dd{font-weight:800;margin:4px 0 0}table{background:#181f27;border-collapse:collapse;border-radius:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #2b3642;padding:12px;text-align:left}section{margin-top:28px}@media(max-width:760px){main{padding:20px}table{font-size:.9rem}}";
|
|
11016
10999
|
var renderVoiceTraceTimelineHTML = (report, options = {}) => {
|
|
11017
|
-
const snippet =
|
|
11000
|
+
const snippet = escapeHtml(`const traceStore = createVoiceTraceSinkStore({
|
|
11018
11001
|
store: runtimeStorage.traces,
|
|
11019
11002
|
sinks: [
|
|
11020
11003
|
createVoiceTraceHTTPSink({
|
|
@@ -11040,7 +11023,7 @@ app.use(
|
|
|
11040
11023
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
11041
11024
|
})
|
|
11042
11025
|
);`);
|
|
11043
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
11026
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(options.title ?? "Voice Trace Timelines")}</title><style>${timelineCSS}.primitive{background:#181f27;border:1px solid #334155;border-radius:20px;margin:20px 0;padding:18px}.primitive p{line-height:1.55}.primitive pre{background:#0b1118;border:1px solid #2b3642;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.primitive code{color:#bfdbfe}</style></head><body><main><header><p class="eyebrow">Self-hosted voice debugging</p><h1>${escapeHtml(options.title ?? "Voice Trace Timelines")}</h1><p class="muted">Per-call event timelines with provider latency, fallback, timeout, handoff, and error context.</p></header><section class="metrics"><article><span>Sessions</span><strong>${String(report.total)}</strong></article><article><span>Failed</span><strong>${String(report.failed)}</strong></article><article><span>Warnings</span><strong>${String(report.warnings)}</strong></article></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceTraceTimelineRoutes(...)</code> makes traces the proof backbone</h2><p class="muted">Mount trace timelines from the same trace store used by readiness, simulations, provider recovery, delivery sinks, and phone-agent smoke proof.</p><pre><code>${snippet}</code></pre></section><table><thead><tr><th>Session</th><th>Status</th><th>Events</th><th>Turns</th><th>Errors</th><th>Duration</th><th>Providers</th></tr></thead><tbody>${renderSessionRows(report)}</tbody></table></main></body></html>`;
|
|
11044
11027
|
};
|
|
11045
11028
|
var createVoiceTraceTimelineRoutes = (options) => {
|
|
11046
11029
|
const path = options.path ?? "/api/voice-traces";
|
|
@@ -11710,7 +11693,6 @@ var renderVoiceFailureReplayMarkdown = (report) => {
|
|
|
11710
11693
|
].join(`
|
|
11711
11694
|
`);
|
|
11712
11695
|
};
|
|
11713
|
-
var escapeHtml15 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
11714
11696
|
var formatMs2 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
11715
11697
|
var outcomeLabels = (outcome) => [
|
|
11716
11698
|
outcome.complete ? "complete" : undefined,
|
|
@@ -11836,18 +11818,18 @@ var renderVoiceOperationsRecordGuardrailMarkdown = (record) => {
|
|
|
11836
11818
|
`);
|
|
11837
11819
|
};
|
|
11838
11820
|
var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
11839
|
-
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${
|
|
11840
|
-
const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${
|
|
11841
|
-
const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${
|
|
11821
|
+
const providers = record.providers.length ? record.providers.map((provider) => `<article><strong>${escapeHtml(provider.provider)}</strong><span>${String(provider.eventCount)} events</span><span>${formatMs2(provider.averageElapsedMs)} avg</span><span>${String(provider.errorCount)} errors</span></article>`).join("") : '<p class="muted">No provider events recorded.</p>';
|
|
11822
|
+
const transcript = record.transcript.length ? record.transcript.map((turn) => `<li><strong>${escapeHtml(turn.id)}</strong>${turn.committedText ? `<p><span class="label">Caller</span>${escapeHtml(turn.committedText)}</p>` : ""}${turn.assistantReplies.map((reply) => `<p><span class="label">Assistant</span>${escapeHtml(reply)}</p>`).join("")}${turn.errors.map((error) => `<p class="error"><span class="label">Error</span>${escapeHtml(error)}</p>`).join("")}</li>`).join("") : "<li>No transcript turns recorded.</li>";
|
|
11823
|
+
const providerDecisions = record.providerDecisions.length ? record.providerDecisions.map((decision) => `<li><strong>${escapeHtml(decision.provider ?? decision.selectedProvider ?? decision.fallbackProvider ?? "provider")}</strong> <span>${escapeHtml(decision.status ?? decision.type)}</span> ${formatMs2(decision.elapsedMs)}${decision.surface ? `<p><span class="label">Surface</span>${escapeHtml(decision.surface)}</p>` : ""}${decision.kind ? `<p><span class="label">Kind</span>${escapeHtml(decision.kind)}</p>` : ""}${decision.selectedProvider ? `<p>Selected: ${escapeHtml(decision.selectedProvider)}</p>` : ""}${decision.fallbackProvider ? `<p>Fallback: ${escapeHtml(decision.fallbackProvider)}</p>` : ""}${decision.error ? `<p class="error">${escapeHtml(decision.error)}</p>` : ""}${decision.reason ? `<p>${escapeHtml(decision.reason)}</p>` : ""}</li>`).join("") : "<li>No provider decisions recorded.</li>";
|
|
11842
11824
|
const providerDecisionSummary = record.providerDecisionSummary;
|
|
11843
|
-
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${
|
|
11844
|
-
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${
|
|
11845
|
-
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${
|
|
11846
|
-
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${
|
|
11847
|
-
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${
|
|
11825
|
+
const handoffs = record.handoffs.length ? record.handoffs.map((handoff) => `<li><strong>${escapeHtml(handoff.fromAgentId ?? "unknown")}</strong> to <strong>${escapeHtml(handoff.targetAgentId ?? "unknown")}</strong> <span>${escapeHtml(handoff.status ?? "")}</span><p>${escapeHtml(handoff.summary ?? handoff.reason ?? "")}</p></li>`).join("") : "<li>No agent handoffs recorded.</li>";
|
|
11826
|
+
const tools = record.tools.length ? record.tools.map((tool) => `<li><strong>${escapeHtml(tool.toolName ?? "tool")}</strong> <span>${escapeHtml(tool.status ?? "")}</span> ${formatMs2(tool.elapsedMs)} ${tool.error ? `<p>${escapeHtml(tool.error)}</p>` : ""}</li>`).join("") : "<li>No tool calls recorded.</li>";
|
|
11827
|
+
const reviews = record.reviews?.reviews.length ? record.reviews.reviews.map((review) => `<li><strong>${escapeHtml(review.title)}</strong> <span>${escapeHtml(review.summary.outcome ?? "")}</span><p>${escapeHtml(review.postCall?.summary ?? review.transcript.actual)}</p></li>`).join("") : "<li>No call reviews recorded.</li>";
|
|
11828
|
+
const tasks = record.tasks?.tasks.length ? record.tasks.tasks.map((task) => `<li><strong>${escapeHtml(task.title)}</strong> <span>${escapeHtml(task.status)}</span><p>${escapeHtml(task.recommendedAction)}</p></li>`).join("") : "<li>No ops tasks recorded.</li>";
|
|
11829
|
+
const integrationEvents = record.integrationEvents?.events.length ? record.integrationEvents.events.map((event) => `<li><strong>${escapeHtml(event.type)}</strong> <span>${escapeHtml(event.deliveryStatus ?? "local")}</span><p>${escapeHtml(event.deliveryError ?? event.deliveredTo ?? "")}</p></li>`).join("") : "<li>No integration events recorded.</li>";
|
|
11848
11830
|
const guardrails = record.guardrails.total ? record.guardrails.decisions.map((decision) => {
|
|
11849
11831
|
const findings = decision.findings.map((finding) => finding.label ?? finding.ruleId ?? finding.action).filter((value) => typeof value === "string").join(", ") || "none";
|
|
11850
|
-
return `<li><strong>assistant.guardrail ${
|
|
11832
|
+
return `<li><strong>assistant.guardrail ${escapeHtml(decision.stage ?? "unknown")}</strong> <span>${escapeHtml(decision.status ?? "")}</span><p>Allowed: ${escapeHtml(String(decision.allowed ?? "unknown"))} \xB7 Proof: ${escapeHtml(decision.proof ?? "runtime")}${decision.turnId ? ` \xB7 Turn: ${escapeHtml(decision.turnId)}` : ""}</p><p>${escapeHtml(findings)}</p></li>`;
|
|
11851
11833
|
}).join("") : "<li>No assistant.guardrail events recorded.</li>";
|
|
11852
11834
|
const telephonyMedia = record.telephonyMedia.events.length ? record.telephonyMedia.events.slice(0, 50).map((event) => {
|
|
11853
11835
|
const details = [
|
|
@@ -11858,12 +11840,12 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
11858
11840
|
event.sequenceNumber ? `Seq: ${event.sequenceNumber}` : undefined,
|
|
11859
11841
|
`Audio bytes: ${String(event.audioBytes)}`
|
|
11860
11842
|
].filter((detail) => typeof detail === "string");
|
|
11861
|
-
return `<li><strong>${
|
|
11843
|
+
return `<li><strong>${escapeHtml(event.event)}</strong> <span>${escapeHtml(new Date(event.at).toLocaleString())}</span><p>${escapeHtml(details.join(" \xB7 "))}</p></li>`;
|
|
11862
11844
|
}).join("") : "<li>No telephony media trace events recorded.</li>";
|
|
11863
|
-
const mediaPipelineSection = record.mediaPipeline ? `<section id="media-pipeline"><h2>Media Pipeline</h2><p class="muted">Surface: ${
|
|
11864
|
-
const mediaPipelineCard = record.mediaPipeline ? `<div class="card"><span>Media pipeline</span><strong>${
|
|
11845
|
+
const mediaPipelineSection = record.mediaPipeline ? `<section id="media-pipeline"><h2>Media Pipeline</h2><p class="muted">Surface: ${escapeHtml(record.mediaPipeline.surface)} \xB7 Status: ${escapeHtml(record.mediaPipeline.status)} \xB7 Quality: ${escapeHtml(record.mediaPipeline.qualityStatus)} \xB7 Transport: ${escapeHtml(record.mediaPipeline.transportStatus ?? "n/a")} \xB7 Graph: ${escapeHtml(record.mediaPipeline.processorGraphStatus ?? "n/a")} \xB7 Frames: ${String(record.mediaPipeline.frames)} \xB7 Jitter: ${record.mediaPipeline.jitterMs === undefined ? "n/a" : `${String(record.mediaPipeline.jitterMs)}ms`}</p><ul>${record.mediaPipeline.issueCodes.length ? record.mediaPipeline.issueCodes.map((code) => `<li><strong>${escapeHtml(code)}</strong></li>`).join("") : "<li>No media pipeline issue codes.</li>"}</ul></section>` : "";
|
|
11846
|
+
const mediaPipelineCard = record.mediaPipeline ? `<div class="card"><span>Media pipeline</span><strong>${escapeHtml(record.mediaPipeline.status)}</strong><span>${String(record.mediaPipeline.issueCodes.length)} issue code(s)</span></div>` : "";
|
|
11865
11847
|
const mediaPipelineNavLink = record.mediaPipeline ? '<a href="#media-pipeline">Media pipeline</a>' : "";
|
|
11866
|
-
const snippet =
|
|
11848
|
+
const snippet = escapeHtml(`app.use(
|
|
11867
11849
|
createVoiceOperationsRecordRoutes({
|
|
11868
11850
|
audit: auditStore,
|
|
11869
11851
|
integrationEvents: opsEvents,
|
|
@@ -11877,9 +11859,9 @@ var renderVoiceOperationsRecordHTML = (record, options = {}) => {
|
|
|
11877
11859
|
tasks: opsTasks
|
|
11878
11860
|
})
|
|
11879
11861
|
);`);
|
|
11880
|
-
const incidentMarkdown =
|
|
11881
|
-
const incidentLink = options.incidentHref ? `<a href="${
|
|
11882
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
11862
|
+
const incidentMarkdown = escapeHtml(renderVoiceOperationsRecordIncidentMarkdown(record));
|
|
11863
|
+
const incidentLink = options.incidentHref ? `<a href="${escapeHtml(options.incidentHref)}">Download incident.md</a>` : "";
|
|
11864
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(options.title ?? "Voice Operations Record")}</title><style>body{background:#101417;color:#f9f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.eyebrow{color:#fbbf24;font-size:.8rem;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 #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy{color:#86efac}.warning{color:#fbbf24}.failed,.error{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));margin:20px 0}.card,.primitive{background:#182025;border:1px solid #2d3a43;border-radius:20px;padding:16px}.card span,.muted,.label{color:#a9b4bd}.label{display:block;font-size:.72rem;font-weight:900;letter-spacing:.12em;text-transform:uppercase}.card strong{display:block;font-size:2rem}section{margin-top:28px}article{display:grid;gap:8px}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#182025;border:1px solid #2d3a43;border-radius:16px;padding:14px}pre{background:#080d10;border:1px solid #2d3a43;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}.hero-actions{display:flex;flex-wrap:wrap;gap:10px;margin-top:16px}.hero-actions a{background:#fbbf24;border-radius:999px;color:#111827;font-weight:900;padding:10px 14px;text-decoration:none}.two-column{display:grid;gap:18px;grid-template-columns:minmax(0,1.15fr) minmax(280px,.85fr)}@media(max-width:860px){main{padding:20px}.two-column{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">Call log replacement</p><h1>${escapeHtml(options.title ?? "Voice Operations Record")}</h1><p class="status ${escapeHtml(record.status)}">${escapeHtml(record.status)}</p><div class="hero-actions"><a href="#transcript">Transcript</a><a href="#provider-decisions">Provider decisions</a><a href="#telephony-media">Telephony media</a>${mediaPipelineNavLink}<a href="#guardrails">Guardrails</a><a href="#incident-handoff">Incident handoff</a>${incidentLink}</div><section class="grid"><div class="card"><span>Events</span><strong>${String(record.summary.eventCount)}</strong></div><div class="card"><span>Turns</span><strong>${String(record.summary.turnCount)}</strong></div><div class="card"><span>Errors</span><strong>${String(record.summary.errorCount)}</strong></div><div class="card"><span>Duration</span><strong>${formatMs2(record.summary.callDurationMs)}</strong></div><div class="card"><span>Provider recovery</span><strong>${escapeHtml(providerDecisionSummary.recoveryStatus)}</strong><span>${String(providerDecisionSummary.fallbacks)} fallback / ${String(providerDecisionSummary.degraded)} degraded / ${String(providerDecisionSummary.errors)} errors</span></div><div class="card"><span>Telephony media</span><strong>${String(record.telephonyMedia.media)}</strong><span>${String(record.telephonyMedia.inbound)} inbound / ${String(record.telephonyMedia.outbound)} outbound / ${String(record.telephonyMedia.clears)} clears</span></div><div class="card"><span>Guardrails</span><strong>${String(record.guardrails.blocked)}</strong></div><div class="card"><span>Audit</span><strong>${String(record.audit?.total ?? 0)}</strong></div><div class="card"><span>Reviews</span><strong>${String(record.reviews?.total ?? 0)}</strong></div><div class="card"><span>Tasks</span><strong>${String(record.tasks?.total ?? 0)}</strong></div><div class="card"><span>Integrations</span><strong>${String(record.integrationEvents?.total ?? 0)}</strong></div>${mediaPipelineCard}</section><section class="two-column"><div><h2 id="transcript">Transcript</h2><ul>${transcript}</ul></div><div><h2 id="provider-decisions">Provider Decisions</h2><ul>${providerDecisions}</ul></div></section><section id="telephony-media"><h2>Telephony Media</h2><p class="muted">Live <code>client.telephony_media</code> stream lifecycle evidence attached to this session. Carriers: ${escapeHtml(record.telephonyMedia.carriers.join(", ") || "none")}. Streams: ${escapeHtml(record.telephonyMedia.streamIds.join(", ") || "none")}. Inbound: ${String(record.telephonyMedia.inbound)}. Outbound: ${String(record.telephonyMedia.outbound)}. Marks: ${String(record.telephonyMedia.marks)}. Clears: ${String(record.telephonyMedia.clears)}.</p><ul>${telephonyMedia}</ul></section>${mediaPipelineSection}<section id="guardrails"><h2>Guardrail Evidence</h2><p class="muted">Live <code>assistant.guardrail</code> decisions attached to this session.</p><ul>${guardrails}</ul></section><section id="incident-handoff"><h2>Copyable Incident Handoff</h2><p class="muted">Paste this into Slack, Linear, Zendesk, or an incident review. ${incidentLink}</p><pre><code>${incidentMarkdown}</code></pre></section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceOperationsRecordRoutes(...)</code> gives every call one debuggable object</h2><p class="muted">Use this as the support/debug payload across traces, provider routing, tools, handoffs, guardrails, audit, latency, replay, reviews, tasks, media streams, and webhook delivery.</p><pre><code>${snippet}</code></pre></section><section><h2>Provider Summary</h2><div class="grid">${providers}</div></section><section><h2>Handoffs</h2><ul>${handoffs}</ul></section><section><h2>Tools</h2><ul>${tools}</ul></section><section><h2>Reviews</h2><ul>${reviews}</ul></section><section><h2>Tasks</h2><ul>${tasks}</ul></section><section><h2>Integration Events</h2><ul>${integrationEvents}</ul></section></main></body></html>`;
|
|
11883
11865
|
};
|
|
11884
11866
|
var createVoiceOperationsRecordRoutes = (options) => {
|
|
11885
11867
|
const path = options.path ?? "/api/voice-operations/:sessionId";
|
|
@@ -12122,7 +12104,6 @@ var createVoiceSessionSnapshotRoutes = (options = {}) => {
|
|
|
12122
12104
|
};
|
|
12123
12105
|
|
|
12124
12106
|
// src/callDebugger.ts
|
|
12125
|
-
var escapeHtml16 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
12126
12107
|
var resolveSessionId = (params) => {
|
|
12127
12108
|
const sessionId = params.sessionId;
|
|
12128
12109
|
return typeof sessionId === "string" ? sessionId : "";
|
|
@@ -12214,20 +12195,20 @@ var buildVoiceCallDebuggerReport = async (options, input) => {
|
|
|
12214
12195
|
status
|
|
12215
12196
|
};
|
|
12216
12197
|
};
|
|
12217
|
-
var renderMetric = (label, value) => `<div class="card"><span>${
|
|
12198
|
+
var renderMetric = (label, value) => `<div class="card"><span>${escapeHtml(label)}</span><strong>${escapeHtml(String(value))}</strong></div>`;
|
|
12218
12199
|
var renderArtifact = (artifact) => {
|
|
12219
|
-
const body = `<strong>${
|
|
12220
|
-
return artifact.href ? `<a href="${
|
|
12200
|
+
const body = `<strong>${escapeHtml(artifact.label)}</strong><span>${escapeHtml(artifact.status ?? "n/a")}</span>`;
|
|
12201
|
+
return artifact.href ? `<a href="${escapeHtml(artifact.href)}">${body}</a>` : `<div>${body}</div>`;
|
|
12221
12202
|
};
|
|
12222
12203
|
var renderVoiceCallDebuggerHTML = (report, options = {}) => {
|
|
12223
12204
|
const title = options.title ?? "Voice Call Debugger";
|
|
12224
|
-
const transcript = report.operationsRecord.transcript.length ? report.operationsRecord.transcript.map((turn) => `<li><strong>${
|
|
12225
|
-
const providerDecisions = report.operationsRecord.providerDecisions.length ? report.operationsRecord.providerDecisions.slice(0, 12).map((decision) => `<li><strong>${
|
|
12226
|
-
const failureIssues = report.failureReplay.summary.issues.length ? report.failureReplay.summary.issues.map((issue) => `<li>${
|
|
12227
|
-
const heard = report.failureReplay.summary.userHeard.length ? report.failureReplay.summary.userHeard.map((text) => `<li>${
|
|
12205
|
+
const transcript = report.operationsRecord.transcript.length ? report.operationsRecord.transcript.map((turn) => `<li><strong>${escapeHtml(turn.id)}</strong><p>${escapeHtml(turn.committedText ?? turn.transcripts.at(-1) ?? "No transcript text.")}</p><p>${escapeHtml(turn.assistantReplies.at(-1) ?? "No assistant reply recorded.")}</p></li>`).join("") : "<li>No transcript turns recorded.</li>";
|
|
12206
|
+
const providerDecisions = report.operationsRecord.providerDecisions.length ? report.operationsRecord.providerDecisions.slice(0, 12).map((decision) => `<li><strong>${escapeHtml(decision.provider ?? decision.selectedProvider ?? "provider")}</strong><p>${escapeHtml(decision.status ?? "unknown")} ${decision.fallbackProvider ? `via ${escapeHtml(decision.fallbackProvider)}` : ""}</p><p>${escapeHtml(decision.reason ?? "No reason recorded.")}</p></li>`).join("") : "<li>No provider decisions recorded.</li>";
|
|
12207
|
+
const failureIssues = report.failureReplay.summary.issues.length ? report.failureReplay.summary.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("") : "<li>No failure or recovery issues recorded.</li>";
|
|
12208
|
+
const heard = report.failureReplay.summary.userHeard.length ? report.failureReplay.summary.userHeard.map((text) => `<li>${escapeHtml(text)}</li>`).join("") : "<li>No assistant output recorded.</li>";
|
|
12228
12209
|
const artifacts = report.snapshot.artifacts.length ? report.snapshot.artifacts.map(renderArtifact).join("") : "<div><strong>No linked artifacts</strong><span>empty</span></div>";
|
|
12229
12210
|
const incidentPath = `/voice-call-debugger/${encodeURIComponent(report.sessionId)}/incident.md`;
|
|
12230
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
12211
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0d1216;color:#f8f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.eyebrow{color:#5eead4;font-size:.78rem;font-weight:900;letter-spacing:.14em;text-transform:uppercase}h1{font-size:clamp(2.4rem,7vw,5rem);line-height:.9;margin:.2rem 0 1rem}.status{border:1px solid #475569;border-radius:999px;display:inline-flex;padding:8px 12px}.healthy,.pass{color:#86efac}.warning,.warn,.degraded{color:#fbbf24}.failed,.fail{color:#fca5a5}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(175px,1fr));margin:20px 0}.card,section{background:#161f26;border:1px solid #2b3943;border-radius:20px;padding:16px}.card span,.muted{color:#a9b5bd}.card strong{display:block;font-size:1.8rem;margin-top:4px}section{margin-top:18px}.two{display:grid;gap:18px;grid-template-columns:1fr 1fr}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#0f171d;border:1px solid #2b3943;border-radius:14px;padding:12px}.actions,.artifacts{display:flex;flex-wrap:wrap;gap:10px}.actions a,.artifacts a,.artifacts div{background:#5eead4;border-radius:999px;color:#061014;font-weight:900;padding:10px 14px;text-decoration:none}.artifacts a,.artifacts div{align-items:center;background:#111a20;border:1px solid #2b3943;color:#f8f4e8;display:flex;gap:10px;justify-content:space-between}.artifacts span{color:#5eead4;text-transform:uppercase}pre{background:#090e12;border:1px solid #2b3943;border-radius:16px;color:#dbeafe;overflow:auto;padding:14px}@media(max-width:860px){main{padding:20px}.two{grid-template-columns:1fr}}</style></head><body><main><p class="eyebrow">One-call support artifact</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">${escapeHtml(report.status)}</p><p class="muted">Session <code>${escapeHtml(report.sessionId)}</code>. Checked ${escapeHtml(new Date(report.checkedAt).toLocaleString())}.</p><div class="actions"><a href="/api/voice-call-debugger/${encodeURIComponent(report.sessionId)}">JSON</a><a href="${escapeHtml(incidentPath)}">Incident markdown</a>${report.failureReplay.operationsRecordHref ? `<a href="${escapeHtml(report.failureReplay.operationsRecordHref)}">Operations record</a>` : ""}</div><section class="grid">${renderMetric("Snapshot", report.snapshot.status)}${renderMetric("Events", report.operationsRecord.summary.eventCount)}${renderMetric("Turns", report.operationsRecord.summary.turnCount)}${renderMetric("Errors", report.operationsRecord.summary.errorCount)}${renderMetric("Provider recovery", report.operationsRecord.providerDecisionSummary.recoveryStatus)}${renderMetric("Fallbacks", report.operationsRecord.providerDecisionSummary.fallbacks)}${renderMetric("Media warnings", report.snapshot.media.filter((media) => media.report.status !== "pass").length)}${renderMetric("Telephony media", report.operationsRecord.telephonyMedia.total)}</section><section><h2>Linked Debug Artifacts</h2><div class="artifacts">${artifacts}</div></section><section class="two"><div><h2>Provider Decisions</h2><ul>${providerDecisions}</ul></div><div><h2>Failure Replay</h2><ul>${failureIssues}</ul><h3>User Heard</h3><ul>${heard}</ul></div></section><section><h2>Transcript</h2><ul>${transcript}</ul></section><section><h2>Copyable Incident Handoff</h2><pre><code>${escapeHtml(report.incidentMarkdown)}</code></pre></section></main></body></html>`;
|
|
12231
12212
|
};
|
|
12232
12213
|
var createVoiceCallDebuggerRoutes = (options) => {
|
|
12233
12214
|
const path = options.path ?? "/api/voice-call-debugger/:sessionId";
|
|
@@ -13406,7 +13387,6 @@ var assertVoiceCampaignReadinessEvidence = (report, input = {}) => {
|
|
|
13406
13387
|
}
|
|
13407
13388
|
return assertion;
|
|
13408
13389
|
};
|
|
13409
|
-
var escapeHtml17 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13410
13390
|
var getString8 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
13411
13391
|
var campaignAttemptSessionId = (attempt) => getString8(attempt.metadata?.sessionId) ?? getString8(attempt.metadata?.voiceSessionId) ?? getString8(attempt.metadata?.callSessionId);
|
|
13412
13392
|
var resolveCampaignOperationsRecordHref = (value, input) => {
|
|
@@ -13424,7 +13404,7 @@ var resolveCampaignOperationsRecordHref = (value, input) => {
|
|
|
13424
13404
|
};
|
|
13425
13405
|
var renderVoiceCampaignsHTML = (records, options = {}) => {
|
|
13426
13406
|
const title = options.title ?? "Voice Campaigns";
|
|
13427
|
-
const rows = records.map((record) => `<tr><td>${
|
|
13407
|
+
const rows = records.map((record) => `<tr><td>${escapeHtml(record.campaign.name)}</td><td>${escapeHtml(record.campaign.status)}</td><td>${String(record.recipients.length)}</td><td>${String(record.attempts.length)}</td><td>${new Date(record.campaign.updatedAt).toLocaleString()}</td></tr>`).join("");
|
|
13428
13408
|
const summary = summarizeVoiceCampaigns(records);
|
|
13429
13409
|
const attemptRows = records.flatMap((record) => record.attempts.slice(-8).reverse().map((attempt) => {
|
|
13430
13410
|
const recipient = record.recipients.find((item) => item.id === attempt.recipientId);
|
|
@@ -13435,15 +13415,15 @@ var renderVoiceCampaignsHTML = (records, options = {}) => {
|
|
|
13435
13415
|
recipient,
|
|
13436
13416
|
sessionId
|
|
13437
13417
|
});
|
|
13438
|
-
return `<tr><td>${
|
|
13418
|
+
return `<tr><td>${escapeHtml(record.campaign.name)}</td><td>${escapeHtml(attempt.status)}</td><td>${escapeHtml(recipient?.phone ?? attempt.recipientId)}</td><td>${escapeHtml(sessionId ?? "")}</td><td>${href ? `<a href="${escapeHtml(href)}">Open operations record</a>` : ""}</td></tr>`;
|
|
13439
13419
|
})).slice(0, 20).join("");
|
|
13440
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13420
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#111827;color:#f9fafb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero{background:linear-gradient(135deg,rgba(251,146,60,.18),rgba(45,212,191,.12));border:1px solid #334155;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#fdba74;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,5rem);line-height:.9;margin:.2rem 0 1rem}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));margin:18px 0}.grid article,table{background:#172033;border:1px solid #334155;border-radius:18px}.grid article{padding:16px}.grid span{color:#aab5c0}.grid strong{display:block;font-size:2rem;margin:.25rem 0}table{border-collapse:collapse;margin-top:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:12px;text-align:left}a{color:#fdba74}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted outbound</p><h1>${escapeHtml(title)}</h1><p>Campaign orchestration, recipients, attempts, retries, and outcomes without a hosted dialer dashboard.</p><section class="grid"><article><span>Campaigns</span><strong>${String(summary.campaigns.total)}</strong></article><article><span>Recipients</span><strong>${String(summary.recipients.total)}</strong></article><article><span>Attempts</span><strong>${String(summary.attempts.total)}</strong></article><article><span>Running</span><strong>${String(summary.campaigns.running)}</strong></article></section></section><table><thead><tr><th>Name</th><th>Status</th><th>Recipients</th><th>Attempts</th><th>Updated</th></tr></thead><tbody>${rows || '<tr><td colspan="5">No campaigns yet.</td></tr>'}</tbody></table><h2>Recent attempts</h2><table><thead><tr><th>Campaign</th><th>Status</th><th>Recipient</th><th>Session</th><th>Debug</th></tr></thead><tbody>${attemptRows || '<tr><td colspan="5">No attempts yet.</td></tr>'}</tbody></table></main></body></html>`;
|
|
13441
13421
|
};
|
|
13442
13422
|
var renderVoiceCampaignObservabilityHTML = (report, options = {}) => {
|
|
13443
13423
|
const title = options.title ?? "Voice Campaign Observability";
|
|
13444
|
-
const campaignRows = report.campaigns.map((campaign) => `<tr><td>${
|
|
13445
|
-
const failureRows = report.failureReasons.map((failure) => `<tr><td>${
|
|
13446
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13424
|
+
const campaignRows = report.campaigns.map((campaign) => `<tr><td>${escapeHtml(campaign.name)}</td><td>${escapeHtml(campaign.status)}</td><td>${String(campaign.queueDepth)}</td><td>${String(campaign.activeAttempts)}</td><td>${String(campaign.stuckRecipients + campaign.stuckAttempts)}</td><td>${campaign.lease ? escapeHtml(campaign.lease.workerId) : "none"}</td></tr>`).join("");
|
|
13425
|
+
const failureRows = report.failureReasons.map((failure) => `<tr><td>${escapeHtml(failure.reason)}</td><td>${String(failure.count)}</td></tr>`).join("");
|
|
13426
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0b1220;color:#e5edf7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.2),rgba(251,146,60,.14));border:1px solid #334155;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.2rem,5vw,4.6rem);line-height:.95;margin:.2rem 0 1rem}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));margin:18px 0}.card,table{background:#111c2f;border:1px solid #334155;border-radius:18px}.card{padding:16px}.card span{color:#9fb0c5}.card strong{display:block;font-size:2rem;margin:.25rem 0}table{border-collapse:collapse;margin-top:18px;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:12px;text-align:left}.warn{color:#fde68a}.bad{color:#fecaca}</style></head><body><main><section class="hero"><p class="eyebrow">Campaign ops</p><h1>${escapeHtml(title)}</h1><p>Queue depth, active leases, attempt rates, failure reasons, and stuck work for self-hosted outbound voice.</p><section class="grid"><article class="card"><span>Queued recipients</span><strong>${String(report.queue.queuedRecipients)}</strong></article><article class="card"><span>Active attempts</span><strong>${String(report.queue.activeAttempts)}</strong></article><article class="card"><span>Running campaigns</span><strong>${String(report.queue.runningCampaigns)}</strong></article><article class="card"><span>Active leases</span><strong>${report.leases.known ? String(report.leases.active) : "n/a"}</strong></article><article class="card"><span>Attempts/window</span><strong>${String(report.attemptRate.started)}</strong></article><article class="card"><span>Stuck work</span><strong class="${report.stuck.attempts.length + report.stuck.recipients.length > 0 ? "bad" : ""}">${String(report.stuck.attempts.length + report.stuck.recipients.length)}</strong></article></section></section><h2>Campaigns</h2><table><thead><tr><th>Name</th><th>Status</th><th>Queued</th><th>Active</th><th>Stuck</th><th>Lease</th></tr></thead><tbody>${campaignRows || '<tr><td colspan="6">No campaigns yet.</td></tr>'}</tbody></table><h2>Failure Reasons</h2><table><thead><tr><th>Reason</th><th>Count</th></tr></thead><tbody>${failureRows || '<tr><td colspan="2">No failures recorded.</td></tr>'}</tbody></table></main></body></html>`;
|
|
13447
13427
|
};
|
|
13448
13428
|
var readJsonBody = async (request) => {
|
|
13449
13429
|
const text = await request.text();
|
|
@@ -13487,7 +13467,6 @@ var createVoiceCampaignRoutes = (options) => {
|
|
|
13487
13467
|
|
|
13488
13468
|
// src/competitiveCoverage.ts
|
|
13489
13469
|
import { Elysia as Elysia15 } from "elysia";
|
|
13490
|
-
var escapeHtml18 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13491
13470
|
var escapeMarkdown = (value) => value.replaceAll("|", "\\|");
|
|
13492
13471
|
var resolveSurfaceStatus = (surface) => {
|
|
13493
13472
|
if (surface.status)
|
|
@@ -13655,24 +13634,24 @@ var renderVoiceCompetitiveCoverageMarkdown = (report, title = "Voice Competitive
|
|
|
13655
13634
|
`);
|
|
13656
13635
|
var renderVoiceCompetitiveCoverageHTML = (report, title = "Voice Competitive Coverage") => {
|
|
13657
13636
|
const surfaceCards = report.surfaces.map((surface) => {
|
|
13658
|
-
const evidence = (surface.evidence ?? []).map((item) => `<li><strong>${
|
|
13659
|
-
return `<article class="surface ${
|
|
13660
|
-
<header><div><p class="eyebrow">${
|
|
13661
|
-
<p>${
|
|
13637
|
+
const evidence = (surface.evidence ?? []).map((item) => `<li><strong>${escapeHtml(item.name)}</strong>${item.kind ? ` <span>${escapeHtml(item.kind)}</span>` : ""}${item.status ? ` <em>${escapeHtml(item.status)}</em>` : ""}${item.href ? ` <a href="${escapeHtml(item.href)}">open</a>` : ""}</li>`).join("");
|
|
13638
|
+
return `<article class="surface ${escapeHtml(surface.status)} ${escapeHtml(surface.depth)}">
|
|
13639
|
+
<header><div><p class="eyebrow">${escapeHtml(surface.coverage)} \xB7 ${escapeHtml(surface.depth)}</p><h2>${escapeHtml(surface.surface)}</h2></div><strong>${escapeHtml(surface.status)}</strong></header>
|
|
13640
|
+
<p>${escapeHtml(surface.why)}</p>
|
|
13662
13641
|
<dl>
|
|
13663
|
-
<div><dt>Competitors</dt><dd>${
|
|
13664
|
-
<div><dt>Operations record</dt><dd>${
|
|
13665
|
-
<div><dt>Readiness gate</dt><dd>${
|
|
13666
|
-
<div><dt>Frameworks</dt><dd>${
|
|
13642
|
+
<div><dt>Competitors</dt><dd>${escapeHtml((surface.competitors ?? []).join(", ") || "n/a")}</dd></div>
|
|
13643
|
+
<div><dt>Operations record</dt><dd>${escapeHtml(surface.operationsRecord ?? "unknown")}</dd></div>
|
|
13644
|
+
<div><dt>Readiness gate</dt><dd>${escapeHtml(surface.readinessGate ?? "unknown")}</dd></div>
|
|
13645
|
+
<div><dt>Frameworks</dt><dd>${escapeHtml((surface.frameworkPrimitives ?? []).join(", ") || "n/a")}</dd></div>
|
|
13667
13646
|
</dl>
|
|
13668
|
-
${surface.remainingGap ? `<p class="gap"><strong>Gap:</strong> ${
|
|
13669
|
-
${surface.nextMove ? `<p class="next"><strong>Next:</strong> ${
|
|
13647
|
+
${surface.remainingGap ? `<p class="gap"><strong>Gap:</strong> ${escapeHtml(surface.remainingGap)}</p>` : ""}
|
|
13648
|
+
${surface.nextMove ? `<p class="next"><strong>Next:</strong> ${escapeHtml(surface.nextMove)}</p>` : ""}
|
|
13670
13649
|
${evidence ? `<h3>Evidence</h3><ul>${evidence}</ul>` : '<p class="muted">No evidence links configured.</p>'}
|
|
13671
13650
|
</article>`;
|
|
13672
13651
|
}).join(`
|
|
13673
13652
|
`);
|
|
13674
|
-
const issueList = report.issues.map((issue) => `<li class="${
|
|
13675
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
13653
|
+
const issueList = report.issues.map((issue) => `<li class="${escapeHtml(issue.severity)}"><strong>${escapeHtml(issue.code)}</strong>${issue.surface ? ` ${escapeHtml(issue.surface)}` : ""}: ${escapeHtml(issue.message)}</li>`).join("");
|
|
13654
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0e1412;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,.surface,.issues{background:#17201c;border:1px solid #2e3c35;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.16),rgba(245,158,11,.12))}.eyebrow{color:#5eead4;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{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.surfaces{display:grid;gap:14px}.surface header{align-items:flex-start;display:flex;gap:16px;justify-content:space-between}.surface.pass{border-color:rgba(34,197,94,.55)}.surface.warn{border-color:rgba(245,158,11,.72)}.surface.fail{border-color:rgba(239,68,68,.75)}.surface.advantage h2{color:#bbf7d0}.surface.intentional-gap h2{color:#cbd5e1}dl{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}dt{color:#9fb0a8;font-size:.8rem;font-weight:800}dd{margin:0;overflow-wrap:anywhere}.gap{color:#fde68a}.next{color:#bfdbfe}.muted{color:#a8b5ad}a{color:#5eead4}.issues li{margin:.4rem 0}.issues .error{color:#fecaca}.issues .warning{color:#fde68a}@media(max-width:760px){main{padding:18px}.surface header{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Self-hosted market proof</p><h1>${escapeHtml(title)}</h1><p>Generated ${escapeHtml(report.generatedAt)}. This report scores whether AbsoluteJS Voice merely covers a hosted-platform buyer surface or beats it for a code-owned/self-hosted buyer.</p><div class="summary"><span class="pill">Status ${escapeHtml(report.status)}</span><span class="pill">Vapi-style ${escapeHtml(report.vapiCoverageEstimate)}</span><span class="pill">Market ${escapeHtml(report.marketCoverageEstimate)}</span><span class="pill">${String(report.summary.surfaces)} surfaces</span><span class="pill">${String(report.summary.advantage)} advantage</span><span class="pill">${String(report.summary.intentionalGaps)} intentional gaps</span></div></section><section class="issues"><h2>Issues</h2><ul>${issueList || "<li>No issues.</li>"}</ul></section><section class="surfaces">${surfaceCards || '<article class="surface"><p>No competitive surfaces configured.</p></article>'}</section></main></body></html>`;
|
|
13676
13655
|
};
|
|
13677
13656
|
var normalizeCompetitiveCoverageReport = (value) => ("status" in value) && ("summary" in value) && ("issues" in value) ? value : buildVoiceCompetitiveCoverageReport(value);
|
|
13678
13657
|
var createVoiceCompetitiveCoverageRoutes = (options) => {
|
|
@@ -13956,7 +13935,6 @@ var parseRetentionScopes = (value) => {
|
|
|
13956
13935
|
const allowed = new Set(allRetentionScopes);
|
|
13957
13936
|
return value.split(",").map((entry) => entry.trim()).filter((entry) => allowed.has(entry));
|
|
13958
13937
|
};
|
|
13959
|
-
var escapeHtml19 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
13960
13938
|
var buildStorageSurfaces = (options) => [
|
|
13961
13939
|
{
|
|
13962
13940
|
configured: Boolean(options.session ?? options.sessions),
|
|
@@ -14193,12 +14171,12 @@ var buildVoiceDataControlReport = async (options) => {
|
|
|
14193
14171
|
zeroRetentionAvailable: true
|
|
14194
14172
|
};
|
|
14195
14173
|
};
|
|
14196
|
-
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${
|
|
14174
|
+
var renderDataRetentionReportRows = (report) => report.scopes.map((scope) => `<tr><td>${escapeHtml(scope.scope)}</td><td>${scope.scannedCount}</td><td>${scope.deletedCount}</td><td>${escapeHtml(scope.skippedReason ?? "")}</td><td><code>${escapeHtml(scope.deletedIds.join(", "))}</code></td></tr>`).join("");
|
|
14197
14175
|
var renderVoiceDataControlHTML = (report, options = {}) => {
|
|
14198
14176
|
const title = options.title ?? "Voice Data Control";
|
|
14199
|
-
const storageRows = report.storage.map((surface) => `<tr><td>${
|
|
14200
|
-
const keyRows = report.providerKeys.map((key) => `<tr><td>${
|
|
14201
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
14177
|
+
const storageRows = report.storage.map((surface) => `<tr><td>${escapeHtml(surface.name)}</td><td>${surface.configured ? "Configured" : "Missing"}</td><td>${escapeHtml(surface.control)}</td><td>${surface.selfHosted ? "Yes" : "No"}</td></tr>`).join("");
|
|
14178
|
+
const keyRows = report.providerKeys.map((key) => `<tr><td>${escapeHtml(key.name)}</td><td><code>${escapeHtml(key.env ?? "n/a")}</code></td><td>${key.required ? "Required" : "Optional"}</td><td>${escapeHtml(key.recommendation)}</td></tr>`).join("");
|
|
14179
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
14202
14180
|
};
|
|
14203
14181
|
var renderVoiceDataControlMarkdown = (report, options = {}) => [
|
|
14204
14182
|
`# ${options.title ?? "Voice Data Control"}`,
|
|
@@ -15278,12 +15256,11 @@ var createVoiceOpsTaskProcessorWorkerLoop = (options) => {
|
|
|
15278
15256
|
};
|
|
15279
15257
|
|
|
15280
15258
|
// src/deliveryRuntime.ts
|
|
15281
|
-
var escapeHtml20 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15282
15259
|
var renderSummaryCard = (label, summary) => {
|
|
15283
15260
|
if (!summary) {
|
|
15284
|
-
return `<article><span>${
|
|
15261
|
+
return `<article><span>${escapeHtml(label)}</span><strong>Disabled</strong><p class="muted">No worker configured.</p></article>`;
|
|
15285
15262
|
}
|
|
15286
|
-
return `<article><span>${
|
|
15263
|
+
return `<article><span>${escapeHtml(label)}</span><strong>${String(summary.delivered)}/${String(summary.total)}</strong><p class="muted">${String(summary.pending)} pending · ${String(summary.failed)} failed · ${String(summary.deadLettered)} dead-lettered</p></article>`;
|
|
15287
15264
|
};
|
|
15288
15265
|
var resolvePresetLeases = (leases) => ("claim" in leases) ? {
|
|
15289
15266
|
audit: leases,
|
|
@@ -15494,9 +15471,9 @@ var buildVoiceDeliveryRuntimeReport = async (runtime) => ({
|
|
|
15494
15471
|
});
|
|
15495
15472
|
var renderVoiceDeliveryRuntimeHTML = (report, options = {}) => {
|
|
15496
15473
|
const title = options.title ?? "AbsoluteJS Voice Delivery Runtime";
|
|
15497
|
-
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${
|
|
15498
|
-
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${
|
|
15499
|
-
const snippet =
|
|
15474
|
+
const tickForm = options.tickPath === false ? "" : `<form method="post" action="${escapeHtml(options.tickPath ?? "/api/voice-delivery-runtime/tick")}"><button type="submit">Tick delivery workers</button></form>`;
|
|
15475
|
+
const requeueForm = options.requeueDeadLettersPath === false ? "" : `<form method="post" action="${escapeHtml(options.requeueDeadLettersPath ?? "/api/voice-delivery-runtime/requeue-dead-letters")}"><button type="submit">Requeue dead letters</button></form>`;
|
|
15476
|
+
const snippet = escapeHtml(`const deliveryRuntime = createVoiceDeliveryRuntime(
|
|
15500
15477
|
createVoiceDeliveryRuntimePresetConfig({
|
|
15501
15478
|
audit: {
|
|
15502
15479
|
deliveries: runtimeStorage.auditDeliveries,
|
|
@@ -15522,7 +15499,7 @@ app.use(
|
|
|
15522
15499
|
traceDeliveries: runtimeStorage.traceDeliveries
|
|
15523
15500
|
})
|
|
15524
15501
|
);`);
|
|
15525
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15502
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0f1411;color:#f7f2df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}a{color:#86efac;text-decoration:none}.hero{background:linear-gradient(135deg,rgba(34,197,94,.18),rgba(14,165,233,.13));border:1px solid #263a30;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,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}.status.running{border-color:rgba(34,197,94,.7);color:#bbf7d0}.muted{color:#b9c3b4}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin:18px 0}article,.card{background:#151d18;border:1px solid #263a30;border-radius:22px;padding:18px}.primitive{background:#111a15;border-color:#41604a}article span{color:#b9c3b4;display:block;font-weight:800}article strong{display:block;font-size:2.3rem;margin-top:8px}.actions{display:flex;flex-wrap:wrap;gap:10px}button{background:#86efac;border:0;border-radius:999px;color:#07120b;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}pre{background:#09100c;border:1px solid #263a30;border-radius:18px;color:#dcfce7;overflow:auto;padding:16px}.primitive p{color:#c8d8ca;line-height:1.55}.primitive code{color:#bbf7d0}</style></head><body><main><p><a href="/delivery-sinks">Delivery sinks</a></p><section class="hero"><p class="eyebrow">Worker control plane</p><h1>${escapeHtml(title)}</h1><p class="muted">Inspect queue summaries, manually tick failed/pending audit and trace deliveries, and requeue dead letters after operator review.</p><p class="status ${report.isRunning ? "running" : ""}">${report.isRunning ? "Running" : "Stopped"}</p><p class="muted">Checked ${escapeHtml(new Date(report.checkedAt).toLocaleString())}</p><div class="actions">${tickForm}${requeueForm}</div></section><section class="grid">${renderSummaryCard("Audit", report.summary.audit)}${renderSummaryCard("Trace", report.summary.trace)}</section><section class="card primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceDeliveryRuntimeRoutes(...)</code> builds this control plane</h2><p>Own the audit and trace delivery queues in your app, mount one runtime route group, and pass the same runtime into production readiness so failed or dead-lettered exports block deploys.</p><pre><code>${snippet}</code></pre></section></main></body></html>`;
|
|
15526
15503
|
};
|
|
15527
15504
|
var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
15528
15505
|
const path = options.path ?? "/api/voice-delivery-runtime";
|
|
@@ -15565,9 +15542,20 @@ var createVoiceDeliveryRuntimeRoutes = (options) => {
|
|
|
15565
15542
|
return routes;
|
|
15566
15543
|
};
|
|
15567
15544
|
|
|
15545
|
+
// src/internal/status.ts
|
|
15546
|
+
var voiceStatusRank = (status) => status === "fail" ? 2 : status === "warn" ? 1 : 0;
|
|
15547
|
+
var worstVoiceStatus = (statuses) => {
|
|
15548
|
+
let worst = "pass";
|
|
15549
|
+
for (const status of statuses) {
|
|
15550
|
+
if (voiceStatusRank(status) > voiceStatusRank(worst)) {
|
|
15551
|
+
worst = status;
|
|
15552
|
+
}
|
|
15553
|
+
}
|
|
15554
|
+
return worst;
|
|
15555
|
+
};
|
|
15556
|
+
|
|
15568
15557
|
// src/deliverySinkRoutes.ts
|
|
15569
15558
|
import { Elysia as Elysia18 } from "elysia";
|
|
15570
|
-
var escapeHtml21 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15571
15559
|
var deliveryStatus = (summary) => {
|
|
15572
15560
|
if (!summary) {
|
|
15573
15561
|
return "warn";
|
|
@@ -15585,7 +15573,7 @@ var rollupDeliverySinkStatus = (report) => {
|
|
|
15585
15573
|
deliveryStatus(report.auditDeliveries?.summary),
|
|
15586
15574
|
deliveryStatus(report.traceDeliveries?.summary)
|
|
15587
15575
|
];
|
|
15588
|
-
return
|
|
15576
|
+
return worstVoiceStatus(statuses);
|
|
15589
15577
|
};
|
|
15590
15578
|
var deliverySinkLabel = (kind) => `${String(kind).replaceAll(/[-_]+/g, " ")} sink`;
|
|
15591
15579
|
var createVoiceDeliverySinkDescriptor = (input) => ({
|
|
@@ -15666,13 +15654,13 @@ var renderSurfaceCard = (surface) => {
|
|
|
15666
15654
|
return "";
|
|
15667
15655
|
}
|
|
15668
15656
|
const value = `${surface.summary.delivered}/${surface.summary.total}`;
|
|
15669
|
-
const body = `<span>${
|
|
15670
|
-
return `<article>${surface.href ? `<a href="${
|
|
15657
|
+
const body = `<span>${escapeHtml(surface.label)}</span><strong>${escapeHtml(value)}</strong><p class="muted">Delivered export records.</p>`;
|
|
15658
|
+
return `<article>${surface.href ? `<a href="${escapeHtml(surface.href)}">${body}</a>` : body}</article>`;
|
|
15671
15659
|
};
|
|
15672
15660
|
var renderVoiceDeliverySinkHTML = (report, options = {}) => {
|
|
15673
15661
|
const title = options.title ?? "AbsoluteJS Voice Delivery Sinks";
|
|
15674
|
-
const sinks = report.sinks.length ? report.sinks.map((sink) => `<article><span>${
|
|
15675
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15662
|
+
const sinks = report.sinks.length ? report.sinks.map((sink) => `<article><span>${escapeHtml(sink.kind)}</span><strong style="font-size:1.5rem">${escapeHtml(sink.label)}</strong>${sink.description ? `<p class="muted">${escapeHtml(sink.description)}</p>` : ""}${sink.mode ? `<p class="muted">Mode: ${escapeHtml(sink.mode)}</p>` : ""}${sink.target ? `<p class="muted">Target: <code>${escapeHtml(sink.target)}</code></p>` : ""}${sink.href ? `<p><a href="${escapeHtml(sink.href)}">Open sink</a></p>` : ""}</article>`).join("") : '<article><span>Sink</span><strong style="font-size:1.5rem">Not described</strong><p class="muted">Pass sink descriptors to document your file, webhook, S3, SQLite, or Postgres targets.</p></article>';
|
|
15663
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#11120d;color:#fbf7e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{max-width:1120px;margin:auto;padding:32px}a{color:#fde68a;text-decoration:none}.hero{background:linear-gradient(135deg,rgba(253,230,138,.2),rgba(34,197,94,.14));border:1px solid #3a3420;border-radius:30px;margin-bottom:18px;padding:28px}.eyebrow{color:#fde68a;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.4rem,6vw,4.8rem);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,.65)}.status.fail{border-color:rgba(239,68,68,.75)}.muted{color:#b8b093}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));margin:18px 0}article,.card{background:#191a13;border:1px solid #33311f;border-radius:22px;padding:18px}article span{color:#b8b093;display:block;font-weight:800}article strong{display:block;font-size:2.4rem;margin-top:8px}pre{background:#0c0d09;border:1px solid #33311f;border-radius:18px;color:#fef3c7;overflow:auto;padding:16px}code{color:#fef3c7}</style></head><body><main><p><a href="/production-readiness">Production readiness</a></p><section class="hero"><p class="eyebrow">Composable sink primitive</p><h1>${escapeHtml(title)}</h1><p class="muted">Delivery queues prove audit and trace exports without owning your infrastructure. Swap file, webhook, S3, SQLite, or Postgres sinks behind the same readiness surface.</p><p class="status ${escapeHtml(report.status)}">Overall: ${escapeHtml(report.status.toUpperCase())}</p><p class="muted">Checked ${escapeHtml(new Date(report.checkedAt).toLocaleString())}</p></section><section class="grid">${renderSurfaceCard(report.auditDeliveries)}${renderSurfaceCard(report.traceDeliveries)}${sinks}</section><section class="card"><h2>Primitive shape</h2><p class="muted">Mount delivery sink routes beside audit and trace delivery queues. Production readiness can consume the same stores for pass/fail evidence.</p><pre>createVoiceDeliverySinkRoutes({
|
|
15676
15664
|
auditDeliveries: { store: runtimeStorage.auditDeliveries },
|
|
15677
15665
|
traceDeliveries: { store: runtimeStorage.traceDeliveries },
|
|
15678
15666
|
sinks: createVoiceDeliverySinkPair({
|
|
@@ -15704,8 +15692,7 @@ var createVoiceDeliverySinkRoutes = (options) => {
|
|
|
15704
15692
|
|
|
15705
15693
|
// src/demoReadyRoutes.ts
|
|
15706
15694
|
import { Elysia as Elysia19 } from "elysia";
|
|
15707
|
-
var
|
|
15708
|
-
var rollupStatus = (sections) => sections.some((section) => section.status === "fail") ? "fail" : sections.some((section) => section.status === "warn") ? "warn" : "pass";
|
|
15695
|
+
var rollupStatus = (sections) => worstVoiceStatus(sections.map((section) => section.status));
|
|
15709
15696
|
var resolveLoader = async (loader, input) => typeof loader === "function" ? await loader(input) : loader;
|
|
15710
15697
|
var buildVoiceDemoReadyReport = async (options, input = {}) => {
|
|
15711
15698
|
const query = input.query ?? {};
|
|
@@ -15788,12 +15775,12 @@ var buildVoiceDemoReadyReport = async (options, input = {}) => {
|
|
|
15788
15775
|
};
|
|
15789
15776
|
};
|
|
15790
15777
|
var renderVoiceDemoReadyHTML = (report) => {
|
|
15791
|
-
const sections = report.sections.map((section) => `<article class="section ${
|
|
15792
|
-
<div><span>${
|
|
15793
|
-
<strong>${
|
|
15794
|
-
${section.href ? `<a href="${
|
|
15778
|
+
const sections = report.sections.map((section) => `<article class="section ${escapeHtml(section.status)}">
|
|
15779
|
+
<div><span>${escapeHtml(section.status.toUpperCase())}</span><h2>${escapeHtml(section.label)}</h2>${section.description ? `<p>${escapeHtml(section.description)}</p>` : ""}</div>
|
|
15780
|
+
<strong>${escapeHtml(String(section.value ?? section.status))}</strong>
|
|
15781
|
+
${section.href ? `<a href="${escapeHtml(section.href)}">Open</a>` : ""}
|
|
15795
15782
|
</article>`).join("");
|
|
15796
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15783
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(report.title)}</title><style>body{background:#0d141b;color:#f8f3e7;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(20,184,166,.2),rgba(245,158,11,.12));border:1px solid #283544;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;font-weight:900;padding:8px 12px}.sections{display:grid;gap:14px}.section{align-items:center;background:#151d26;border:1px solid #283544;border-radius:22px;display:grid;gap:16px;grid-template-columns:1fr auto auto;padding:18px}.section span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.section h2{margin:.2rem 0}.section p{color:#b9c0c8;margin:.2rem 0 0}.section strong{font-size:1.4rem}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.65)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}@media(max-width:760px){main{padding:20px}.section{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Demo readiness</p><h1>${escapeHtml(report.title)}</h1><p>One customer-facing checklist for the self-hosted voice proof surfaces: ops status, production readiness, phone setup, and phone smoke traces.</p><p class="status ${escapeHtml(report.status)}">Overall: ${escapeHtml(report.status.toUpperCase())}</p><p>Checked ${escapeHtml(new Date(report.checkedAt).toLocaleString())}</p></section><section class="sections">${sections || '<article class="section warn"><div><span>WARN</span><h2>No checks configured</h2><p>Add ops status, production readiness, phone setup, or phone smoke loaders.</p></div><strong>warn</strong></article>'}</section></main></body></html>`;
|
|
15797
15784
|
};
|
|
15798
15785
|
var createVoiceDemoReadyRoutes = (options) => {
|
|
15799
15786
|
const path = options.path ?? "/api/demo-ready";
|
|
@@ -15822,7 +15809,6 @@ var createVoiceDemoReadyRoutes = (options) => {
|
|
|
15822
15809
|
|
|
15823
15810
|
// src/diagnosticsRoutes.ts
|
|
15824
15811
|
import { Elysia as Elysia20 } from "elysia";
|
|
15825
|
-
var escapeHtml23 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15826
15812
|
var getString9 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
15827
15813
|
var getNumber6 = (value) => {
|
|
15828
15814
|
const parsed = typeof value === "number" ? value : typeof value === "string" ? Number(value) : undefined;
|
|
@@ -15891,9 +15877,9 @@ var renderDiagnosticsIndex = (input) => {
|
|
|
15891
15877
|
const rows = [...sessions.entries()].sort(([, left], [, right]) => (right.at(-1)?.at ?? 0) - (left.at(-1)?.at ?? 0)).slice(0, 50).map(([sessionId, events]) => {
|
|
15892
15878
|
const summary = summarizeVoiceTrace(events);
|
|
15893
15879
|
const encoded = encodeURIComponent(sessionId);
|
|
15894
|
-
return `<tr><td>${
|
|
15880
|
+
return `<tr><td>${escapeHtml(sessionId)}</td><td>${summary.eventCount}</td><td>${summary.turnCount}</td><td>${summary.errorCount}</td><td><a href="${input.basePath}/html?sessionId=${encoded}&redact=true">HTML</a> \xB7 <a href="${input.basePath}/markdown?sessionId=${encoded}&redact=true">Markdown</a> \xB7 <a href="${input.basePath}/json?sessionId=${encoded}&redact=true">JSON</a></td></tr>`;
|
|
15895
15881
|
}).join("");
|
|
15896
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
15882
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(input.title)}</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1100px;margin:auto}table{width:100%;border-collapse:collapse;background:white}td,th{border-bottom:1px solid #eee;padding:.7rem;text-align:left}a{color:#9a3412}</style></head><body><main><h1>${escapeHtml(input.title)}</h1><p>Recent voice trace diagnostics. Exports support filters: sessionId, traceId, turnId, scenarioId, type, provider, status, since, until, limit, redact.</p><table><thead><tr><th>Session</th><th>Events</th><th>Turns</th><th>Errors</th><th>Exports</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
15897
15883
|
};
|
|
15898
15884
|
var withRedaction = (events, query, defaultRedact) => {
|
|
15899
15885
|
const shouldRedact = query.redact === undefined ? defaultRedact : getBoolean2(query.redact);
|
|
@@ -15969,7 +15955,6 @@ import { Elysia as Elysia22 } from "elysia";
|
|
|
15969
15955
|
|
|
15970
15956
|
// src/handoffHealth.ts
|
|
15971
15957
|
import { Elysia as Elysia21 } from "elysia";
|
|
15972
|
-
var escapeHtml24 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
15973
15958
|
var getString10 = (value) => typeof value === "string" && value.length > 0 ? value : undefined;
|
|
15974
15959
|
var isStatus = (value) => value === "delivered" || value === "failed" || value === "skipped";
|
|
15975
15960
|
var increment4 = (record, key) => {
|
|
@@ -16087,10 +16072,10 @@ var renderActionSummary = (summary) => {
|
|
|
16087
16072
|
return [
|
|
16088
16073
|
'<section class="voice-handoff-health-columns">',
|
|
16089
16074
|
"<article><h3>Actions</h3>",
|
|
16090
|
-
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${
|
|
16075
|
+
actions.length === 0 ? "<p>No handoff actions yet.</p>" : `<ul>${actions.map(([action, count]) => `<li>${escapeHtml(action)}: ${String(count)}</li>`).join("")}</ul>`,
|
|
16091
16076
|
"</article>",
|
|
16092
16077
|
"<article><h3>Adapters</h3>",
|
|
16093
|
-
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${
|
|
16078
|
+
adapters.length === 0 ? "<p>No adapter deliveries yet.</p>" : `<ul>${adapters.map(([adapterId, counts]) => `<li>${escapeHtml(adapterId)}: ${String(counts.delivered)} delivered / ${String(counts.failed)} failed / ${String(counts.skipped)} skipped</li>`).join("")}</ul>`,
|
|
16094
16079
|
"</article>",
|
|
16095
16080
|
"</section>"
|
|
16096
16081
|
].join("");
|
|
@@ -16104,22 +16089,22 @@ var renderVoiceHandoffHealthHTML = (summary) => [
|
|
|
16104
16089
|
summary.events.length === 0 ? '<p class="voice-handoff-health-empty">No handoffs found.</p>' : [
|
|
16105
16090
|
'<div class="voice-handoff-health-events">',
|
|
16106
16091
|
...summary.events.map((event) => [
|
|
16107
|
-
`<article class="${
|
|
16092
|
+
`<article class="${escapeHtml(event.status)}">`,
|
|
16108
16093
|
'<div class="voice-handoff-health-event-header">',
|
|
16109
|
-
`<strong>${
|
|
16110
|
-
`<span>${
|
|
16094
|
+
`<strong>${escapeHtml(event.action ?? "handoff")}</strong>`,
|
|
16095
|
+
`<span>${escapeHtml(event.status)}</span>`,
|
|
16111
16096
|
"</div>",
|
|
16112
|
-
`<p><small>${
|
|
16113
|
-
event.target ? `<p>Target: ${
|
|
16114
|
-
event.reason ? `<p>Reason: ${
|
|
16097
|
+
`<p><small>${escapeHtml(event.sessionId)}</small></p>`,
|
|
16098
|
+
event.target ? `<p>Target: ${escapeHtml(event.target)}</p>` : "",
|
|
16099
|
+
event.reason ? `<p>Reason: ${escapeHtml(event.reason)}</p>` : "",
|
|
16115
16100
|
event.deliveries.length ? `<ul>${event.deliveries.map((delivery) => [
|
|
16116
16101
|
"<li>",
|
|
16117
|
-
`${
|
|
16118
|
-
delivery.deliveredTo ? ` to ${
|
|
16119
|
-
delivery.error ? ` (${
|
|
16102
|
+
`${escapeHtml(delivery.adapterId)}: ${escapeHtml(delivery.status)}`,
|
|
16103
|
+
delivery.deliveredTo ? ` to ${escapeHtml(delivery.deliveredTo)}` : "",
|
|
16104
|
+
delivery.error ? ` (${escapeHtml(delivery.error)})` : "",
|
|
16120
16105
|
"</li>"
|
|
16121
16106
|
].join("")).join("")}</ul>` : "",
|
|
16122
|
-
event.replayHref ? `<p><a href="${
|
|
16107
|
+
event.replayHref ? `<p><a href="${escapeHtml(event.replayHref)}">Open replay</a></p>` : "",
|
|
16123
16108
|
"</article>"
|
|
16124
16109
|
].join("")),
|
|
16125
16110
|
"</div>"
|
|
@@ -16272,12 +16257,11 @@ var evaluateVoiceQuality = async (input) => {
|
|
|
16272
16257
|
thresholds
|
|
16273
16258
|
};
|
|
16274
16259
|
};
|
|
16275
|
-
var escapeHtml25 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
16276
16260
|
var formatMetricValue = (metric) => metric.unit === "rate" ? `${(metric.actual * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.actual)}ms` : String(metric.actual);
|
|
16277
16261
|
var formatThreshold = (metric) => metric.unit === "rate" ? `${(metric.threshold * 100).toFixed(2)}%` : metric.unit === "ms" ? `${Math.round(metric.threshold)}ms` : String(metric.threshold);
|
|
16278
16262
|
var renderVoiceQualityHTML = (report, options = {}) => {
|
|
16279
|
-
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${
|
|
16280
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
16263
|
+
const rows = Object.entries(report.metrics).map(([key, metric]) => `<tr class="${metric.pass ? "pass" : "fail"}"><td>${escapeHtml(metric.label)}</td><td>${escapeHtml(formatMetricValue(metric))}</td><td>${escapeHtml(formatThreshold(metric))}</td><td>${metric.pass ? "pass" : "fail"}</td><td><code>${escapeHtml(key)}</code></td></tr>`).join("");
|
|
16264
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</nav>` : "";
|
|
16281
16265
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>AbsoluteJS Voice Quality</title><style>body{font-family:ui-sans-serif,system-ui,sans-serif;margin:2rem;background:#f8f7f2;color:#181713}main{max-width:1100px;margin:auto}nav{display:flex;flex-wrap:wrap;gap:.5rem;margin:0 0 1.25rem}nav a{background:#181713;border-radius:999px;color:white;padding:.35rem .7rem;text-decoration:none}.status{border-radius:999px;display:inline-flex;padding:.35rem .75rem;font-weight:800}.status.pass{background:#dcfce7;color:#166534}.status.fail{background:#fee2e2;color:#991b1b}table{border-collapse:collapse;width:100%;background:white;margin-top:1rem}td,th{border-bottom:1px solid #eee;padding:.75rem;text-align:left}.pass td{border-left:4px solid #16a34a}.fail td{border-left:4px solid #dc2626}code{background:#f3f4f6;padding:.15rem .3rem;border-radius:.3rem}</style></head><body><main>${links}<h1>Voice quality gates</h1><p class="status ${report.status}">${report.status}</p><p>${report.eventCount} event(s) checked.</p><table><thead><tr><th>Metric</th><th>Actual</th><th>Threshold</th><th>Status</th><th>Key</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
16282
16266
|
};
|
|
16283
16267
|
var createVoiceQualityRoutes = (options) => {
|
|
@@ -16311,7 +16295,6 @@ var createVoiceQualityRoutes = (options) => {
|
|
|
16311
16295
|
};
|
|
16312
16296
|
|
|
16313
16297
|
// src/evalRoutes.ts
|
|
16314
|
-
var escapeHtml26 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
16315
16298
|
var rate2 = (count, total) => count / Math.max(1, total);
|
|
16316
16299
|
var normalizeSearchText = (value) => value.trim().toLowerCase();
|
|
16317
16300
|
var getString12 = (value) => typeof value === "string" ? value : undefined;
|
|
@@ -16642,7 +16625,7 @@ var createVoiceFileScenarioFixtureStore = (filePath) => ({
|
|
|
16642
16625
|
var formatTime = (value) => value === undefined ? "unknown" : new Date(value).toLocaleString();
|
|
16643
16626
|
var formatPercent = (value) => `${(value * 100).toFixed(2)}%`;
|
|
16644
16627
|
var renderVoiceEvalPrimitiveCopy = () => {
|
|
16645
|
-
const snippet =
|
|
16628
|
+
const snippet = escapeHtml(`app.use(
|
|
16646
16629
|
createVoiceEvalRoutes({
|
|
16647
16630
|
path: '/evals',
|
|
16648
16631
|
store: traceStore,
|
|
@@ -16663,44 +16646,44 @@ var renderVoiceEvalPrimitiveCopy = () => {
|
|
|
16663
16646
|
};
|
|
16664
16647
|
var renderVoiceEvalHTML = (report, options = {}) => {
|
|
16665
16648
|
const title = options.title ?? "AbsoluteJS Voice Evals";
|
|
16666
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
16667
|
-
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${
|
|
16649
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</nav>` : "";
|
|
16650
|
+
const trend = report.trend.length ? report.trend.map((bucket) => `<tr><td>${escapeHtml(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>';
|
|
16668
16651
|
const sessions = report.sessions.length ? report.sessions.map((session) => {
|
|
16669
16652
|
const failedMetrics = Object.entries(session.quality.metrics).filter(([, metric]) => !metric.pass).map(([, metric]) => metric.label).join(", ");
|
|
16670
|
-
const sessionLabel = session.operationsRecordHref ? `<a href="${
|
|
16671
|
-
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${
|
|
16653
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml(session.operationsRecordHref)}">${escapeHtml(session.sessionId)}</a>` : escapeHtml(session.sessionId);
|
|
16654
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml(session.status)}</td><td>${session.eventCount}</td><td>${session.summary.turnCount}</td><td>${session.summary.errorCount}</td><td>${escapeHtml(formatTime(session.endedAt))}</td><td>${escapeHtml(failedMetrics || "none")}</td></tr>`;
|
|
16672
16655
|
}).join("") : '<tr><td colspan="7">No sessions found.</td></tr>';
|
|
16673
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
16656
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
16674
16657
|
};
|
|
16675
16658
|
var renderVoiceEvalBaselineHTML = (comparison, options = {}) => {
|
|
16676
16659
|
const title = options.title ?? "AbsoluteJS Voice Eval Baseline";
|
|
16677
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
16678
|
-
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${
|
|
16679
|
-
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${
|
|
16680
|
-
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${
|
|
16681
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
16660
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</nav>` : "";
|
|
16661
|
+
const reasons = comparison.reasons.length ? comparison.reasons.map((reason) => `<li>${escapeHtml(reason)}</li>`).join("") : "<li>No baseline regressions detected.</li>";
|
|
16662
|
+
const newFailures = comparison.newFailedSessionIds.length ? comparison.newFailedSessionIds.map((id) => `<li>${escapeHtml(id)}</li>`).join("") : "<li>none</li>";
|
|
16663
|
+
const recovered = comparison.recoveredSessionIds.length ? comparison.recoveredSessionIds.map((id) => `<li>${escapeHtml(id)}</li>`).join("") : "<li>none</li>";
|
|
16664
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p class="status ${comparison.status}">${comparison.status}</p><div class="grid"><article class="card"><span>Baseline pass rate</span><strong>${escapeHtml(formatPercent(comparison.baseline.passRate))}</strong></article><article class="card"><span>Current pass rate</span><strong>${escapeHtml(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>${escapeHtml(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>`;
|
|
16682
16665
|
};
|
|
16683
16666
|
var renderVoiceScenarioEvalHTML = (report, options = {}) => {
|
|
16684
16667
|
const title = options.title ?? "AbsoluteJS Voice Scenario Evals";
|
|
16685
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
16668
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</nav>` : "";
|
|
16686
16669
|
const scenarios = report.scenarios.length ? report.scenarios.map((scenario) => {
|
|
16687
|
-
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${
|
|
16670
|
+
const scenarioIssues = scenario.issues.length ? `<ul>${scenario.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("")}</ul>` : "";
|
|
16688
16671
|
const sessions = scenario.sessions.length ? scenario.sessions.map((session) => {
|
|
16689
|
-
const sessionLabel = session.operationsRecordHref ? `<a href="${
|
|
16690
|
-
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${
|
|
16672
|
+
const sessionLabel = session.operationsRecordHref ? `<a href="${escapeHtml(session.operationsRecordHref)}">${escapeHtml(session.sessionId)}</a>` : escapeHtml(session.sessionId);
|
|
16673
|
+
return `<tr class="${session.status}"><td>${sessionLabel}</td><td>${escapeHtml(session.status)}</td><td>${session.eventCount}</td><td>${escapeHtml(session.issues.join(", ") || "none")}</td></tr>`;
|
|
16691
16674
|
}).join("") : '<tr><td colspan="4">No matching sessions.</td></tr>';
|
|
16692
|
-
return `<section class="scenario ${scenario.status}"><h2>${
|
|
16675
|
+
return `<section class="scenario ${scenario.status}"><h2>${escapeHtml(scenario.label)}</h2>${scenario.description ? `<p>${escapeHtml(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>`;
|
|
16693
16676
|
}).join("") : "<section><p>No scenarios configured.</p></section>";
|
|
16694
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
16677
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
16695
16678
|
};
|
|
16696
16679
|
var renderVoiceScenarioFixtureEvalHTML = (report, options = {}) => {
|
|
16697
16680
|
const title = options.title ?? "AbsoluteJS Voice Fixture Evals";
|
|
16698
|
-
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${
|
|
16681
|
+
const links = options.links?.length ? `<nav>${options.links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</nav>` : "";
|
|
16699
16682
|
const fixtures = report.fixtures.length ? report.fixtures.map((fixture) => {
|
|
16700
|
-
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${
|
|
16701
|
-
return `<section class="${fixture.status}"><h2>${
|
|
16683
|
+
const scenarios = fixture.report.scenarios.map((scenario) => `<tr class="${scenario.status}"><td>${escapeHtml(scenario.label)}</td><td>${escapeHtml(scenario.status)}</td><td>${scenario.matchedSessions}</td><td>${escapeHtml([...scenario.issues, ...scenario.sessions.flatMap((session) => session.issues)].join(", ") || "none")}</td></tr>`).join("");
|
|
16684
|
+
return `<section class="${fixture.status}"><h2>${escapeHtml(fixture.label)}</h2>${fixture.description ? `<p>${escapeHtml(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>`;
|
|
16702
16685
|
}).join("") : "<section><p>No scenario fixtures configured.</p></section>";
|
|
16703
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
16686
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
16704
16687
|
};
|
|
16705
16688
|
var createVoiceEvalRoutes = (options) => {
|
|
16706
16689
|
const path = options.path ?? "/evals";
|
|
@@ -17509,7 +17492,6 @@ var wrapVoiceHTMLInHTMXContainer = (html, attrs) => {
|
|
|
17509
17492
|
};
|
|
17510
17493
|
|
|
17511
17494
|
// src/client/htmxDashboardRenderers.ts
|
|
17512
|
-
var escapeHtml27 = (text) => text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
17513
17495
|
var formatUsd = (value, currency = "USD") => new Intl.NumberFormat("en-US", {
|
|
17514
17496
|
currency,
|
|
17515
17497
|
maximumFractionDigits: 4,
|
|
@@ -17524,8 +17506,8 @@ var formatRelative = (ms) => {
|
|
|
17524
17506
|
return `${String(minutes).padStart(2, "0")}:${String(remaining).padStart(2, "0")}`;
|
|
17525
17507
|
};
|
|
17526
17508
|
var polledWrapperAttributes = (attrs) => buildVoiceHTMXAttributes(attrs);
|
|
17527
|
-
var renderCostRow = (bucket, currency, isTotal) => `<tr data-bucket-key="${
|
|
17528
|
-
<td style="padding:8px 12px;">${
|
|
17509
|
+
var renderCostRow = (bucket, currency, isTotal) => `<tr data-bucket-key="${escapeHtml(bucket.bucketKey)}" style="${isTotal ? "border-top:2px solid rgba(255,255,255,0.15);font-weight:600;" : ""}">
|
|
17510
|
+
<td style="padding:8px 12px;">${escapeHtml(bucket.bucketKey)}</td>
|
|
17529
17511
|
<td style="padding:8px 12px;text-align:right;">${formatInteger(bucket.callCount)}</td>
|
|
17530
17512
|
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.llmUsd, currency)}</td>
|
|
17531
17513
|
<td style="padding:8px 12px;text-align:right;">${formatUsd(bucket.ttsUsd, currency)}</td>
|
|
@@ -17542,7 +17524,7 @@ var defaultCostDashboard = ({
|
|
|
17542
17524
|
}) => {
|
|
17543
17525
|
const body = report.buckets.map((bucket) => renderCostRow(bucket, currency, false)).join("");
|
|
17544
17526
|
const total = renderCostRow(report.grandTotal, currency, true);
|
|
17545
|
-
const inner = report.buckets.length === 0 ? `<p style="font-size:13px;opacity:0.7;">${
|
|
17527
|
+
const inner = report.buckets.length === 0 ? `<p style="font-size:13px;opacity:0.7;">${escapeHtml(emptyMessage)}</p>` : `<table style="border-collapse:collapse;font-size:13px;width:100%;">
|
|
17546
17528
|
<thead><tr style="opacity:0.7;text-align:left;">
|
|
17547
17529
|
<th style="padding:8px 12px;">Bucket</th>
|
|
17548
17530
|
<th style="padding:8px 12px;text-align:right;">Calls</th>
|
|
@@ -17554,7 +17536,7 @@ var defaultCostDashboard = ({
|
|
|
17554
17536
|
</tr></thead><tbody>${body}${total}</tbody></table>`;
|
|
17555
17537
|
return `<section aria-label="voice-cost-dashboard" class="absolute-voice-cost-dashboard"${polledWrapperAttributes(attributes)} style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
17556
17538
|
<header style="align-items:baseline;display:flex;gap:12px;margin-bottom:12px;">
|
|
17557
|
-
<strong style="font-size:16px;">${
|
|
17539
|
+
<strong style="font-size:16px;">${escapeHtml(title)}</strong>
|
|
17558
17540
|
<span style="font-size:13px;opacity:0.7;">${report.buckets.length} buckets \xB7 grand total ${formatUsd(report.grandTotal.totalUsd, currency)}</span>
|
|
17559
17541
|
</header>
|
|
17560
17542
|
${inner}
|
|
@@ -17568,8 +17550,8 @@ var REPLAY_CATEGORY_COLOR = {
|
|
|
17568
17550
|
};
|
|
17569
17551
|
var renderReplayEntry = (event, startedAt) => `<li style="align-items:center;border-left:3px solid ${REPLAY_CATEGORY_COLOR[event.category]};display:flex;font-size:13px;gap:12px;padding-left:12px;">
|
|
17570
17552
|
<span style="color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;width:60px;">${formatRelative(event.at - startedAt)}</span>
|
|
17571
|
-
<strong style="font-size:13px;">${
|
|
17572
|
-
${event.detail ? `<span style="opacity:0.85;">${
|
|
17553
|
+
<strong style="font-size:13px;">${escapeHtml(event.label)}</strong>
|
|
17554
|
+
${event.detail ? `<span style="opacity:0.85;">${escapeHtml(event.detail)}</span>` : ""}
|
|
17573
17555
|
</li>`;
|
|
17574
17556
|
var defaultReplayTimeline = ({
|
|
17575
17557
|
attributes,
|
|
@@ -17577,9 +17559,9 @@ var defaultReplayTimeline = ({
|
|
|
17577
17559
|
report,
|
|
17578
17560
|
title
|
|
17579
17561
|
}) => {
|
|
17580
|
-
const headline =
|
|
17562
|
+
const headline = escapeHtml(title ?? report.metadata.title ?? "Replay");
|
|
17581
17563
|
const items = report.events.map((event) => renderReplayEntry(event, report.startedAt)).join("");
|
|
17582
|
-
const inner = report.events.length === 0 ? `<p style="font-size:13px;opacity:0.7;">${
|
|
17564
|
+
const inner = report.events.length === 0 ? `<p style="font-size:13px;opacity:0.7;">${escapeHtml(emptyMessage)}</p>` : `<ol style="display:flex;flex-direction:column;gap:6px;list-style:none;margin:0;padding:0;">${items}</ol>`;
|
|
17583
17565
|
return `<section aria-label="voice-replay-timeline" class="absolute-voice-replay-timeline"${polledWrapperAttributes(attributes)} style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
17584
17566
|
<header style="align-items:baseline;display:flex;gap:12px;margin-bottom:12px;">
|
|
17585
17567
|
<strong style="font-size:16px;">${headline}</strong>
|
|
@@ -17597,8 +17579,8 @@ var LIVE_CATEGORY_COLOR = {
|
|
|
17597
17579
|
};
|
|
17598
17580
|
var renderLiveEntry = (event, firstAt) => `<li style="align-items:center;border-left:3px solid ${LIVE_CATEGORY_COLOR[event.kind] ?? "#94a3b8"};display:flex;font-size:13px;gap:12px;padding-left:12px;">
|
|
17599
17581
|
<span style="color:#cbd5e1;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:12px;width:60px;">${formatRelative(event.at - firstAt)}</span>
|
|
17600
|
-
<strong style="font-size:13px;">${
|
|
17601
|
-
${event.detail ? `<span style="opacity:0.85;">${
|
|
17582
|
+
<strong style="font-size:13px;">${escapeHtml(event.title)}</strong>
|
|
17583
|
+
${event.detail ? `<span style="opacity:0.85;">${escapeHtml(event.detail)}</span>` : ""}
|
|
17602
17584
|
</li>`;
|
|
17603
17585
|
var defaultLiveCallViewer = ({
|
|
17604
17586
|
attributes,
|
|
@@ -17607,13 +17589,13 @@ var defaultLiveCallViewer = ({
|
|
|
17607
17589
|
}) => {
|
|
17608
17590
|
const firstAt = state.events[0]?.at ?? Date.now();
|
|
17609
17591
|
const items = state.events.map((event) => renderLiveEntry(event, firstAt)).join("");
|
|
17610
|
-
return `<section aria-label="voice-live-call-viewer" class="absolute-voice-live-call-viewer" data-agent-state="${
|
|
17592
|
+
return `<section aria-label="voice-live-call-viewer" class="absolute-voice-live-call-viewer" data-agent-state="${escapeHtml(state.agentState)}"${polledWrapperAttributes(attributes)} style="background:#0f172a;border-radius:16px;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;padding:20px;">
|
|
17611
17593
|
<header style="align-items:center;display:flex;gap:12px;margin-bottom:12px;">
|
|
17612
|
-
<strong style="font-size:16px;">${
|
|
17613
|
-
<span style="background:rgba(59,130,246,0.18);border-radius:999px;font-size:11px;padding:3px 10px;text-transform:uppercase;">${
|
|
17614
|
-
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${
|
|
17594
|
+
<strong style="font-size:16px;">${escapeHtml(title)}</strong>
|
|
17595
|
+
<span style="background:rgba(59,130,246,0.18);border-radius:999px;font-size:11px;padding:3px 10px;text-transform:uppercase;">${escapeHtml(state.agentState)}</span>
|
|
17596
|
+
<span style="font-size:13px;margin-left:auto;opacity:0.7;">${escapeHtml(state.sessionId)} \xB7 ${formatRelative(state.callDurationMs)}</span>
|
|
17615
17597
|
</header>
|
|
17616
|
-
${state.partialTranscript ? `<p style="background:rgba(16,185,129,0.12);border-radius:12px;font-size:13px;margin:0 0 12px;opacity:0.95;padding:10px 12px;">"${
|
|
17598
|
+
${state.partialTranscript ? `<p style="background:rgba(16,185,129,0.12);border-radius:12px;font-size:13px;margin:0 0 12px;opacity:0.95;padding:10px 12px;">"${escapeHtml(state.partialTranscript)}"</p>` : ""}
|
|
17617
17599
|
<ol style="display:flex;flex-direction:column;gap:6px;list-style:none;margin:0;max-height:320px;overflow-y:auto;padding:0;">${items}</ol>
|
|
17618
17600
|
</section>`;
|
|
17619
17601
|
};
|
|
@@ -18155,14 +18137,12 @@ var buildVoiceOpsActionHistoryReport = async (options) => {
|
|
|
18155
18137
|
total: entries.length
|
|
18156
18138
|
};
|
|
18157
18139
|
};
|
|
18158
|
-
var escapeHtml28 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
18159
18140
|
var renderVoiceOpsActionHistoryHTML = (report) => {
|
|
18160
|
-
const rows = report.entries.map((entry) => `<article class="${entry.ok ? "ok" : "fail"}"><span>${
|
|
18141
|
+
const rows = report.entries.map((entry) => `<article class="${entry.ok ? "ok" : "fail"}"><span>${escapeHtml(entry.ok ? "success" : "error")}</span><strong>${escapeHtml(entry.actionId)}</strong><p>${escapeHtml(new Date(entry.at).toLocaleString())}${entry.status ? ` \xB7 HTTP ${String(entry.status)}` : ""}</p>${entry.error ? `<p>${escapeHtml(entry.error)}</p>` : ""}</article>`).join("");
|
|
18161
18142
|
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Voice Ops Action History</title><style>body{background:#11140f;color:#f7f1df;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,article{background:#181d15;border:1px solid #2c3327;border-radius:24px;padding:20px}.hero{margin-bottom:16px}h1{font-size:clamp(2rem,6vw,4rem);line-height:.95}section{display:grid;gap:12px}article.ok{border-color:rgba(34,197,94,.55)}article.fail{border-color:rgba(239,68,68,.75)}span{color:#facc15;font-weight:900;text-transform:uppercase}p{color:#c8ccb8}</style></head><body><main><section class="hero"><span>Operator proof</span><h1>Voice Ops Action History</h1><p>${String(report.total)} action(s), ${String(report.failed)} failed.</p></section><section>${rows || "<p>No operator actions have been recorded.</p>"}</section></main></body></html>`;
|
|
18162
18143
|
};
|
|
18163
18144
|
|
|
18164
18145
|
// src/incidentTimeline.ts
|
|
18165
|
-
var escapeHtml29 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
18166
18146
|
var resolveValue = async (value) => typeof value === "function" ? await value() : value;
|
|
18167
18147
|
var linkForSession = (link, sessionId) => {
|
|
18168
18148
|
if (!link || !sessionId) {
|
|
@@ -18229,7 +18209,7 @@ var defaultIncidentRecoveryActions = (events, links) => {
|
|
|
18229
18209
|
}
|
|
18230
18210
|
return actions;
|
|
18231
18211
|
};
|
|
18232
|
-
var worstStatus = (statuses) =>
|
|
18212
|
+
var worstStatus = (statuses) => worstVoiceStatus(statuses);
|
|
18233
18213
|
var statusRank2 = (status) => status === "fail" ? 3 : status === "warn" ? 2 : status === "pass" ? 1 : 0;
|
|
18234
18214
|
var isRecord2 = (value) => Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
18235
18215
|
var getIncidentRecoveryBody = (event) => {
|
|
@@ -18283,8 +18263,8 @@ var buildVoiceIncidentRecoveryOutcomeReport = async (options) => {
|
|
|
18283
18263
|
};
|
|
18284
18264
|
var renderVoiceIncidentRecoveryOutcomeHTML = (report, options = {}) => {
|
|
18285
18265
|
const title = options.title ?? "AbsoluteJS Voice Incident Recovery Outcomes";
|
|
18286
|
-
const rows = report.entries.map((entry) => `<article class="${
|
|
18287
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
18266
|
+
const rows = report.entries.map((entry) => `<article class="${escapeHtml(entry.outcome)}"><span>${escapeHtml(entry.outcome.toUpperCase())}</span><h2>${escapeHtml(entry.actionId)}</h2><p>${escapeHtml(new Date(entry.at).toLocaleString())}</p><strong>${escapeHtml(entry.beforeStatus ?? "unknown")} -> ${escapeHtml(entry.afterStatus ?? "unknown")}</strong>${entry.detail ? `<p>${escapeHtml(entry.detail)}</p>` : ""}</article>`).join("");
|
|
18267
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
18288
18268
|
};
|
|
18289
18269
|
var buildVoiceIncidentRecoveryOutcomeReadinessCheck = (report, options = {}) => {
|
|
18290
18270
|
const failOnFailed = options.failOnFailed ?? true;
|
|
@@ -18481,8 +18461,8 @@ ${rows || "| n/a | 0 | 0 | 0 | 0 | 0 | n/a | n/a |"}
|
|
|
18481
18461
|
};
|
|
18482
18462
|
var renderVoiceIncidentRecoveryTrendHTML = (report, options = {}) => {
|
|
18483
18463
|
const title = options.title ?? "AbsoluteJS Voice Incident Recovery Trend";
|
|
18484
|
-
const rows = report.cycles.map((cycle) => `<tr><td>${
|
|
18485
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
18464
|
+
const rows = report.cycles.map((cycle) => `<tr><td>${escapeHtml(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>${escapeHtml(percent(cycle.improvementRate))}</td><td>${escapeHtml(percent(cycle.regressionRate))}</td></tr>`).join("");
|
|
18465
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p class="${escapeHtml(report.status)}">Status: ${escapeHtml(report.status)}</p><div class="summary"><span>${String(report.summary.cycles)} cycles</span><span>${String(report.summary.total)} actions</span><span>${escapeHtml(percent(report.summary.improvementRate))} improved</span><span>${escapeHtml(percent(report.summary.regressionRate))} regressed</span><span>${escapeHtml(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>`;
|
|
18486
18466
|
};
|
|
18487
18467
|
var pushOperationalStatusEvents = (events, report, links) => {
|
|
18488
18468
|
if (!report) {
|
|
@@ -18713,22 +18693,22 @@ ${report.actions.map((action) => `- ${action.method ?? "GET"} ${action.id}: ${ac
|
|
|
18713
18693
|
var renderVoiceIncidentTimelineHTML = (report, options = {}) => {
|
|
18714
18694
|
const title = options.title ?? "AbsoluteJS Voice Incident Timeline";
|
|
18715
18695
|
const actionPath = options.actionPath ?? "/api/voice/incident-timeline/actions";
|
|
18716
|
-
const events = report.events.map((event) => `<article class="${
|
|
18717
|
-
<span>${
|
|
18718
|
-
<h2>${
|
|
18719
|
-
<p>${
|
|
18720
|
-
${event.value === undefined ? "" : `<strong>${
|
|
18721
|
-
${event.detail ? `<p>${
|
|
18722
|
-
<div>${event.href ? `<a href="${
|
|
18696
|
+
const events = report.events.map((event) => `<article class="${escapeHtml(event.severity)}">
|
|
18697
|
+
<span>${escapeHtml(event.severity.toUpperCase())} / ${escapeHtml(event.category)}</span>
|
|
18698
|
+
<h2>${escapeHtml(event.label)}</h2>
|
|
18699
|
+
<p>${escapeHtml(new Date(event.at).toLocaleString())}${event.sessionId ? ` \xB7 session ${escapeHtml(event.sessionId)}` : ""}</p>
|
|
18700
|
+
${event.value === undefined ? "" : `<strong>${escapeHtml(String(event.value))}</strong>`}
|
|
18701
|
+
${event.detail ? `<p>${escapeHtml(event.detail)}</p>` : ""}
|
|
18702
|
+
<div>${event.href ? `<a href="${escapeHtml(event.href)}">Open source</a>` : ""}${event.action?.href ? `<a href="${escapeHtml(event.action.href)}">${escapeHtml(event.action.label)}</a>` : ""}</div>
|
|
18723
18703
|
</article>`).join("");
|
|
18724
18704
|
const actions = report.actions.map((action) => {
|
|
18725
|
-
const label =
|
|
18726
|
-
const detail = action.detail ? `<p>${
|
|
18727
|
-
const href = action.href ? `<a href="${
|
|
18728
|
-
const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${
|
|
18729
|
-
return `<article class="action"><span>${
|
|
18705
|
+
const label = escapeHtml(action.label);
|
|
18706
|
+
const detail = action.detail ? `<p>${escapeHtml(action.detail)}</p>` : "";
|
|
18707
|
+
const href = action.href ? `<a href="${escapeHtml(action.href)}">Open target</a>` : "";
|
|
18708
|
+
const control = action.method === "POST" ? `<button type="button" data-voice-incident-action="${escapeHtml(action.id)}" ${action.disabled ? "disabled" : ""}>${label}</button>` : href;
|
|
18709
|
+
return `<article class="action"><span>${escapeHtml(action.method ?? "GET")}</span><h2>${label}</h2>${detail}<div>${control}${href && action.method === "POST" ? href : ""}</div></article>`;
|
|
18730
18710
|
}).join("");
|
|
18731
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
18711
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">Overall: ${escapeHtml(report.status.toUpperCase())}</p><p class="muted">Generated ${escapeHtml(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>`;
|
|
18732
18712
|
};
|
|
18733
18713
|
var createVoiceIncidentTimelineRoutes = (options) => {
|
|
18734
18714
|
const path = options.path ?? "/api/voice/incident-timeline";
|
|
@@ -18941,7 +18921,6 @@ var createVoiceIncidentTimelineRoutes = (options) => {
|
|
|
18941
18921
|
|
|
18942
18922
|
// src/liveLatency.ts
|
|
18943
18923
|
import { Elysia as Elysia29 } from "elysia";
|
|
18944
|
-
var escapeHtml30 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
18945
18924
|
var percentile = (values, percentileValue) => {
|
|
18946
18925
|
if (values.length === 0) {
|
|
18947
18926
|
return;
|
|
@@ -19008,8 +18987,8 @@ await traceStore.append({
|
|
|
19008
18987
|
sessionId,
|
|
19009
18988
|
type: 'client.live_latency'
|
|
19010
18989
|
});`;
|
|
19011
|
-
const rows = report.recent.map((sample) => `<tr><td>${
|
|
19012
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
18990
|
+
const rows = report.recent.map((sample) => `<tr><td>${escapeHtml(sample.sessionId)}</td><td>${escapeHtml(formatMs3(sample.latencyMs))}</td><td>${escapeHtml(sample.status ?? "unknown")}</td><td>${escapeHtml(new Date(sample.at).toLocaleString())}</td></tr>`).join("");
|
|
18991
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p>Recent real browser speech-to-assistant response measurements from persisted <code>client.live_latency</code> traces.</p><p class="status ${escapeHtml(report.status)}">Status: ${escapeHtml(report.status)}</p><section class="metrics"><article><span>p50</span><strong>${escapeHtml(formatMs3(report.p50LatencyMs))}</strong></article><article><span>p95</span><strong>${escapeHtml(formatMs3(report.p95LatencyMs))}</strong></article><article><span>Average</span><strong>${escapeHtml(formatMs3(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>${escapeHtml(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>`;
|
|
19013
18992
|
};
|
|
19014
18993
|
var createVoiceLiveLatencyRoutes = (options) => {
|
|
19015
18994
|
const path = options.path ?? "/api/live-latency";
|
|
@@ -19376,13 +19355,7 @@ import {
|
|
|
19376
19355
|
summarizeMediaQualityReport as summarizeMediaQualityReport2,
|
|
19377
19356
|
summarizeMediaTransportReport as summarizeMediaTransportReport2
|
|
19378
19357
|
} from "@absolutejs/media";
|
|
19379
|
-
var
|
|
19380
|
-
var statusRank3 = {
|
|
19381
|
-
pass: 0,
|
|
19382
|
-
warn: 1,
|
|
19383
|
-
fail: 2
|
|
19384
|
-
};
|
|
19385
|
-
var worstStatus2 = (statuses) => statuses.reduce((worst, status) => statusRank3[status] > statusRank3[worst] ? status : worst, "pass");
|
|
19358
|
+
var worstStatus2 = (statuses) => worstVoiceStatus(statuses);
|
|
19386
19359
|
var buildVoiceMediaPipelineReport = (options = {}) => {
|
|
19387
19360
|
const frames = options.frames ?? [];
|
|
19388
19361
|
const calibration = buildMediaPipelineCalibrationReport(options);
|
|
@@ -19560,9 +19533,9 @@ var renderVoiceMediaPipelineHTML = (report, title = "Voice Media Pipeline Proof"
|
|
|
19560
19533
|
...report.calibration.issues,
|
|
19561
19534
|
...report.interruption.issues,
|
|
19562
19535
|
...report.quality.issues
|
|
19563
|
-
].map((issue) => `<li class="${
|
|
19564
|
-
const segments = report.vad.segments.map((segment) => `<tr><td>${
|
|
19565
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
19536
|
+
].map((issue) => `<li class="${escapeHtml(issue.severity)}"><strong>${escapeHtml(issue.code)}</strong>: ${escapeHtml(issue.message)}</li>`).join("");
|
|
19537
|
+
const segments = report.vad.segments.map((segment) => `<tr><td>${escapeHtml(segment.segmentId)}</td><td>${escapeHtml(segment.frameCount)}</td><td>${escapeHtml(segment.durationMs ?? "n/a")}</td><td>${escapeHtml(segment.turnId ?? "n/a")}</td></tr>`).join("");
|
|
19538
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1100px;padding:32px}.hero,.card{background:#17201d;border:1px solid #2e3d36;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:.1em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}.metric{background:#101814;border:1px solid #2e3d36;border-radius:18px;padding:14px}.metric span{color:#a8b5ad;display:block;font-size:.78rem;text-transform:uppercase}.metric strong{display:block;font-size:1.65rem;margin-top:5px}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:7px 11px}.pass{color:#86efac}.warn,.warning{color:#fde68a}.fail,.error{color:#fecaca}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3d36;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Native media pipeline</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">${escapeHtml(report.status)}</p><p>${escapeHtml(report.surface)}</p><section class="summary"><div class="metric"><span>Frames</span><strong>${String(report.frames)}</strong></div><div class="metric"><span>Input audio</span><strong>${String(report.calibration.inputAudioFrames)}</strong></div><div class="metric"><span>Assistant audio</span><strong>${String(report.calibration.assistantAudioFrames)}</strong></div><div class="metric"><span>Trace linked</span><strong>${String(report.calibration.traceLinkedFrames)}</strong></div><div class="metric"><span>First audio</span><strong>${escapeHtml(report.calibration.firstAudioLatencyMs ?? "n/a")}ms</strong></div><div class="metric"><span>VAD segments</span><strong>${String(report.vad.segments.length)}</strong></div><div class="metric"><span>Media quality</span><strong>${escapeHtml(report.quality.status)}</strong></div><div class="metric"><span>Media gaps</span><strong>${String(report.quality.gapCount)}</strong></div><div class="metric"><span>Media jitter</span><strong>${escapeHtml(report.quality.jitterMs ?? "n/a")}ms</strong></div><div class="metric"><span>Speech ratio</span><strong>${String(report.quality.speechRatio)}</strong></div><div class="metric"><span>Timestamp drift</span><strong>${escapeHtml(report.quality.timestampDriftMs ?? "n/a")}ms</strong></div><div class="metric"><span>Interruptions</span><strong>${String(report.interruption.interruptionFrames)}</strong></div><div class="metric"><span>Processor graph</span><strong>${String(report.processorGraph?.nodes.length ?? 0)} nodes</strong></div><div class="metric"><span>Graph out/drop</span><strong>${String(report.processorGraph?.emittedFrames ?? 0)}/${String(report.processorGraph?.droppedFrames ?? 0)}</strong></div><div class="metric"><span>Resampling</span><strong>${report.calibration.resamplingRequired ? "required" : "not required"}</strong></div><div class="metric"><span>Transport</span><strong>${escapeHtml(report.transport?.state ?? "n/a")}</strong></div><div class="metric"><span>Transport in/out</span><strong>${String(report.transport?.inputFrames ?? 0)}/${String(report.transport?.outputFrames ?? 0)}</strong></div><div class="metric"><span>Backpressure</span><strong>${String(report.transport?.backpressureEvents ?? 0)}</strong></div></section></section><section class="card"><h2>Issues</h2><ul>${issues || '<li class="pass">No media pipeline issues.</li>'}</ul></section><section class="card"><h2>VAD Segments</h2><table><thead><tr><th>Segment</th><th>Frames</th><th>Duration ms</th><th>Turn</th></tr></thead><tbody>${segments || '<tr><td colspan="4">No VAD segments.</td></tr>'}</tbody></table></section></main></body></html>`;
|
|
19566
19539
|
};
|
|
19567
19540
|
var createVoiceMediaPipelineRoutes = (options = {}) => {
|
|
19568
19541
|
const path = options.path ?? "/api/voice/media-pipeline";
|
|
@@ -21759,7 +21732,6 @@ import { Elysia as Elysia44 } from "elysia";
|
|
|
21759
21732
|
|
|
21760
21733
|
// src/resilienceRoutes.ts
|
|
21761
21734
|
import { Elysia as Elysia34 } from "elysia";
|
|
21762
|
-
var escapeHtml32 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
21763
21735
|
var getString14 = (value) => typeof value === "string" ? value : undefined;
|
|
21764
21736
|
var getNumber8 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
21765
21737
|
var getBoolean3 = (value) => value === true;
|
|
@@ -21907,13 +21879,13 @@ var summarizeRoutingEvents = (events) => {
|
|
|
21907
21879
|
};
|
|
21908
21880
|
var renderProviderCards2 = (title, providers) => {
|
|
21909
21881
|
if (providers.length === 0) {
|
|
21910
|
-
return `<p class="muted">No ${
|
|
21882
|
+
return `<p class="muted">No ${escapeHtml(title)} provider health yet.</p>`;
|
|
21911
21883
|
}
|
|
21912
21884
|
return `<div class="provider-grid">${providers.map((provider) => `
|
|
21913
|
-
<article class="card provider ${
|
|
21885
|
+
<article class="card provider ${escapeHtml(provider.status)}">
|
|
21914
21886
|
<div class="card-header">
|
|
21915
|
-
<strong>${
|
|
21916
|
-
<span>${
|
|
21887
|
+
<strong>${escapeHtml(provider.provider)}</strong>
|
|
21888
|
+
<span>${escapeHtml(provider.status)}${provider.recommended ? " \xB7 recommended" : ""}</span>
|
|
21917
21889
|
</div>
|
|
21918
21890
|
<dl>
|
|
21919
21891
|
<div><dt>Runs</dt><dd>${provider.runCount}</dd></div>
|
|
@@ -21922,7 +21894,7 @@ var renderProviderCards2 = (title, providers) => {
|
|
|
21922
21894
|
<div><dt>Timeouts</dt><dd>${provider.timeoutCount}</dd></div>
|
|
21923
21895
|
<div><dt>Fallbacks</dt><dd>${provider.fallbackCount}</dd></div>
|
|
21924
21896
|
</dl>
|
|
21925
|
-
${provider.lastError ? `<p class="muted">${
|
|
21897
|
+
${provider.lastError ? `<p class="muted">${escapeHtml(provider.lastError)}</p>` : ""}
|
|
21926
21898
|
</article>
|
|
21927
21899
|
`).join("")}</div>`;
|
|
21928
21900
|
};
|
|
@@ -21931,24 +21903,24 @@ var renderTimeline2 = (events) => {
|
|
|
21931
21903
|
return '<p class="muted">No provider routing events yet. Run the app or simulate provider failover.</p>';
|
|
21932
21904
|
}
|
|
21933
21905
|
return `<div class="timeline">${events.slice(0, 40).map((event) => `
|
|
21934
|
-
<article class="card event ${
|
|
21906
|
+
<article class="card event ${escapeHtml(event.status ?? "unknown")}">
|
|
21935
21907
|
<div class="card-header">
|
|
21936
|
-
<strong>${
|
|
21908
|
+
<strong>${escapeHtml(event.kind.toUpperCase())} ${escapeHtml(event.operation ?? "generate")}</strong>
|
|
21937
21909
|
<span>${new Date(event.at).toLocaleString()}</span>
|
|
21938
21910
|
</div>
|
|
21939
21911
|
<p>
|
|
21940
|
-
<span class="pill">${
|
|
21941
|
-
<span class="pill">provider: ${
|
|
21942
|
-
${event.fallbackProvider ? `<span class="pill">fallback: ${
|
|
21912
|
+
<span class="pill">${escapeHtml(event.status ?? "unknown")}</span>
|
|
21913
|
+
<span class="pill">provider: ${escapeHtml(event.provider ?? "unknown")}</span>
|
|
21914
|
+
${event.fallbackProvider ? `<span class="pill">fallback: ${escapeHtml(event.fallbackProvider)}</span>` : ""}
|
|
21943
21915
|
${event.timedOut ? '<span class="pill danger">timed out</span>' : ""}
|
|
21944
21916
|
</p>
|
|
21945
21917
|
<dl>
|
|
21946
21918
|
<div><dt>Attempt</dt><dd>${event.attempt ?? 0}</dd></div>
|
|
21947
21919
|
<div><dt>Elapsed</dt><dd>${event.elapsedMs ?? 0}ms</dd></div>
|
|
21948
21920
|
<div><dt>Budget</dt><dd>${event.latencyBudgetMs ?? 0}ms</dd></div>
|
|
21949
|
-
<div><dt>Session</dt><dd>${
|
|
21921
|
+
<div><dt>Session</dt><dd>${escapeHtml(event.sessionId)}</dd></div>
|
|
21950
21922
|
</dl>
|
|
21951
|
-
${event.error ? `<p class="muted">${
|
|
21923
|
+
${event.error ? `<p class="muted">${escapeHtml(event.error)}</p>` : ""}
|
|
21952
21924
|
</article>
|
|
21953
21925
|
`).join("")}</div>`;
|
|
21954
21926
|
};
|
|
@@ -21958,9 +21930,9 @@ var renderSessionKind = (kind, summary) => {
|
|
|
21958
21930
|
const status = latest?.status ?? "idle";
|
|
21959
21931
|
const fallback = latest?.fallbackProvider && latest.fallbackProvider !== provider ? ` -> ${latest.fallbackProvider}` : "";
|
|
21960
21932
|
return `<div>
|
|
21961
|
-
<dt>${
|
|
21962
|
-
<dd>${
|
|
21963
|
-
<small>${
|
|
21933
|
+
<dt>${escapeHtml(kind.toUpperCase())}</dt>
|
|
21934
|
+
<dd>${escapeHtml(provider)}${escapeHtml(fallback)}</dd>
|
|
21935
|
+
<small>${escapeHtml(status)} \xB7 ${summary.runCount} event${summary.runCount === 1 ? "" : "s"} \xB7 ${summary.errorCount} error${summary.errorCount === 1 ? "" : "s"} \xB7 ${summary.fallbackCount} fallback${summary.fallbackCount === 1 ? "" : "s"}</small>
|
|
21964
21936
|
</div>`;
|
|
21965
21937
|
};
|
|
21966
21938
|
var renderSessionSummaries = (sessions) => {
|
|
@@ -21968,10 +21940,10 @@ var renderSessionSummaries = (sessions) => {
|
|
|
21968
21940
|
return '<p class="muted">No call-level routing summaries yet. Run a voice session or provider simulation.</p>';
|
|
21969
21941
|
}
|
|
21970
21942
|
return `<div class="session-grid">${sessions.slice(0, 12).map((session) => `
|
|
21971
|
-
<article class="card session ${
|
|
21943
|
+
<article class="card session ${escapeHtml(session.status)}">
|
|
21972
21944
|
<div class="card-header">
|
|
21973
|
-
<strong>${
|
|
21974
|
-
<span>${
|
|
21945
|
+
<strong>${escapeHtml(session.sessionId)}</strong>
|
|
21946
|
+
<span>${escapeHtml(session.status)}</span>
|
|
21975
21947
|
</div>
|
|
21976
21948
|
<p>
|
|
21977
21949
|
<span class="pill">${session.eventCount} routing events</span>
|
|
@@ -21998,21 +21970,21 @@ var renderSimulationControls = (kind, simulation) => {
|
|
|
21998
21970
|
const pathPrefix = simulation.pathPrefix ?? `/api/${kind}-simulate`;
|
|
21999
21971
|
const failureProviders = simulation.failureProviders ?? configuredProviders.map(({ provider }) => provider);
|
|
22000
21972
|
const canFail = (provider) => configuredProviders.some((entry) => entry.provider === provider) && (!simulation.fallbackRequiredProvider || configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider));
|
|
22001
|
-
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${
|
|
22002
|
-
<p class="muted">${
|
|
21973
|
+
return `<div class="simulate-panel" data-sim-kind="${kind}" data-sim-prefix="${escapeHtml(pathPrefix)}">
|
|
21974
|
+
<p class="muted">${escapeHtml(simulation.failureMessage ?? `Simulate ${kind.toUpperCase()} provider failure without changing provider credentials.`)}</p>
|
|
22003
21975
|
<div class="simulate-actions">
|
|
22004
|
-
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${
|
|
22005
|
-
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${
|
|
21976
|
+
${failureProviders.map((provider) => `<button type="button" data-provider-fail="${escapeHtml(provider)}"${canFail(provider) ? "" : " disabled"}>Simulate ${escapeHtml(provider)} ${kind.toUpperCase()} failure</button>`).join("")}
|
|
21977
|
+
${configuredProviders.map((provider) => `<button type="button" data-provider-recover="${escapeHtml(provider.provider)}">Mark ${escapeHtml(provider.provider)} recovered</button>`).join("")}
|
|
22006
21978
|
</div>
|
|
22007
|
-
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${
|
|
21979
|
+
${simulation.fallbackRequiredProvider && !configuredProviders.some((entry) => entry.provider === simulation.fallbackRequiredProvider) ? `<p class="muted">${escapeHtml(simulation.fallbackRequiredMessage ?? `Configure ${simulation.fallbackRequiredProvider} to enable fallback simulation.`)}</p>` : ""}
|
|
22008
21980
|
<pre class="simulate-output" hidden></pre>
|
|
22009
21981
|
</div>`;
|
|
22010
21982
|
};
|
|
22011
21983
|
var renderVoiceResilienceHTML = (input) => {
|
|
22012
21984
|
const summary = summarizeRoutingEvents(input.routingEvents);
|
|
22013
|
-
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${
|
|
22014
|
-
const links = input.links?.length ? input.links.map((link) => `<a href="${
|
|
22015
|
-
const snippet =
|
|
21985
|
+
const kindCounts = [...summary.byKind.entries()].map(([kind, count]) => `<span class="pill">${escapeHtml(kind)}: ${String(count)}</span>`).join("");
|
|
21986
|
+
const links = input.links?.length ? input.links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join(" \xB7 ") : "";
|
|
21987
|
+
const snippet = escapeHtml(`const sttSimulator = createVoiceIOProviderFailureSimulator({
|
|
22016
21988
|
kind: 'stt',
|
|
22017
21989
|
providers: ['deepgram', 'assemblyai'],
|
|
22018
21990
|
fallback: ['deepgram', 'assemblyai'],
|
|
@@ -22050,7 +22022,7 @@ app.use(
|
|
|
22050
22022
|
<head>
|
|
22051
22023
|
<meta charset="utf-8" />
|
|
22052
22024
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
22053
|
-
<title>${
|
|
22025
|
+
<title>${escapeHtml(input.title ?? "AbsoluteJS Voice Resilience")}</title>
|
|
22054
22026
|
<style>
|
|
22055
22027
|
:root { color-scheme: dark; }
|
|
22056
22028
|
body { background: radial-gradient(circle at top left, #172554, #09090b 36%, #050505); color: #f4f4f5; font-family: ui-sans-serif, system-ui, sans-serif; margin: 0; padding: 24px; }
|
|
@@ -22326,7 +22298,6 @@ var evaluateVoiceTelephonyContract = (input) => {
|
|
|
22326
22298
|
};
|
|
22327
22299
|
|
|
22328
22300
|
// src/telephony/matrix.ts
|
|
22329
|
-
var escapeHtml33 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
22330
22301
|
var labelForProvider = (provider) => provider.split("-").map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
|
|
22331
22302
|
var resolveEntryStatus = (contract, setup, smoke) => {
|
|
22332
22303
|
if (!contract.pass || !setup.ready || smoke?.pass === false) {
|
|
@@ -22387,13 +22358,13 @@ var badgeStyles = {
|
|
|
22387
22358
|
};
|
|
22388
22359
|
var renderVoiceTelephonyCarrierMatrixHTML = (matrix, options = {}) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 1040px; margin: 40px auto; padding: 0 20px; color: #172033;">
|
|
22389
22360
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Carrier matrix</p>
|
|
22390
|
-
<h1 style="font-size: 34px; margin: 0 0 8px;">${
|
|
22361
|
+
<h1 style="font-size: 34px; margin: 0 0 8px;">${escapeHtml(options.title ?? "AbsoluteJS Voice Carrier Matrix")}</h1>
|
|
22391
22362
|
<p style="color:#52606d; margin: 0 0 24px;">${matrix.summary.ready}/${matrix.summary.providers} ready, ${matrix.summary.contractsPassing}/${matrix.summary.providers} contract passing, ${matrix.summary.smokePassing}/${matrix.summary.providers} smoke passing.</p>
|
|
22392
22363
|
<section style="display:grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px;">
|
|
22393
22364
|
${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; border-radius:18px; padding:18px; background:#fff; box-shadow:0 18px 48px rgba(15,23,42,.08);">
|
|
22394
22365
|
<div style="display:flex; justify-content:space-between; gap:12px; align-items:center;">
|
|
22395
|
-
<h2 style="margin:0; font-size:20px;">${
|
|
22396
|
-
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${
|
|
22366
|
+
<h2 style="margin:0; font-size:20px;">${escapeHtml(entry.name)}</h2>
|
|
22367
|
+
<span style="border:1px solid; border-radius:999px; padding:4px 10px; font-size:12px; font-weight:700; ${badgeStyles[entry.status]}">${escapeHtml(entry.status.toUpperCase())}</span>
|
|
22397
22368
|
</div>
|
|
22398
22369
|
<dl style="display:grid; grid-template-columns: 1fr 1fr; gap:8px 12px; margin:16px 0;">
|
|
22399
22370
|
<dt style="color:#64748b;">Setup</dt><dd style="margin:0; font-weight:700;">${entry.ready ? "Ready" : "Needs attention"}</dd>
|
|
@@ -22401,9 +22372,9 @@ ${matrix.entries.map((entry) => `<article style="border:1px solid #d9e2ec; borde
|
|
|
22401
22372
|
<dt style="color:#64748b;">Smoke</dt><dd style="margin:0; font-weight:700;">${entry.smoke ? entry.smoke.pass ? "Pass" : "Fail" : "Missing"}</dd>
|
|
22402
22373
|
<dt style="color:#64748b;">Contract</dt><dd style="margin:0; font-weight:700;">${entry.contract.pass ? "Pass" : "Fail"}</dd>
|
|
22403
22374
|
</dl>
|
|
22404
|
-
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${
|
|
22405
|
-
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${
|
|
22406
|
-
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${
|
|
22375
|
+
<p style="margin:0 0 8px; color:#475569;"><strong>Stream:</strong> <code>${escapeHtml(entry.setup.urls.stream || "missing")}</code></p>
|
|
22376
|
+
<p style="margin:0 0 12px; color:#475569;"><strong>Webhook:</strong> <code>${escapeHtml(entry.setup.urls.webhook || "missing")}</code></p>
|
|
22377
|
+
${entry.issues.length ? `<ul style="margin:12px 0 0; padding-left:18px;">${entry.issues.map((issue) => `<li>${escapeHtml(issue.severity)}: ${escapeHtml(issue.message)}</li>`).join("")}</ul>` : '<p style="margin:12px 0 0; color:#166534;">No contract issues.</p>'}
|
|
22407
22378
|
</article>`).join("")}
|
|
22408
22379
|
</section>
|
|
22409
22380
|
</main>`;
|
|
@@ -23791,7 +23762,6 @@ var resolveTwilioStreamParameters = async (parameters, input) => {
|
|
|
23791
23762
|
return parameters;
|
|
23792
23763
|
};
|
|
23793
23764
|
var joinUrlPath = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
23794
|
-
var escapeHtml34 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
23795
23765
|
var getWebhookVerificationUrl = (webhook, input) => {
|
|
23796
23766
|
if (!webhook?.verificationUrl) {
|
|
23797
23767
|
return;
|
|
@@ -23836,23 +23806,23 @@ var buildTwilioVoiceSetupStatus = async (options, input) => {
|
|
|
23836
23806
|
};
|
|
23837
23807
|
var renderTwilioVoiceSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
23838
23808
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio setup</p>
|
|
23839
|
-
<h1>${
|
|
23809
|
+
<h1>${escapeHtml(title)}</h1>
|
|
23840
23810
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
23841
23811
|
<section>
|
|
23842
23812
|
<h2>URLs</h2>
|
|
23843
23813
|
<ul>
|
|
23844
|
-
<li><strong>TwiML:</strong> <code>${
|
|
23845
|
-
<li><strong>Media stream:</strong> <code>${
|
|
23846
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
23814
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml(status.urls.twiml)}</code></li>
|
|
23815
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml(status.urls.stream)}</code></li>
|
|
23816
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml(status.urls.webhook)}</code></li>
|
|
23847
23817
|
</ul>
|
|
23848
23818
|
</section>
|
|
23849
23819
|
<section>
|
|
23850
23820
|
<h2>Signing</h2>
|
|
23851
23821
|
<p>Mode: <code>${status.signing.mode}</code></p>
|
|
23852
|
-
${status.signing.verificationUrl ? `<p>Verification URL: <code>${
|
|
23822
|
+
${status.signing.verificationUrl ? `<p>Verification URL: <code>${escapeHtml(status.signing.verificationUrl)}</code></p>` : ""}
|
|
23853
23823
|
</section>
|
|
23854
|
-
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
23855
|
-
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
23824
|
+
${status.missing.length ? `<section><h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml(name)}</code></li>`).join("")}</ul></section>` : ""}
|
|
23825
|
+
${status.warnings.length ? `<section><h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join("")}</ul></section>` : ""}
|
|
23856
23826
|
</main>`;
|
|
23857
23827
|
var extractTwilioStreamUrl = (twiml) => twiml.match(/<Stream\b[^>]*\surl="([^"]+)"/i)?.[1]?.replaceAll("&", "&");
|
|
23858
23828
|
var createSmokeCheck = (name, status, message, details) => ({
|
|
@@ -23863,20 +23833,20 @@ var createSmokeCheck = (name, status, message, details) => ({
|
|
|
23863
23833
|
});
|
|
23864
23834
|
var renderTwilioVoiceSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
23865
23835
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Twilio smoke test</p>
|
|
23866
|
-
<h1>${
|
|
23836
|
+
<h1>${escapeHtml(title)}</h1>
|
|
23867
23837
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
23868
23838
|
<section>
|
|
23869
23839
|
<h2>Checks</h2>
|
|
23870
23840
|
<ul>
|
|
23871
|
-
${report.checks.map((check) => `<li><strong>${
|
|
23841
|
+
${report.checks.map((check) => `<li><strong>${escapeHtml(check.name)}</strong>: ${escapeHtml(check.status)}${check.message ? ` - ${escapeHtml(check.message)}` : ""}</li>`).join("")}
|
|
23872
23842
|
</ul>
|
|
23873
23843
|
</section>
|
|
23874
23844
|
<section>
|
|
23875
23845
|
<h2>Observed URLs</h2>
|
|
23876
23846
|
<ul>
|
|
23877
|
-
<li><strong>TwiML:</strong> <code>${
|
|
23878
|
-
<li><strong>Stream:</strong> <code>${
|
|
23879
|
-
<li><strong>Webhook:</strong> <code>${
|
|
23847
|
+
<li><strong>TwiML:</strong> <code>${escapeHtml(report.setup.urls.twiml)}</code></li>
|
|
23848
|
+
<li><strong>Stream:</strong> <code>${escapeHtml(report.twiml?.streamUrl ?? report.setup.urls.stream)}</code></li>
|
|
23849
|
+
<li><strong>Webhook:</strong> <code>${escapeHtml(report.setup.urls.webhook)}</code></li>
|
|
23880
23850
|
</ul>
|
|
23881
23851
|
</section>
|
|
23882
23852
|
</main>`;
|
|
@@ -24559,7 +24529,6 @@ var createTwilioVoiceRoutes = (options) => {
|
|
|
24559
24529
|
|
|
24560
24530
|
// src/telephony/plivo.ts
|
|
24561
24531
|
var escapeXml3 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
24562
|
-
var escapeHtml35 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
24563
24532
|
var joinUrlPath2 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
24564
24533
|
var resolveRequestOrigin2 = (request) => {
|
|
24565
24534
|
const url = new URL(request.url);
|
|
@@ -24990,21 +24959,21 @@ var buildPlivoVoiceSetupStatus = async (options, input) => {
|
|
|
24990
24959
|
};
|
|
24991
24960
|
var renderPlivoSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
24992
24961
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo setup</p>
|
|
24993
|
-
<h1>${
|
|
24962
|
+
<h1>${escapeHtml(title)}</h1>
|
|
24994
24963
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
24995
24964
|
<ul>
|
|
24996
|
-
<li><strong>Answer XML:</strong> <code>${
|
|
24997
|
-
<li><strong>Audio stream:</strong> <code>${
|
|
24998
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
24965
|
+
<li><strong>Answer XML:</strong> <code>${escapeHtml(status.urls.answer)}</code></li>
|
|
24966
|
+
<li><strong>Audio stream:</strong> <code>${escapeHtml(status.urls.stream)}</code></li>
|
|
24967
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml(status.urls.webhook)}</code></li>
|
|
24999
24968
|
</ul>
|
|
25000
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
25001
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
24969
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml(name)}</code></li>`).join("")}</ul>` : ""}
|
|
24970
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join("")}</ul>` : ""}
|
|
25002
24971
|
</main>`;
|
|
25003
24972
|
var renderPlivoSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
25004
24973
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Plivo smoke test</p>
|
|
25005
|
-
<h1>${
|
|
24974
|
+
<h1>${escapeHtml(title)}</h1>
|
|
25006
24975
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
25007
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
24976
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml(check.name)}</strong>: ${escapeHtml(check.status)}${check.message ? ` - ${escapeHtml(check.message)}` : ""}</li>`).join("")}</ul>
|
|
25008
24977
|
</main>`;
|
|
25009
24978
|
var runPlivoSmokeTest = async (input) => {
|
|
25010
24979
|
const setup = await buildPlivoVoiceSetupStatus(input.options, input);
|
|
@@ -25208,7 +25177,6 @@ import { Buffer as Buffer5 } from "buffer";
|
|
|
25208
25177
|
import { Database as Database4 } from "bun:sqlite";
|
|
25209
25178
|
import { Elysia as Elysia39 } from "elysia";
|
|
25210
25179
|
var escapeXml4 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
25211
|
-
var escapeHtml36 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
25212
25180
|
var joinUrlPath3 = (origin, path) => `${origin.replace(/\/$/, "")}${path.startsWith("/") ? path : `/${path}`}`;
|
|
25213
25181
|
var resolveRequestOrigin3 = (request) => {
|
|
25214
25182
|
const url = new URL(request.url);
|
|
@@ -25602,21 +25570,21 @@ var buildTelnyxVoiceSetupStatus = async (options, input) => {
|
|
|
25602
25570
|
};
|
|
25603
25571
|
var renderTelnyxSetupHTML = (status, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
25604
25572
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx setup</p>
|
|
25605
|
-
<h1>${
|
|
25573
|
+
<h1>${escapeHtml(title)}</h1>
|
|
25606
25574
|
<p><strong>Status:</strong> ${status.ready ? "Ready" : "Needs attention"}</p>
|
|
25607
25575
|
<ul>
|
|
25608
|
-
<li><strong>TeXML:</strong> <code>${
|
|
25609
|
-
<li><strong>Media stream:</strong> <code>${
|
|
25610
|
-
<li><strong>Status webhook:</strong> <code>${
|
|
25576
|
+
<li><strong>TeXML:</strong> <code>${escapeHtml(status.urls.texml)}</code></li>
|
|
25577
|
+
<li><strong>Media stream:</strong> <code>${escapeHtml(status.urls.stream)}</code></li>
|
|
25578
|
+
<li><strong>Status webhook:</strong> <code>${escapeHtml(status.urls.webhook)}</code></li>
|
|
25611
25579
|
</ul>
|
|
25612
|
-
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${
|
|
25613
|
-
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${
|
|
25580
|
+
${status.missing.length ? `<h2>Missing env</h2><ul>${status.missing.map((name) => `<li><code>${escapeHtml(name)}</code></li>`).join("")}</ul>` : ""}
|
|
25581
|
+
${status.warnings.length ? `<h2>Warnings</h2><ul>${status.warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join("")}</ul>` : ""}
|
|
25614
25582
|
</main>`;
|
|
25615
25583
|
var renderTelnyxSmokeHTML = (report, title) => `<main style="font-family: ui-sans-serif, system-ui; max-width: 860px; margin: 40px auto; padding: 0 20px;">
|
|
25616
25584
|
<p style="letter-spacing: .12em; text-transform: uppercase; color: #52606d;">Telnyx smoke test</p>
|
|
25617
|
-
<h1>${
|
|
25585
|
+
<h1>${escapeHtml(title)}</h1>
|
|
25618
25586
|
<p><strong>Status:</strong> ${report.pass ? "Pass" : "Fail"}</p>
|
|
25619
|
-
<ul>${report.checks.map((check) => `<li><strong>${
|
|
25587
|
+
<ul>${report.checks.map((check) => `<li><strong>${escapeHtml(check.name)}</strong>: ${escapeHtml(check.status)}${check.message ? ` - ${escapeHtml(check.message)}` : ""}</li>`).join("")}</ul>
|
|
25620
25588
|
</main>`;
|
|
25621
25589
|
var runTelnyxSmokeTest = async (input) => {
|
|
25622
25590
|
const setup = await buildTelnyxVoiceSetupStatus(input.options, input);
|
|
@@ -26110,12 +26078,11 @@ var defaultThresholds = {
|
|
|
26110
26078
|
}
|
|
26111
26079
|
};
|
|
26112
26080
|
var providerKinds = ["llm", "stt", "tts"];
|
|
26113
|
-
var
|
|
26081
|
+
var statusRank3 = {
|
|
26114
26082
|
pass: 0,
|
|
26115
26083
|
warn: 1,
|
|
26116
26084
|
fail: 2
|
|
26117
26085
|
};
|
|
26118
|
-
var escapeHtml37 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
26119
26086
|
var roundMetric3 = (value) => Math.round(value * 1e4) / 1e4;
|
|
26120
26087
|
var rate4 = (count, total) => count / Math.max(1, total);
|
|
26121
26088
|
var uniqueSorted5 = (values) => [
|
|
@@ -26307,7 +26274,7 @@ var evaluateVoiceProviderSloEvidence = (report, input = {}) => {
|
|
|
26307
26274
|
const timeouts = kindReports.reduce((total, kind) => total + kind.timeouts, 0);
|
|
26308
26275
|
const unresolvedErrors = kindReports.reduce((total, kind) => total + kind.unresolvedErrors, 0);
|
|
26309
26276
|
const maxStatus2 = input.maxStatus ?? "pass";
|
|
26310
|
-
if (
|
|
26277
|
+
if (statusRank3[report.status] > statusRank3[maxStatus2]) {
|
|
26311
26278
|
issues.push(`Expected provider SLO status at most ${maxStatus2}, found ${report.status}.`);
|
|
26312
26279
|
}
|
|
26313
26280
|
if (input.minEvents !== undefined && report.events < input.minEvents) {
|
|
@@ -26411,11 +26378,11 @@ var renderVoiceProviderSloHTML = (report, options = {}) => {
|
|
|
26411
26378
|
const title = options.title ?? "AbsoluteJS Voice Provider SLOs";
|
|
26412
26379
|
const kindCards = providerKinds.map((kind) => {
|
|
26413
26380
|
const kindReport = report.kinds[kind];
|
|
26414
|
-
const metrics = Object.values(kindReport.metrics).map((metric) => `<div><dt>${
|
|
26381
|
+
const metrics = Object.values(kindReport.metrics).map((metric) => `<div><dt>${escapeHtml(metric.label)}</dt><dd>${escapeHtml(formatMetricValue2(metric))}</dd><small>budget ${escapeHtml(formatMetricThreshold(metric))}</small></div>`).join("");
|
|
26415
26382
|
const providers = kindReport.providers.length ? kindReport.providers.join(", ") : "none recorded";
|
|
26416
|
-
return `<article class="${
|
|
26383
|
+
return `<article class="${escapeHtml(kindReport.status)}"><h2>${kind.toUpperCase()} <span>${escapeHtml(kindReport.status)}</span></h2><p>${kindReport.events} routing event(s), ${kindReport.eventsWithLatency} latency sample(s), providers: ${escapeHtml(providers)}.</p><dl>${metrics}</dl></article>`;
|
|
26417
26384
|
}).join("");
|
|
26418
|
-
const issues = report.issues.length > 0 ? `<ul>${report.issues.map((issue) => `<li class="${
|
|
26385
|
+
const issues = report.issues.length > 0 ? `<ul>${report.issues.map((issue) => `<li class="${escapeHtml(issue.status)}"><strong>${escapeHtml(issue.kind ? `${issue.kind.toUpperCase()} ${issue.label}` : issue.label)}</strong><span>${escapeHtml(issue.detail ?? "")}</span></li>`).join("")}</ul>` : "<p>No provider SLO issues.</p>";
|
|
26419
26386
|
const snippet = `createVoiceProviderSloRoutes({
|
|
26420
26387
|
store: runtimeStorage.traces,
|
|
26421
26388
|
requiredKinds: ['llm', 'stt', 'tts'],
|
|
@@ -26425,7 +26392,7 @@ var renderVoiceProviderSloHTML = (report, options = {}) => {
|
|
|
26425
26392
|
tts: { maxAverageElapsedMs: 1200, maxP95ElapsedMs: 2200 }
|
|
26426
26393
|
}
|
|
26427
26394
|
})`;
|
|
26428
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
26395
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101318;color:#f8f4e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1180px;padding:32px}.hero,article,.primitive{background:#171b22;border:1px solid #2c3340;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(14,165,233,.2),rgba(245,158,11,.12))}.eyebrow{color:#7dd3fc;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.9rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.status,article h2 span{border:1px solid #475569;border-radius:999px;display:inline-flex;font-size:.85rem;padding:6px 10px}.pass{border-color:rgba(34,197,94,.65)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}.grid{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(280px,1fr))}dl{display:grid;gap:10px;grid-template-columns:repeat(auto-fit,minmax(150px,1fr))}dt{color:#cbd5e1;font-size:.78rem;text-transform:uppercase}dd{font-size:1.7rem;font-weight:900;margin:0}small{color:#a8b3c2}ul{display:grid;gap:10px;list-style:none;padding:0}li{background:#101318;border:1px solid #2c3340;border-radius:16px;padding:12px}li span{color:#cbd5e1;display:block;margin-top:4px}.primitive{background:#11161d}.primitive code{color:#bae6fd}.primitive pre{background:#070b10;border:1px solid #243041;border-radius:16px;color:#e0f2fe;overflow:auto;padding:16px}</style></head><body><main><section class="hero"><p class="eyebrow">Provider latency and fallback proof</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">${escapeHtml(report.status)}</p><p>${report.events} provider routing event(s), ${report.eventsWithLatency} latency sample(s).</p></section><section class="grid">${kindCards}</section><section class="primitive"><p class="eyebrow">Copy into your app</p><h2><code>createVoiceProviderSloRoutes(...)</code> turns provider speed into release evidence</h2><p>Pair this report with production readiness so LLM/STT/TTS latency, timeout, fallback, and unresolved error regressions block deploys.</p><pre><code>${escapeHtml(snippet)}</code></pre></section><section><h2>Issues</h2>${issues}</section></main></body></html>`;
|
|
26429
26396
|
};
|
|
26430
26397
|
var createVoiceProviderSloRoutes = (options) => {
|
|
26431
26398
|
const path = options.path ?? "/api/voice/provider-slos";
|
|
@@ -26470,7 +26437,6 @@ var createVoiceProviderSloRoutes = (options) => {
|
|
|
26470
26437
|
|
|
26471
26438
|
// src/sessionObservability.ts
|
|
26472
26439
|
import { Elysia as Elysia42 } from "elysia";
|
|
26473
|
-
var escapeHtml38 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
26474
26440
|
var formatMs4 = (value) => value === undefined ? "n/a" : `${String(value)}ms`;
|
|
26475
26441
|
var resolveHref = (href, sessionId) => {
|
|
26476
26442
|
if (href === false) {
|
|
@@ -26613,8 +26579,8 @@ var buildVoiceSessionObservabilityReport = async (options) => {
|
|
|
26613
26579
|
turns: buildTurnWaterfalls(record)
|
|
26614
26580
|
};
|
|
26615
26581
|
};
|
|
26616
|
-
var renderLinks = (links) => links.length === 0 ? "" : `<div class="actions">${links.map((link) => `<a href="${
|
|
26617
|
-
var renderTurns = (turns) => turns.length === 0 ? '<p class="muted">No turn-level events recorded yet.</p>' : turns.map((turn) => `<article class="turn"><header><strong>${
|
|
26582
|
+
var renderLinks = (links) => links.length === 0 ? "" : `<div class="actions">${links.map((link) => `<a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a>`).join("")}</div>`;
|
|
26583
|
+
var renderTurns = (turns) => turns.length === 0 ? '<p class="muted">No turn-level events recorded yet.</p>' : turns.map((turn) => `<article class="turn"><header><strong>${escapeHtml(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>${escapeHtml(stage.type)}</td><td>${escapeHtml(stage.label)}</td><td>${escapeHtml(stage.provider ?? "")}</td><td>${escapeHtml(stage.status ?? "")}</td><td>${formatMs4(stage.elapsedMs)}</td></tr>`).join("")}</tbody></table></article>`).join("");
|
|
26618
26584
|
var renderVoiceSessionObservabilityMarkdown = (report) => `# Voice session observability: ${report.sessionId}
|
|
26619
26585
|
|
|
26620
26586
|
Status: ${report.status}
|
|
@@ -26655,7 +26621,7 @@ ${turn.stages.map((stage) => `- +${stage.offsetMs}ms ${stage.type}: ${stage.labe
|
|
|
26655
26621
|
## Incident Handoff
|
|
26656
26622
|
|
|
26657
26623
|
${report.incidentMarkdown}`;
|
|
26658
|
-
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>${
|
|
26624
|
+
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>${escapeHtml(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>${escapeHtml(report.sessionId)}</h1><p class="status ${escapeHtml(report.status)}">${escapeHtml(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>${escapeHtml(report.incidentMarkdown)}</code></pre></section></main></body></html>`;
|
|
26659
26625
|
var routeSessionId = (params) => typeof params.sessionId === "string" ? params.sessionId : "";
|
|
26660
26626
|
var linkByRel = (links) => {
|
|
26661
26627
|
const map = new Map;
|
|
@@ -27103,7 +27069,6 @@ None.
|
|
|
27103
27069
|
};
|
|
27104
27070
|
|
|
27105
27071
|
// src/opsRecovery.ts
|
|
27106
|
-
var escapeHtml39 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
27107
27072
|
var getString16 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
27108
27073
|
var hrefForSession = (value, sessionId) => {
|
|
27109
27074
|
if (typeof value === "function") {
|
|
@@ -27119,7 +27084,7 @@ var hrefForSession = (value, sessionId) => {
|
|
|
27119
27084
|
return value;
|
|
27120
27085
|
};
|
|
27121
27086
|
var operationsRecordHrefForSession = (links, sessionId) => hrefForSession(links?.operationsRecords, sessionId);
|
|
27122
|
-
var rollupStatus2 = (issues) => issues.
|
|
27087
|
+
var rollupStatus2 = (issues) => worstVoiceStatus(issues.map((issue) => issue.severity));
|
|
27123
27088
|
var providerUnresolved = (provider) => provider.status === "degraded" || provider.status === "rate-limited" || provider.status === "suppressed";
|
|
27124
27089
|
var collectFailedSessions = (events, limit, links) => events.filter((event) => {
|
|
27125
27090
|
if (event.type !== "session.error") {
|
|
@@ -27317,13 +27282,13 @@ ${failedSessions || "None."}
|
|
|
27317
27282
|
${report.latency ? renderVoiceLatencySLOMarkdown(report.latency, { title: "Latency SLO" }) : "Latency SLO disabled."}
|
|
27318
27283
|
`;
|
|
27319
27284
|
};
|
|
27320
|
-
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${
|
|
27285
|
+
var renderDeliverySummary = (label, summary) => summary ? `<article><span>${escapeHtml(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>${escapeHtml(label)}</span><strong>not configured</strong></article>`;
|
|
27321
27286
|
var renderVoiceOpsRecoveryHTML = (report, options = {}) => {
|
|
27322
27287
|
const title = options.title ?? "Voice Ops Recovery";
|
|
27323
|
-
const issues = report.issues.map((issue) => `<tr><td>${
|
|
27324
|
-
const providers = report.providers.providers.map((provider) => `<tr><td>${
|
|
27325
|
-
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${
|
|
27326
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
27288
|
+
const issues = report.issues.map((issue) => `<tr><td>${escapeHtml(issue.severity)}</td><td><code>${escapeHtml(issue.code)}</code></td><td>${issue.href ? `<a href="${escapeHtml(issue.href)}">${escapeHtml(issue.label)}</a>` : escapeHtml(issue.label)}</td><td>${escapeHtml(String(issue.value ?? ""))}</td><td>${escapeHtml(issue.detail ?? "")}</td></tr>`).join("");
|
|
27289
|
+
const providers = report.providers.providers.map((provider) => `<tr><td>${escapeHtml(provider.provider)}</td><td>${escapeHtml(provider.status)}</td><td>${String(provider.runCount)}</td><td>${String(provider.errorCount)}</td><td>${String(provider.fallbackCount)}</td><td>${escapeHtml(provider.lastError ?? "")}</td></tr>`).join("");
|
|
27290
|
+
const failedSessions = report.failedSessions.map((session) => `<li>${session.operationsRecordHref ? `<a href="${escapeHtml(session.operationsRecordHref)}">${escapeHtml(session.sessionId)}</a>` : escapeHtml(session.sessionId)}${session.provider ? ` via ${escapeHtml(session.provider)}` : ""}${session.error ? `: ${escapeHtml(session.error)}` : ""}</li>`).join("");
|
|
27291
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p><span class="status">${escapeHtml(report.status)}</span> Checked ${escapeHtml(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>${escapeHtml(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>`;
|
|
27327
27292
|
};
|
|
27328
27293
|
var createVoiceOpsRecoveryRoutes = (options = {}) => {
|
|
27329
27294
|
const path = options.path ?? "/api/voice/ops-recovery";
|
|
@@ -27389,7 +27354,6 @@ var buildVoiceReadinessRecoveryActions = (input, options = {}) => {
|
|
|
27389
27354
|
sourceChecks: sourceChecks.length
|
|
27390
27355
|
};
|
|
27391
27356
|
};
|
|
27392
|
-
var escapeHtml40 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
27393
27357
|
var formatVoiceProofFreshnessDuration = (valueMs) => {
|
|
27394
27358
|
if (valueMs < 1000) {
|
|
27395
27359
|
return `${Math.max(0, Math.round(valueMs))}ms`;
|
|
@@ -27560,7 +27524,7 @@ var createVoiceProductionReadinessProofRuntime = (options = {}) => {
|
|
|
27560
27524
|
store
|
|
27561
27525
|
};
|
|
27562
27526
|
};
|
|
27563
|
-
var rollupStatus3 = (checks) => checks.
|
|
27527
|
+
var rollupStatus3 = (checks) => worstVoiceStatus(checks.map((check) => check.status));
|
|
27564
27528
|
var readinessGateCodes = {
|
|
27565
27529
|
"Agent squad contracts": "voice.readiness.agent_squad_contracts",
|
|
27566
27530
|
"Audit evidence": "voice.readiness.audit_evidence",
|
|
@@ -29482,25 +29446,25 @@ var buildVoiceProductionReadinessReport = async (options, input = {}) => {
|
|
|
29482
29446
|
var buildVoiceProductionReadinessGate = async (options, input = {}) => summarizeVoiceProductionReadinessGate(await buildVoiceProductionReadinessReport(options, input), options.gate || undefined);
|
|
29483
29447
|
var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
29484
29448
|
const title = options.title ?? "AbsoluteJS Voice Production Readiness";
|
|
29485
|
-
const thresholdLink = report.links.sloReadinessThresholds ? `<p><a href="${
|
|
29486
|
-
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${
|
|
29449
|
+
const thresholdLink = report.links.sloReadinessThresholds ? `<p><a href="${escapeHtml(report.links.sloReadinessThresholds)}">Open Calibration -> Active Readiness Gate</a> to inspect the thresholds currently driving calibrated provider, latency, interruption, reconnect, and monitoring gates.</p>` : "";
|
|
29450
|
+
const profile = report.profile ? `<section class="profile"><p class="eyebrow">Readiness profile</p><h2>${escapeHtml(report.profile.name)}</h2><p>${escapeHtml(report.profile.description)}</p><p>${escapeHtml(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="${escapeHtml(surface.href)}">${escapeHtml(surface.label)}</a>` : escapeHtml(surface.label)}</strong></article>`).join("")}</div></section>` : "";
|
|
29487
29451
|
const checks = report.checks.map((check, index) => {
|
|
29488
|
-
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${
|
|
29489
|
-
const explanation = check.gateExplanation ? `<p class="gate-explanation">Why this gate is ${
|
|
29490
|
-
return `<article class="check ${
|
|
29452
|
+
const actions = (check.actions ?? []).map((action) => action.method === "POST" ? `<button type="button" data-readiness-action="${index}" data-action-url="${escapeHtml(action.href)}">${escapeHtml(action.label)}</button>` : `<a href="${escapeHtml(action.href)}">${escapeHtml(action.label)}</a>`).join("");
|
|
29453
|
+
const explanation = check.gateExplanation ? `<p class="gate-explanation">Why this gate is ${escapeHtml(check.status)}: observed ${escapeHtml(String(check.gateExplanation.observed ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml(check.gateExplanation.unit)}` : ""}; threshold ${escapeHtml(String(check.gateExplanation.threshold ?? "n/a"))}${check.gateExplanation.unit ? ` ${escapeHtml(check.gateExplanation.unit)}` : ""}. ${escapeHtml(check.gateExplanation.remediation)} ${check.gateExplanation.sourceHref ? `<a href="${escapeHtml(check.gateExplanation.sourceHref)}">Open threshold source</a>` : ""}</p>` : "";
|
|
29454
|
+
return `<article class="check ${escapeHtml(check.status)}">
|
|
29491
29455
|
<div>
|
|
29492
|
-
<span>${
|
|
29493
|
-
<h2>${
|
|
29494
|
-
${check.detail ? `<p>${
|
|
29456
|
+
<span>${escapeHtml(check.status.toUpperCase())}</span>
|
|
29457
|
+
<h2>${escapeHtml(check.label)}</h2>
|
|
29458
|
+
${check.detail ? `<p>${escapeHtml(check.detail)}</p>` : ""}
|
|
29495
29459
|
${explanation}
|
|
29496
|
-
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${
|
|
29460
|
+
${check.proofSource ? `<p class="proof-source">Proof source: ${check.proofSource.href ? `<a href="${escapeHtml(check.proofSource.href)}">${escapeHtml(check.proofSource.sourceLabel)}</a>` : escapeHtml(check.proofSource.sourceLabel)}${check.proofSource.detail ? ` \xB7 ${escapeHtml(check.proofSource.detail)}` : ""}</p>` : ""}
|
|
29497
29461
|
${actions ? `<p class="actions">${actions}</p>` : ""}
|
|
29498
29462
|
</div>
|
|
29499
|
-
<strong>${
|
|
29500
|
-
${check.href ? `<a href="${
|
|
29463
|
+
<strong>${escapeHtml(String(check.value ?? check.status))}</strong>
|
|
29464
|
+
${check.href ? `<a href="${escapeHtml(check.href)}">Open surface</a>` : ""}
|
|
29501
29465
|
</article>`;
|
|
29502
29466
|
}).join("");
|
|
29503
|
-
const snippet =
|
|
29467
|
+
const snippet = escapeHtml(`createVoiceProductionReadinessRoutes({
|
|
29504
29468
|
htmlPath: '/production-readiness',
|
|
29505
29469
|
path: '/api/production-readiness',
|
|
29506
29470
|
gatePath: '/api/production-readiness/gate',
|
|
@@ -29516,7 +29480,7 @@ var renderVoiceProductionReadinessHTML = (report, options = {}) => {
|
|
|
29516
29480
|
providerRoutingContracts: loadProviderRoutingContracts,
|
|
29517
29481
|
store: traceStore
|
|
29518
29482
|
});`);
|
|
29519
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29483
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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 ${escapeHtml(report.status)}">Overall: ${escapeHtml(report.status.toUpperCase())}</p><p>Checked ${escapeHtml(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>`;
|
|
29520
29484
|
};
|
|
29521
29485
|
var createVoiceProductionReadinessRoutes = (options) => {
|
|
29522
29486
|
const path = options.path ?? "/api/production-readiness";
|
|
@@ -29594,10 +29558,9 @@ var createVoiceProductionReadinessRoutes = (options) => {
|
|
|
29594
29558
|
};
|
|
29595
29559
|
|
|
29596
29560
|
// src/operationalStatus.ts
|
|
29597
|
-
var escapeHtml41 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
29598
29561
|
var resolveValue2 = async (value) => typeof value === "function" ? await value() : value;
|
|
29599
29562
|
var isDeliveryRuntime = (value) => Boolean(value && typeof value === "object" && "isRunning" in value && "summarize" in value);
|
|
29600
|
-
var worstStatus3 = (statuses) =>
|
|
29563
|
+
var worstStatus3 = (statuses) => worstVoiceStatus(statuses);
|
|
29601
29564
|
var proofPackStatusToCheck = (status, href) => {
|
|
29602
29565
|
const checkStatus = status.state === "failed" || status.state === "missing" ? "fail" : status.state === "fresh" ? "pass" : "warn";
|
|
29603
29566
|
const age = typeof status.ageMs === "number" ? `${Math.round(status.ageMs / 1000)}s old` : undefined;
|
|
@@ -29664,14 +29627,14 @@ var buildVoiceOperationalStatusReport = async (options) => {
|
|
|
29664
29627
|
};
|
|
29665
29628
|
var renderVoiceOperationalStatusHTML = (report, options = {}) => {
|
|
29666
29629
|
const title = options.title ?? "AbsoluteJS Voice Operational Status";
|
|
29667
|
-
const checks = report.checks.map((check) => `<article class="${
|
|
29668
|
-
<span>${
|
|
29669
|
-
<h2>${
|
|
29670
|
-
<strong>${
|
|
29671
|
-
${check.detail ? `<p>${
|
|
29672
|
-
${check.href ? `<a href="${
|
|
29630
|
+
const checks = report.checks.map((check) => `<article class="${escapeHtml(check.status)}">
|
|
29631
|
+
<span>${escapeHtml(check.status.toUpperCase())}</span>
|
|
29632
|
+
<h2>${escapeHtml(check.label)}</h2>
|
|
29633
|
+
<strong>${escapeHtml(String(check.value ?? check.status))}</strong>
|
|
29634
|
+
${check.detail ? `<p>${escapeHtml(check.detail)}</p>` : ""}
|
|
29635
|
+
${check.href ? `<a href="${escapeHtml(check.href)}">Open surface</a>` : ""}
|
|
29673
29636
|
</article>`).join("");
|
|
29674
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29637
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">Overall: ${escapeHtml(report.status.toUpperCase())}</p><p>${String(report.summary.pass)}/${String(report.summary.total)} checks passing. Checked ${escapeHtml(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>`;
|
|
29675
29638
|
};
|
|
29676
29639
|
var createVoiceOperationalStatusRoutes = (options) => {
|
|
29677
29640
|
const path = options.path ?? "/api/voice/operational-status";
|
|
@@ -29739,7 +29702,6 @@ var DEFAULT_LINKS = [
|
|
|
29739
29702
|
label: "Handoffs"
|
|
29740
29703
|
}
|
|
29741
29704
|
];
|
|
29742
|
-
var escapeHtml42 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
29743
29705
|
var countProviderStatuses = (providers) => {
|
|
29744
29706
|
const degradedStatuses = new Set(["degraded", "rate-limited", "suppressed"]);
|
|
29745
29707
|
const healthy = providers.filter((provider) => provider.status === "healthy").length;
|
|
@@ -29808,16 +29770,16 @@ var buildVoiceOpsConsoleReport = async (options) => {
|
|
|
29808
29770
|
trace
|
|
29809
29771
|
};
|
|
29810
29772
|
};
|
|
29811
|
-
var renderMetricCard = (input) => `<article class="metric"><span>${
|
|
29773
|
+
var renderMetricCard = (input) => `<article class="metric"><span>${escapeHtml(input.label)}</span><strong>${escapeHtml(String(input.value))}</strong>${input.status ? `<p class="${escapeHtml(input.status)}">${escapeHtml(input.status)}</p>` : ""}${input.href ? `<a href="${escapeHtml(input.href)}">Open</a>` : ""}</article>`;
|
|
29812
29774
|
var renderVoiceOpsConsoleHTML = (report, options = {}) => {
|
|
29813
29775
|
const links = report.links.map((link) => `<article class="surface">
|
|
29814
|
-
<div><h2>${
|
|
29815
|
-
<p><a href="${
|
|
29776
|
+
<div><h2>${escapeHtml(link.label)}</h2>${link.description ? `<p>${escapeHtml(link.description)}</p>` : ""}</div>
|
|
29777
|
+
<p><a href="${escapeHtml(link.href)}">Open ${escapeHtml(link.label)}</a>${link.statusHref ? ` \xB7 <a href="${escapeHtml(link.statusHref)}">Status</a>` : ""}</p>
|
|
29816
29778
|
</article>`).join("");
|
|
29817
|
-
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${
|
|
29818
|
-
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${
|
|
29779
|
+
const sessions = report.recentSessions.length ? report.recentSessions.map((session) => `<tr><td>${escapeHtml(session.sessionId)}</td><td>${escapeHtml(session.status)}</td><td>${session.turnCount}</td><td>${session.errorCount}</td><td>${session.replayHref ? `<a href="${escapeHtml(session.replayHref)}">Replay</a>` : ""}</td></tr>`).join("") : '<tr><td colspan="5">No sessions yet.</td></tr>';
|
|
29780
|
+
const routing = report.recentRoutingEvents.length ? report.recentRoutingEvents.map((event) => `<tr><td>${escapeHtml(event.kind)}</td><td>${escapeHtml(event.provider ?? "unknown")}</td><td>${escapeHtml(event.status ?? "unknown")}</td><td>${event.elapsedMs ?? 0}ms</td><td>${escapeHtml(event.sessionId)}</td></tr>`).join("") : '<tr><td colspan="5">No provider routing events yet.</td></tr>';
|
|
29819
29781
|
const title = options.title ?? "AbsoluteJS Voice Ops Console";
|
|
29820
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29782
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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 ${escapeHtml(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>`;
|
|
29821
29783
|
};
|
|
29822
29784
|
var createVoiceOpsConsoleRoutes = (options) => {
|
|
29823
29785
|
const path = options.path ?? "/ops-console";
|
|
@@ -30014,14 +29976,13 @@ var summarizeVoiceOpsStatus = async (options) => {
|
|
|
30014
29976
|
};
|
|
30015
29977
|
|
|
30016
29978
|
// src/opsStatusRoutes.ts
|
|
30017
|
-
var escapeHtml43 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30018
29979
|
var renderVoiceOpsStatusHTML = (report, options = {}) => {
|
|
30019
29980
|
const title = options.title ?? "AbsoluteJS Voice Ops Status";
|
|
30020
29981
|
const surfaces = Object.entries(report.surfaces).map(([key, surface]) => {
|
|
30021
29982
|
const value = "recovered" in surface ? surface.total === 0 ? "0 events" : `${surface.recovered}/${surface.total}` : ("auditTotal" in surface) ? `${surface.auditTotal + surface.traceTotal} deliveries` : ("total" in surface) ? `${Math.max(surface.total - ("failed" in surface ? surface.failed : ("degraded" in surface) ? surface.degraded : 0), 0)}/${surface.total}` : surface.status;
|
|
30022
|
-
return `<article class="surface ${
|
|
29983
|
+
return `<article class="surface ${escapeHtml(surface.status)}"><span>${escapeHtml(surface.status.toUpperCase())}</span><h2>${escapeHtml(key)}</h2><strong>${escapeHtml(value)}</strong></article>`;
|
|
30023
29984
|
}).join("");
|
|
30024
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
29985
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0d141b;color:#f8f3e7;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero{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}.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;font-weight:900;padding:8px 12px}.surfaces{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(180px,1fr))}.surface{background:#151d26;border:1px solid #283544;border-radius:20px;padding:18px}.surface span{color:#aab5c0;font-size:.78rem;font-weight:900;letter-spacing:.08em}.surface strong{font-size:1.5rem}.pass{border-color:rgba(34,197,94,.55)}.fail{border-color:rgba(239,68,68,.75)}a{color:#5eead4}</style></head><body><main><section class="hero"><p class="eyebrow">Ops status</p><h1>${escapeHtml(title)}</h1><p>Compact pass/fail status for framework widgets, demos, and small customer-facing health badges.</p><p class="status ${escapeHtml(report.status)}">Overall: ${escapeHtml(report.status.toUpperCase())}</p><p>${report.passed}/${report.total} checks passing</p></section><section class="surfaces">${surfaces || '<article class="surface pass"><span>PASS</span><h2>No checks configured</h2><strong>0/0</strong></article>'}</section></main></body></html>`;
|
|
30025
29986
|
};
|
|
30026
29987
|
var createVoiceOpsStatusRoutes = (options) => {
|
|
30027
29988
|
const path = options.path ?? "/api/voice/ops-status";
|
|
@@ -30207,7 +30168,6 @@ var createVoiceOpsWebhookReceiverRoutes = (options = {}) => {
|
|
|
30207
30168
|
|
|
30208
30169
|
// src/outcomeContract.ts
|
|
30209
30170
|
import { Elysia as Elysia49 } from "elysia";
|
|
30210
|
-
var escapeHtml44 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30211
30171
|
var resolveSessionHref4 = (value, sessionId) => {
|
|
30212
30172
|
if (value === false) {
|
|
30213
30173
|
return;
|
|
@@ -30418,13 +30378,13 @@ var assertVoiceOutcomeContractEvidence = (report, input = {}) => {
|
|
|
30418
30378
|
var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
30419
30379
|
const title = options.title ?? "Voice Outcome Contracts";
|
|
30420
30380
|
const contracts = report.contracts.map((contract) => {
|
|
30421
|
-
const sessionLinks = contract.operationsRecordHrefs.length ? `<p>${contract.operationsRecordHrefs.map((href, index) => `<a href="${
|
|
30381
|
+
const sessionLinks = contract.operationsRecordHrefs.length ? `<p>${contract.operationsRecordHrefs.map((href, index) => `<a href="${escapeHtml(href)}">${escapeHtml(contract.sessionIds[index] ?? href)}</a>`).join(" \xB7 ")}</p>` : "";
|
|
30422
30382
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
30423
30383
|
<div class="contract-header">
|
|
30424
30384
|
<div>
|
|
30425
|
-
<p class="eyebrow">${
|
|
30426
|
-
<h2>${
|
|
30427
|
-
${contract.description ? `<p>${
|
|
30385
|
+
<p class="eyebrow">${escapeHtml(contract.contractId)}</p>
|
|
30386
|
+
<h2>${escapeHtml(contract.label ?? contract.contractId)}</h2>
|
|
30387
|
+
${contract.description ? `<p>${escapeHtml(contract.description)}</p>` : ""}
|
|
30428
30388
|
${sessionLinks}
|
|
30429
30389
|
</div>
|
|
30430
30390
|
<strong>${contract.pass ? "pass" : "fail"}</strong>
|
|
@@ -30436,10 +30396,10 @@ var renderVoiceOutcomeContractHTML = (report, options = {}) => {
|
|
|
30436
30396
|
<span>handoffs ${String(contract.matched.handoffs)}</span>
|
|
30437
30397
|
<span>events ${String(contract.matched.integrationEvents)}</span>
|
|
30438
30398
|
</div>
|
|
30439
|
-
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${
|
|
30399
|
+
${contract.issues.length ? `<ul>${contract.issues.map((issue) => `<li>${escapeHtml(issue.message)}</li>`).join("")}</ul>` : ""}
|
|
30440
30400
|
</section>`;
|
|
30441
30401
|
}).join("");
|
|
30442
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30402
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
30443
30403
|
};
|
|
30444
30404
|
var createVoiceOutcomeContractJSONHandler = (options) => async () => runVoiceOutcomeContractSuite(options);
|
|
30445
30405
|
var createVoiceOutcomeContractHTMLHandler = (options) => async () => {
|
|
@@ -30473,7 +30433,6 @@ var defaultRequirements = [
|
|
|
30473
30433
|
"lifecycle-outcome",
|
|
30474
30434
|
"no-session-error"
|
|
30475
30435
|
];
|
|
30476
|
-
var escapeHtml45 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
30477
30436
|
var payloadType = (event) => typeof event.payload.type === "string" ? event.payload.type : undefined;
|
|
30478
30437
|
var hasTextPayload = (event) => ["text", "assistantText", "transcript"].some((key) => {
|
|
30479
30438
|
const value = event.payload[key];
|
|
@@ -30582,10 +30541,10 @@ var resolveHandlerOptions = async (options, input) => ({
|
|
|
30582
30541
|
});
|
|
30583
30542
|
var renderVoicePhoneAgentProductionSmokeHTML = (report, options = {}) => {
|
|
30584
30543
|
const title = options.title ?? "AbsoluteJS Voice Phone Smoke Contract";
|
|
30585
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
30586
|
-
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${
|
|
30587
|
-
const requirements = report.required.map((requirement) => `<span class="pill">${
|
|
30588
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
30544
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml(issue.requirement)}</strong>: ${escapeHtml(issue.message)}</li>`).join("");
|
|
30545
|
+
const outcomes = report.observed.lifecycleOutcomes.map((outcome) => `<span class="pill">${escapeHtml(outcome)}</span>`).join("");
|
|
30546
|
+
const requirements = report.required.map((requirement) => `<span class="pill">${escapeHtml(requirement)}</span>`).join("");
|
|
30547
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p class="status ${report.pass ? "pass" : "fail"}">${report.pass ? "PASS" : "FAIL"}</p><p>Contract <code>${escapeHtml(report.contractId)}</code>${report.provider ? ` for <code>${escapeHtml(report.provider)}</code>` : ""}${report.sessionId ? ` on session <code>${escapeHtml(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>`;
|
|
30589
30548
|
};
|
|
30590
30549
|
var createVoicePhoneAgentProductionSmokeJSONHandler = (options) => async ({
|
|
30591
30550
|
query,
|
|
@@ -31508,7 +31467,6 @@ import { Elysia as Elysia55 } from "elysia";
|
|
|
31508
31467
|
|
|
31509
31468
|
// src/providerDecisionTraces.ts
|
|
31510
31469
|
import { Elysia as Elysia54 } from "elysia";
|
|
31511
|
-
var escapeHtml46 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
31512
31470
|
var getString18 = (value) => typeof value === "string" ? value : undefined;
|
|
31513
31471
|
var getNumber11 = (value) => typeof value === "number" && Number.isFinite(value) ? value : undefined;
|
|
31514
31472
|
var isDecisionTrace = (event) => Boolean(event && typeof event === "object" && "provider" in event && "reason" in event && "sessionId" in event && "status" in event && "surface" in event);
|
|
@@ -31523,8 +31481,7 @@ var surfaceForKind = (kind) => {
|
|
|
31523
31481
|
return "live-call";
|
|
31524
31482
|
}
|
|
31525
31483
|
};
|
|
31526
|
-
var
|
|
31527
|
-
var reportStatus = (issues) => issues.reduce((status, issue) => statusRank5[issue.status] > statusRank5[status] ? issue.status : status, "pass");
|
|
31484
|
+
var reportStatus = (issues) => worstVoiceStatus(issues.map((issue) => issue.status));
|
|
31528
31485
|
var uniqueSorted6 = (values) => [
|
|
31529
31486
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
31530
31487
|
].sort();
|
|
@@ -31749,7 +31706,7 @@ var renderVoiceProviderDecisionTraceHTML = (report, title = "Provider Decision T
|
|
|
31749
31706
|
<head>
|
|
31750
31707
|
<meta charset="utf-8" />
|
|
31751
31708
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
31752
|
-
<title>${
|
|
31709
|
+
<title>${escapeHtml(title)}</title>
|
|
31753
31710
|
<style>
|
|
31754
31711
|
body{font-family:ui-sans-serif,system-ui,sans-serif;margin:0;background:#f8fafc;color:#0f172a}
|
|
31755
31712
|
main{max-width:1100px;margin:0 auto;padding:32px}
|
|
@@ -31763,8 +31720,8 @@ code{background:#e2e8f0;border-radius:8px;padding:2px 6px}
|
|
|
31763
31720
|
</head>
|
|
31764
31721
|
<body>
|
|
31765
31722
|
<main>
|
|
31766
|
-
<p class="status ${report.status}">${
|
|
31767
|
-
<h1>${
|
|
31723
|
+
<p class="status ${report.status}">${escapeHtml(report.status)}</p>
|
|
31724
|
+
<h1>${escapeHtml(title)}</h1>
|
|
31768
31725
|
<p class="muted">Runtime proof for why providers were selected, skipped, failed, or recovered by fallback.</p>
|
|
31769
31726
|
<section class="grid">
|
|
31770
31727
|
<article class="card"><strong>${String(report.summary.decisions)}</strong><p>decisions</p></article>
|
|
@@ -31775,10 +31732,10 @@ code{background:#e2e8f0;border-radius:8px;padding:2px 6px}
|
|
|
31775
31732
|
</section>
|
|
31776
31733
|
<section class="surfaces">
|
|
31777
31734
|
${report.surfaces.map((surface) => `<article class="surface">
|
|
31778
|
-
<header><strong>${
|
|
31735
|
+
<header><strong>${escapeHtml(surface.surface)}</strong> <span class="status ${surface.status}">${escapeHtml(surface.status)}</span></header>
|
|
31779
31736
|
<p>${String(surface.decisions)} decision(s), ${String(surface.fallbacks)} fallback(s), ${String(surface.degraded)} degraded decision(s), ${String(surface.errors)} error(s).</p>
|
|
31780
|
-
<p class="muted">Providers: ${
|
|
31781
|
-
<p>${surface.reasons.map((reason) => `<code>${
|
|
31737
|
+
<p class="muted">Providers: ${escapeHtml(surface.providers.join(", ") || "none")}</p>
|
|
31738
|
+
<p>${surface.reasons.map((reason) => `<code>${escapeHtml(reason)}</code>`).join(" ")}</p>
|
|
31782
31739
|
</article>`).join(`
|
|
31783
31740
|
`)}
|
|
31784
31741
|
</section>
|
|
@@ -34189,7 +34146,6 @@ var buildVoiceProofTrendRecommendationReport = (report, options = {}) => {
|
|
|
34189
34146
|
}
|
|
34190
34147
|
};
|
|
34191
34148
|
};
|
|
34192
|
-
var escapeHtml47 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34193
34149
|
var escapeMarkdown2 = (value) => value.replaceAll("|", "\\|");
|
|
34194
34150
|
var renderVoiceProofTrendRecommendationMarkdown = (report, title = "Voice Provider Runtime Recommendations") => [
|
|
34195
34151
|
`# ${title}`,
|
|
@@ -34224,11 +34180,11 @@ var renderVoiceProofTrendRecommendationMarkdown = (report, title = "Voice Provid
|
|
|
34224
34180
|
].join(`
|
|
34225
34181
|
`);
|
|
34226
34182
|
var renderVoiceProofTrendRecommendationHTML = (report, title = "Voice Provider Runtime Recommendations") => {
|
|
34227
|
-
const cards = report.recommendations.map((recommendation) => `<article class="${
|
|
34228
|
-
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${
|
|
34229
|
-
const providerRows = report.providers.length === 0 ? "<li>No provider-specific samples were present.</li>" : report.providers.map((provider) => `<li><strong>#${String(provider.rank)} ${
|
|
34230
|
-
const profileRows = report.profiles.length === 0 ? "<li>No benchmark profiles were present.</li>" : report.profiles.map((profile) => `<li><strong>${
|
|
34231
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
34183
|
+
const cards = report.recommendations.map((recommendation) => `<article class="${escapeHtml(recommendation.status)}"><p class="eyebrow">${escapeHtml(recommendation.surface)} \xB7 ${escapeHtml(recommendation.status)}</p><h2>${escapeHtml(recommendation.recommendation)}</h2><p>${escapeHtml(recommendation.nextMove)}</p><pre>${escapeHtml(JSON.stringify(recommendation.evidence, null, 2))}</pre></article>`).join("");
|
|
34184
|
+
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("");
|
|
34185
|
+
const providerRows = report.providers.length === 0 ? "<li>No provider-specific samples were present.</li>" : report.providers.map((provider) => `<li><strong>#${String(provider.rank)} ${escapeHtml(provider.label ?? provider.id)}</strong><span>${escapeHtml(provider.role ?? "provider")} \xB7 ${escapeHtml(provider.status)} \xB7 p95 ${escapeHtml(provider.p95Ms ?? "n/a")}ms \xB7 ${escapeHtml(provider.samples ?? "n/a")} sample(s)</span><small>${escapeHtml(provider.nextMove)}</small></li>`).join("");
|
|
34186
|
+
const profileRows = report.profiles.length === 0 ? "<li>No benchmark profiles were present.</li>" : report.profiles.map((profile) => `<li><strong>${escapeHtml(profile.label ?? profile.id)}</strong><span>${escapeHtml(profile.status)} \xB7 ${escapeHtml(formatProviderMix(profile.bestProviders))}</span><small>${escapeHtml(profile.nextMove)}</small></li>`).join("");
|
|
34187
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article{background:#17201d;border:1px solid #2e3d36;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:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #42534a;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}pre{background:#0b1110;border-radius:14px;overflow:auto;padding:12px}a{color:#5eead4}li{margin:.45rem 0}li span,li small{display:block;color:#c9d3ca}</style></head><body><main><section class="hero"><p class="eyebrow">Sustained proof recommendations</p><h1>${escapeHtml(title)}</h1><p>Generated ${escapeHtml(report.generatedAt)} from ${escapeHtml(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml(report.status)}</span><span class="pill">Provider ${report.summary.keepCurrentProviderPath ? "keep" : "change"}</span><span class="pill">Best mix ${escapeHtml(formatProviderMix(report.bestProviders))}</span><span class="pill">Profiles ${String(report.profiles.length)}</span><span class="pill">Runtime ${report.summary.keepCurrentRuntimeChannel ? "keep" : "tune"}</span><span class="pill">${String(report.summary.recommendedActions)} action(s)</span></div></section>${cards}<section class="hero"><h2>Benchmark Profiles</h2><ul>${profileRows}</ul></section><section class="hero"><h2>Provider Comparison</h2><ul>${providerRows}</ul></section><section class="hero"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
|
|
34232
34188
|
};
|
|
34233
34189
|
var renderVoiceRealCallProfileHistoryMarkdown = (report, title = "Voice Real-Call Profile History") => [
|
|
34234
34190
|
`# ${title}`,
|
|
@@ -34262,11 +34218,11 @@ var renderVoiceRealCallProfileHistoryMarkdown = (report, title = "Voice Real-Cal
|
|
|
34262
34218
|
].join(`
|
|
34263
34219
|
`);
|
|
34264
34220
|
var renderVoiceRealCallProfileHistoryHTML = (report, title = "Voice Real-Call Profile History") => {
|
|
34265
|
-
const profileRows = report.summary.profiles?.length ? report.summary.profiles.map((profile) => `<tr><td>${
|
|
34266
|
-
const defaultRows = report.defaults.profiles.length > 0 ? report.defaults.profiles.map((profile) => `<tr><td>${
|
|
34267
|
-
const recommendations = report.recommendations.recommendations.map((recommendation) => `<article class="${
|
|
34268
|
-
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${
|
|
34269
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
34221
|
+
const profileRows = report.summary.profiles?.length ? report.summary.profiles.map((profile) => `<tr><td>${escapeHtml(profile.label ?? profile.id)}</td><td>${escapeHtml(profile.status ?? "unknown")}</td><td>${escapeHtml(profile.maxLiveP95Ms ?? "n/a")}</td><td>${escapeHtml(profile.maxProviderP95Ms ?? "n/a")}</td><td>${escapeHtml(profile.maxTurnP95Ms ?? "n/a")}</td><td>${escapeHtml(formatProviderMix(profile.providers ?? []))}</td></tr>`).join("") : '<tr><td colspan="6">No profiles present.</td></tr>';
|
|
34222
|
+
const defaultRows = report.defaults.profiles.length > 0 ? report.defaults.profiles.map((profile) => `<tr><td>${escapeHtml(profile.label ?? profile.profileId)}</td><td>${escapeHtml(profile.status)}</td><td>${escapeHtml(Object.entries(profile.providerRoutes).map(([role, provider]) => `${role}: ${provider}`).join(", ") || "n/a")}</td><td>${escapeHtml(profile.latencyBudgets.maxLiveP95Ms ?? "n/a")}</td><td>${escapeHtml(profile.latencyBudgets.maxProviderP95Ms ?? "n/a")}</td><td>${escapeHtml(profile.latencyBudgets.maxTurnP95Ms ?? "n/a")}</td></tr>`).join("") : '<tr><td colspan="6">No actionable defaults present.</td></tr>';
|
|
34223
|
+
const recommendations = report.recommendations.recommendations.map((recommendation) => `<article class="${escapeHtml(recommendation.status)}"><p class="eyebrow">${escapeHtml(recommendation.surface)} \xB7 ${escapeHtml(recommendation.status)}</p><h2>${escapeHtml(recommendation.recommendation)}</h2><p>${escapeHtml(recommendation.nextMove)}</p></article>`).join("");
|
|
34224
|
+
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("");
|
|
34225
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#111510;color:#f6f0dd;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,article,.card{background:#182117;border:1px solid #32412d;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(132,204,22,.16),rgba(20,184,166,.12))}.eyebrow{color:#bef264;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.7rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #52624b;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail{border-color:rgba(239,68,68,.75)}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #32412d;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Real-call benchmark history</p><h1>${escapeHtml(title)}</h1><p>Generated ${escapeHtml(report.generatedAt)} from ${escapeHtml(report.source)}.</p><div class="summary"><span class="pill">Status ${escapeHtml(report.status)}</span><span class="pill">Reports ${String(report.reports)}</span><span class="pill">Profiles ${String(report.summary.profileCount)}</span><span class="pill">Defaults ${String(report.defaults.summary.actionableProfiles)}/${String(report.defaults.summary.profileCount)}</span><span class="pill">Cycles ${String(report.summary.cycles ?? 0)}</span><span class="pill">Best mix ${escapeHtml(formatProviderMix(report.recommendations.bestProviders))}</span></div></section><section class="card"><h2>Profiles</h2><table><thead><tr><th>Profile</th><th>Status</th><th>Live p95</th><th>Provider p95</th><th>Turn p95</th><th>Provider mix</th></tr></thead><tbody>${profileRows}</tbody></table></section><section class="card"><h2>Actionable Defaults</h2><table><thead><tr><th>Profile</th><th>Status</th><th>Provider routes</th><th>Live budget</th><th>Provider budget</th><th>Turn budget</th></tr></thead><tbody>${defaultRows}</tbody></table></section>${recommendations}<section class="card"><h2>Issues</h2><ul>${issues}</ul></section></main></body></html>`;
|
|
34270
34226
|
};
|
|
34271
34227
|
var renderVoiceRealCallEvidenceRuntimeMarkdown = (report, title = "Voice Real-Call Evidence Runtime") => [
|
|
34272
34228
|
`# ${title}`,
|
|
@@ -34289,9 +34245,9 @@ var renderVoiceRealCallEvidenceRuntimeMarkdown = (report, title = "Voice Real-Ca
|
|
|
34289
34245
|
].join(`
|
|
34290
34246
|
`);
|
|
34291
34247
|
var renderVoiceRealCallEvidenceRuntimeHTML = (report, title = "Voice Real-Call Evidence Runtime") => {
|
|
34292
|
-
const issueItems = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${
|
|
34293
|
-
const profileRows = report.history.summary.profiles?.length ? report.history.summary.profiles.map((profile) => `<tr><td>${
|
|
34294
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
34248
|
+
const issueItems = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("");
|
|
34249
|
+
const profileRows = report.history.summary.profiles?.length ? report.history.summary.profiles.map((profile) => `<tr><td>${escapeHtml(profile.label ?? profile.id)}</td><td>${escapeHtml(profile.status ?? "unknown")}</td><td>${escapeHtml(profile.sessionCount ?? "n/a")}</td><td>${escapeHtml(profile.maxLiveP95Ms ?? "n/a")}</td><td>${escapeHtml(profile.maxProviderP95Ms ?? "n/a")}</td><td>${escapeHtml(formatProviderMix(profile.providers ?? []))}</td></tr>`).join("") : '<tr><td colspan="6">No profile history has been collected yet.</td></tr>';
|
|
34250
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0f1618;color:#f2f7f2;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,.card{background:#162225;border:1px solid #2f4548;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(132,204,22,.1))}.eyebrow{color:#99f6e4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.1rem,6vw,4.3rem);letter-spacing:-.06em;line-height:.94;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{border:1px solid #4b6669;border-radius:999px;padding:8px 12px}.pass{border-color:rgba(34,197,94,.55)}.warn{border-color:rgba(245,158,11,.7)}.fail,.empty,.stale{border-color:rgba(239,68,68,.75)}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2f4548;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Real-call evidence runtime</p><h1>${escapeHtml(title)}</h1><p>Generated ${escapeHtml(report.generatedAt)} from ${escapeHtml(report.source)}.</p><div class="summary"><span class="pill ${escapeHtml(report.status)}">Status ${escapeHtml(report.status)}</span><span class="pill">Stored ${String(report.summary.storedEvidence)}</span><span class="pill">Collected ${String(report.summary.collectedEvidence)}</span><span class="pill">Appended ${String(report.appended)}</span><span class="pill">Duplicates ${String(report.skippedDuplicates)}</span><span class="pill">Sessions ${String(report.summary.sessions)}</span><span class="pill">Profiles ${String(report.summary.profiles)}</span></div></section><section class="card"><h2>Rolling Profile History</h2><table><thead><tr><th>Profile</th><th>Status</th><th>Sessions</th><th>Live p95</th><th>Provider p95</th><th>Provider mix</th></tr></thead><tbody>${profileRows}</tbody></table></section><section class="card"><h2>Issues</h2><ul>${issueItems}</ul></section></main></body></html>`;
|
|
34295
34251
|
};
|
|
34296
34252
|
var createVoiceProofTrendRecommendationRoutes = (options) => {
|
|
34297
34253
|
const path = options.path ?? "/api/voice/proof-trend-recommendations";
|
|
@@ -34644,7 +34600,6 @@ var formatVoiceProofTrendAge = (ageMs) => {
|
|
|
34644
34600
|
|
|
34645
34601
|
// src/providerCapabilities.ts
|
|
34646
34602
|
import { Elysia as Elysia56 } from "elysia";
|
|
34647
|
-
var escapeHtml48 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34648
34603
|
var fromProviderList = (kind, providers, options) => (providers ?? []).map((provider) => ({
|
|
34649
34604
|
configured: true,
|
|
34650
34605
|
features: options.features?.[provider],
|
|
@@ -34709,27 +34664,27 @@ var summarizeVoiceProviderCapabilities = async (options) => {
|
|
|
34709
34664
|
var renderVoiceProviderCapabilityHTML = (report, options = {}) => {
|
|
34710
34665
|
const title = options.title ?? "Voice Provider Capabilities";
|
|
34711
34666
|
const cards = report.capabilities.map((capability) => {
|
|
34712
|
-
const features = (capability.features ?? []).map((feature) => `<span class="pill">${
|
|
34713
|
-
return `<article class="card ${
|
|
34667
|
+
const features = (capability.features ?? []).map((feature) => `<span class="pill">${escapeHtml(feature)}</span>`).join("");
|
|
34668
|
+
return `<article class="card ${escapeHtml(capability.status)}">
|
|
34714
34669
|
<div class="card-header">
|
|
34715
34670
|
<div>
|
|
34716
|
-
<p class="eyebrow">${
|
|
34717
|
-
<h2>${
|
|
34671
|
+
<p class="eyebrow">${escapeHtml(capability.kind)}</p>
|
|
34672
|
+
<h2>${escapeHtml(capability.label ?? capability.provider)}</h2>
|
|
34718
34673
|
</div>
|
|
34719
|
-
<strong>${
|
|
34674
|
+
<strong>${escapeHtml(capability.status)}</strong>
|
|
34720
34675
|
</div>
|
|
34721
|
-
${capability.description ? `<p>${
|
|
34676
|
+
${capability.description ? `<p>${escapeHtml(capability.description)}</p>` : ""}
|
|
34722
34677
|
<dl>
|
|
34723
34678
|
<div><dt>Configured</dt><dd>${capability.configured ? "yes" : "no"}</dd></div>
|
|
34724
34679
|
<div><dt>Selected</dt><dd>${capability.selected ? "yes" : "no"}</dd></div>
|
|
34725
|
-
<div><dt>Model</dt><dd>${
|
|
34680
|
+
<div><dt>Model</dt><dd>${escapeHtml(capability.model ?? "default")}</dd></div>
|
|
34726
34681
|
<div><dt>Runs</dt><dd>${String(capability.health?.runCount ?? 0)}</dd></div>
|
|
34727
34682
|
<div><dt>Errors</dt><dd>${String(capability.health?.errorCount ?? 0)}</dd></div>
|
|
34728
34683
|
</dl>
|
|
34729
34684
|
${features ? `<div class="features">${features}</div>` : ""}
|
|
34730
34685
|
</article>`;
|
|
34731
34686
|
}).join("");
|
|
34732
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
34687
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
34733
34688
|
};
|
|
34734
34689
|
var createVoiceProviderCapabilityJSONHandler = (options) => async () => summarizeVoiceProviderCapabilities(options);
|
|
34735
34690
|
var createVoiceProviderCapabilityHTMLHandler = (options) => async () => {
|
|
@@ -34764,12 +34719,11 @@ var defaultRequirement = {
|
|
|
34764
34719
|
requireFallback: false,
|
|
34765
34720
|
requireTimeoutBudget: false
|
|
34766
34721
|
};
|
|
34767
|
-
var
|
|
34722
|
+
var statusRank4 = {
|
|
34768
34723
|
pass: 0,
|
|
34769
34724
|
warn: 1,
|
|
34770
34725
|
fail: 2
|
|
34771
34726
|
};
|
|
34772
|
-
var escapeHtml49 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34773
34727
|
var isProviderList = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
34774
34728
|
var uniqueSorted7 = (values) => [
|
|
34775
34729
|
...new Set(values.filter((value) => typeof value === "string"))
|
|
@@ -34780,7 +34734,7 @@ var surfaceProviderNames = (surface) => uniqueSorted7([
|
|
|
34780
34734
|
...isProviderList(surface.fallback) ? surface.fallback : [],
|
|
34781
34735
|
...isProviderList(surface.allowProviders) ? surface.allowProviders : []
|
|
34782
34736
|
]);
|
|
34783
|
-
var surfaceStatus = (issues) => issues.reduce((status, issue) =>
|
|
34737
|
+
var surfaceStatus = (issues) => issues.reduce((status, issue) => statusRank4[issue.status] > statusRank4[status] ? issue.status : status, "pass");
|
|
34784
34738
|
var resolvedRequirement = (surface, options) => ({
|
|
34785
34739
|
...defaultRequirement,
|
|
34786
34740
|
...options.defaultRequirement ?? {},
|
|
@@ -34912,21 +34866,21 @@ var renderVoiceProviderOrchestrationMarkdown = (report) => {
|
|
|
34912
34866
|
};
|
|
34913
34867
|
var renderVoiceProviderOrchestrationHTML = (report, options = {}) => {
|
|
34914
34868
|
const title = options.title ?? "Voice Provider Orchestration";
|
|
34915
|
-
const cards = report.surfaces.map((surface) => `<article class="card ${
|
|
34916
|
-
<div class="card-header"><div><p class="eyebrow">${
|
|
34869
|
+
const cards = report.surfaces.map((surface) => `<article class="card ${escapeHtml(surface.status)}">
|
|
34870
|
+
<div class="card-header"><div><p class="eyebrow">${escapeHtml(surface.surface)}</p><h2>${escapeHtml(surface.strategy ?? "default policy")}</h2></div><strong>${escapeHtml(surface.status)}</strong></div>
|
|
34917
34871
|
<dl>
|
|
34918
|
-
<div><dt>Providers</dt><dd>${
|
|
34919
|
-
<div><dt>Fallback</dt><dd>${
|
|
34872
|
+
<div><dt>Providers</dt><dd>${escapeHtml(surface.providers.join(", ") || "none")}</dd></div>
|
|
34873
|
+
<div><dt>Fallback</dt><dd>${escapeHtml(surface.fallbackProviders.join(" -> ") || "none")}</dd></div>
|
|
34920
34874
|
<div><dt>Circuit breaker</dt><dd>${surface.circuitBreaker ? "yes" : "no"}</dd></div>
|
|
34921
34875
|
<div><dt>Timeout</dt><dd>${surface.timeoutBudget ? `${String(surface.timeoutMs)}ms` : "none"}</dd></div>
|
|
34922
34876
|
<div><dt>Max cost</dt><dd>${surface.budgetPolicy.maxCost ?? "none"}</dd></div>
|
|
34923
34877
|
<div><dt>Max latency</dt><dd>${surface.budgetPolicy.maxLatencyMs ? `${String(surface.budgetPolicy.maxLatencyMs)}ms` : "none"}</dd></div>
|
|
34924
34878
|
<div><dt>Min quality</dt><dd>${surface.budgetPolicy.minQuality ?? "none"}</dd></div>
|
|
34925
|
-
<div><dt>Fallback mode</dt><dd>${
|
|
34879
|
+
<div><dt>Fallback mode</dt><dd>${escapeHtml(surface.fallbackMode || "default")}</dd></div>
|
|
34926
34880
|
</dl>
|
|
34927
|
-
${surface.issues.length ? `<ul>${surface.issues.map((issue) => `<li><strong>${
|
|
34881
|
+
${surface.issues.length ? `<ul>${surface.issues.map((issue) => `<li><strong>${escapeHtml(issue.status)}</strong> ${escapeHtml(issue.message)}</li>`).join("")}</ul>` : "<p>No orchestration issues.</p>"}
|
|
34928
34882
|
</article>`).join("");
|
|
34929
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
34883
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><div class="summary"><span class="pill">${escapeHtml(report.profileId)}</span><span class="pill">${escapeHtml(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>`;
|
|
34930
34884
|
};
|
|
34931
34885
|
var createVoiceProviderOrchestrationRoutes = (options) => {
|
|
34932
34886
|
const path = options.path ?? "/api/voice/provider-orchestration";
|
|
@@ -34964,7 +34918,6 @@ var createVoiceProviderOrchestrationRoutes = (options) => {
|
|
|
34964
34918
|
|
|
34965
34919
|
// src/providerStackRecommendations.ts
|
|
34966
34920
|
import { Elysia as Elysia58 } from "elysia";
|
|
34967
|
-
var escapeHtml50 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
34968
34921
|
var profileProviderPriorities = {
|
|
34969
34922
|
"meeting-recorder": {
|
|
34970
34923
|
llm: ["openai", "anthropic", "gemini"],
|
|
@@ -35071,13 +35024,13 @@ var recommendVoiceProviderStack = (input) => {
|
|
|
35071
35024
|
stacks
|
|
35072
35025
|
};
|
|
35073
35026
|
};
|
|
35074
|
-
var rollupContractStatus = (checks) => checks.
|
|
35075
|
-
var
|
|
35027
|
+
var rollupContractStatus = (checks) => worstVoiceStatus(checks.map((check) => check.status));
|
|
35028
|
+
var statusRank5 = {
|
|
35076
35029
|
pass: 0,
|
|
35077
35030
|
warn: 1,
|
|
35078
35031
|
fail: 2
|
|
35079
35032
|
};
|
|
35080
|
-
var statusExceeds = (actual, max) =>
|
|
35033
|
+
var statusExceeds = (actual, max) => statusRank5[actual] > statusRank5[max];
|
|
35081
35034
|
var buildVoiceProviderContractMatrix = (input) => {
|
|
35082
35035
|
const rows = input.contracts.map((contract) => {
|
|
35083
35036
|
const configured = contract.configured !== false;
|
|
@@ -35279,17 +35232,17 @@ var resolveProviderContractMatrixInput = async (matrix) => typeof matrix === "fu
|
|
|
35279
35232
|
var renderVoiceProviderContractMatrixHTML = (report, options = {}) => {
|
|
35280
35233
|
const title = options.title ?? "Voice Provider Contract Matrix";
|
|
35281
35234
|
const rows = report.rows.map((row) => {
|
|
35282
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
35283
|
-
return `<article class="row ${
|
|
35235
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml(check.status)}"><strong>${escapeHtml(check.label)}</strong><span>${escapeHtml(check.detail ?? check.status)}</span>${check.remediation ? `<em>${check.remediation.href ? `<a href="${escapeHtml(check.remediation.href)}">${escapeHtml(check.remediation.label)}</a>` : escapeHtml(check.remediation.label)}: ${escapeHtml(check.remediation.detail)}</em>` : ""}</li>`).join("");
|
|
35236
|
+
return `<article class="row ${escapeHtml(row.status)}">
|
|
35284
35237
|
<div>
|
|
35285
|
-
<p class="eyebrow">${
|
|
35286
|
-
<h2>${
|
|
35287
|
-
<p class="status ${
|
|
35238
|
+
<p class="eyebrow">${escapeHtml(row.kind)}${row.selected ? " \xB7 selected" : ""}</p>
|
|
35239
|
+
<h2>${escapeHtml(row.provider)}</h2>
|
|
35240
|
+
<p class="status ${escapeHtml(row.status)}">${escapeHtml(row.status.toUpperCase())}</p>
|
|
35288
35241
|
</div>
|
|
35289
35242
|
<ul>${checks}</ul>
|
|
35290
35243
|
</article>`;
|
|
35291
35244
|
}).join("");
|
|
35292
|
-
const snippet =
|
|
35245
|
+
const snippet = escapeHtml(`const providerContracts = () =>
|
|
35293
35246
|
createVoiceProviderContractMatrixPreset('phone-agent', {
|
|
35294
35247
|
env: process.env,
|
|
35295
35248
|
providers: {
|
|
@@ -35310,7 +35263,7 @@ createVoiceProductionReadinessRoutes({
|
|
|
35310
35263
|
providerContractMatrix: () =>
|
|
35311
35264
|
buildVoiceProviderContractMatrix(providerContracts())
|
|
35312
35265
|
});`);
|
|
35313
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
35266
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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>`;
|
|
35314
35267
|
};
|
|
35315
35268
|
var createVoiceProviderContractMatrixJSONHandler = (matrix) => async () => buildVoiceProviderContractMatrix(await resolveProviderContractMatrixInput(matrix));
|
|
35316
35269
|
var createVoiceProviderContractMatrixHTMLHandler = (options) => async () => {
|
|
@@ -35451,7 +35404,6 @@ var DEFAULT_REALTIME_FORMAT2 = {
|
|
|
35451
35404
|
encoding: "pcm_s16le",
|
|
35452
35405
|
sampleRateHz: 24000
|
|
35453
35406
|
};
|
|
35454
|
-
var escapeHtml51 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
35455
35407
|
var formatLabel = (format) => `${format.container}/${format.encoding}/${String(format.sampleRateHz)}hz/${String(format.channels)}ch`;
|
|
35456
35408
|
var formatMatches = (actual, expected) => actual.container === expected.container && actual.encoding === expected.encoding && actual.sampleRateHz === expected.sampleRateHz && actual.channels === expected.channels;
|
|
35457
35409
|
var validateFormat = (label, actual, expected, issues) => {
|
|
@@ -35705,9 +35657,9 @@ var renderVoiceRealtimeChannelMarkdown = (report) => [
|
|
|
35705
35657
|
].join(`
|
|
35706
35658
|
`);
|
|
35707
35659
|
var renderVoiceRealtimeChannelHTML = (report, title = "Voice Realtime Channel Proof") => {
|
|
35708
|
-
const issues = report.issues.map((issue) => `<li class="${
|
|
35709
|
-
const rows = report.runtime.samples.map((sample) => `<tr><td>${
|
|
35710
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
35660
|
+
const issues = report.issues.map((issue) => `<li class="${escapeHtml(issue.severity)}"><strong>${escapeHtml(issue.code)}</strong>: ${escapeHtml(issue.message)}</li>`).join("");
|
|
35661
|
+
const rows = report.runtime.samples.map((sample) => `<tr><td>${escapeHtml(sample.kind)}</td><td>${escapeHtml(sample.source ?? "runtime")}</td><td>${escapeHtml(sample.format ? formatLabel(sample.format) : "n/a")}</td><td>${escapeHtml(sample.latencyMs ?? "n/a")}</td><td>${escapeHtml(sample.ok ?? true)}</td></tr>`).join("");
|
|
35662
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1080px;padding:32px}.hero,.card{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(59,130,246,.12))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:grid;gap:12px;grid-template-columns:repeat(auto-fit,minmax(170px,1fr))}.metric{background:#101814;border:1px solid #2e3d36;border-radius:18px;padding:14px}.metric span{color:#a8b5ad;display:block;font-size:.78rem;text-transform:uppercase}.metric strong{display:block;font-size:1.65rem;margin-top:5px}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:7px 11px}.pass{color:#86efac}.warn{color:#fde68a}.fail,.error{color:#fecaca}.warning{color:#fde68a}table{border-collapse:collapse;width:100%}td,th{border-bottom:1px solid #2e3d36;padding:10px;text-align:left}.links a{color:#5eead4;margin-right:12px}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime / duplex readiness</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">${escapeHtml(report.status)}</p><p>Provider <strong>${escapeHtml(report.provider)}</strong> \xB7 ${escapeHtml(report.surface)}</p><p class="links">${report.readinessHref ? `<a href="${escapeHtml(report.readinessHref)}">Readiness</a>` : ""}${report.operationsRecordHref ? `<a href="${escapeHtml(report.operationsRecordHref)}">Operations record</a>` : ""}</p><section class="summary"><div class="metric"><span>Input</span><strong>${escapeHtml(formatLabel(report.inputFormat))}</strong></div><div class="metric"><span>Output</span><strong>${escapeHtml(formatLabel(report.outputFormat))}</strong></div><div class="metric"><span>Browser capture</span><strong>${escapeHtml(report.browserCapture ? `${String(report.browserCapture.sampleRateHz)}hz` : "missing")}</strong></div><div class="metric"><span>Resampling</span><strong>${report.browserCapture?.resamplingRequired ? "required" : "not required"}</strong></div><div class="metric"><span>Input samples</span><strong>${String(report.runtime.inputAudioSamples)}</strong></div><div class="metric"><span>Assistant samples</span><strong>${String(report.runtime.assistantAudioSamples)}</strong></div></section></section><section class="card"><h2>Issues</h2><ul>${issues || '<li class="pass">No realtime channel issues.</li>'}</ul></section><section class="card"><h2>Runtime Samples</h2><table><thead><tr><th>Kind</th><th>Source</th><th>Format</th><th>Latency ms</th><th>OK</th></tr></thead><tbody>${rows || '<tr><td colspan="5">No runtime samples configured.</td></tr>'}</tbody></table></section></main></body></html>`;
|
|
35711
35663
|
};
|
|
35712
35664
|
var createVoiceRealtimeChannelRoutes = (options) => {
|
|
35713
35665
|
const path = options.path ?? "/api/voice/realtime-channel";
|
|
@@ -35782,14 +35734,13 @@ var defaultProviderEnv2 = {
|
|
|
35782
35734
|
"openai-realtime": ["OPENAI_API_KEY"]
|
|
35783
35735
|
};
|
|
35784
35736
|
var defaultRealtimeProviders = ["openai-realtime", "gemini-live"];
|
|
35785
|
-
var
|
|
35737
|
+
var statusRank6 = {
|
|
35786
35738
|
pass: 0,
|
|
35787
35739
|
warn: 1,
|
|
35788
35740
|
fail: 2
|
|
35789
35741
|
};
|
|
35790
|
-
var statusExceeds2 = (actual, max) =>
|
|
35791
|
-
var rollupStatus4 = (checks) => checks.
|
|
35792
|
-
var escapeHtml52 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
35742
|
+
var statusExceeds2 = (actual, max) => statusRank6[actual] > statusRank6[max];
|
|
35743
|
+
var rollupStatus4 = (checks) => worstVoiceStatus(checks.map((check) => check.status));
|
|
35793
35744
|
var resolveProviderHref = (value, provider) => typeof value === "string" ? value : value?.[provider];
|
|
35794
35745
|
var createVoiceRealtimeProviderContractMatrixPreset = (options = {}) => {
|
|
35795
35746
|
const providers = options.providers ?? defaultRealtimeProviders;
|
|
@@ -35963,10 +35914,10 @@ var assertVoiceRealtimeProviderContractEvidence = (report, input = {}) => {
|
|
|
35963
35914
|
var resolveMatrix = async (matrix) => typeof matrix === "function" ? await matrix() : matrix;
|
|
35964
35915
|
var renderVoiceRealtimeProviderContractHTML = (report, title = "Voice Realtime Provider Contracts") => {
|
|
35965
35916
|
const rows = report.rows.map((row) => {
|
|
35966
|
-
const checks = row.checks.map((check) => `<li class="${
|
|
35967
|
-
return `<article class="row ${
|
|
35917
|
+
const checks = row.checks.map((check) => `<li class="${escapeHtml(check.status)}"><strong>${escapeHtml(check.label)}</strong><span>${escapeHtml(check.detail ?? check.status)}</span></li>`).join("");
|
|
35918
|
+
return `<article class="row ${escapeHtml(row.status)}"><div><p class="eyebrow">${row.selected ? "selected" : "available"}</p><h2>${escapeHtml(row.provider)}</h2><p class="status ${escapeHtml(row.status)}">${escapeHtml(row.status)}</p></div><ul>${checks}</ul></article>`;
|
|
35968
35919
|
}).join("");
|
|
35969
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
35920
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#101418;color:#f7f3e8;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero,.row{background:#17201d;border:1px solid #2e3d36;border-radius:24px;margin-bottom:16px;padding:22px}.hero{background:linear-gradient(135deg,rgba(20,184,166,.18),rgba(245,158,11,.1))}.eyebrow{color:#5eead4;font-weight:900;letter-spacing:.1em;text-transform:uppercase}h1{font-size:clamp(2.3rem,6vw,4.8rem);letter-spacing:-.06em;line-height:.9;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill,.status{border:1px solid #3f4f45;border-radius:999px;display:inline-flex;padding:8px 12px}.row{display:grid;gap:18px;grid-template-columns:minmax(190px,.4fr) 1fr}.row ul{display:grid;gap:10px;list-style:none;margin:0;padding:0}.row li{background:#101814;border:1px solid #2e3d36;border-radius:16px;display:grid;gap:4px;padding:12px}.row li span{color:#b8c2ba}.pass{color:#86efac}.warn{color:#fde68a}.fail{color:#fecaca}@media(max-width:760px){main{padding:18px}.row{grid-template-columns:1fr}}</style></head><body><main><section class="hero"><p class="eyebrow">Realtime provider contracts</p><h1>${escapeHtml(title)}</h1><p>Provider-level proof for duplex audio, browser format negotiation, turn commit, latency, reconnect, barge-in, trace evidence, fallback, and readiness gates.</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>${rows || '<article class="row"><p>No realtime provider contracts configured.</p></article>'}</main></body></html>`;
|
|
35970
35921
|
};
|
|
35971
35922
|
var createVoiceRealtimeProviderContractRoutes = (options) => {
|
|
35972
35923
|
const path = options.path ?? "/api/voice/realtime-provider-contracts";
|
|
@@ -36000,7 +35951,6 @@ var createVoiceRealtimeProviderContractRoutes = (options) => {
|
|
|
36000
35951
|
|
|
36001
35952
|
// src/reconnectContract.ts
|
|
36002
35953
|
import { Elysia as Elysia61 } from "elysia";
|
|
36003
|
-
var escapeHtml53 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
36004
35954
|
var unique2 = (values) => [...new Set(values)];
|
|
36005
35955
|
var isReconnectPayload = (value) => {
|
|
36006
35956
|
if (!value || typeof value !== "object") {
|
|
@@ -36213,8 +36163,8 @@ var createVoiceReconnectProofRoutes = (options = {}) => {
|
|
|
36213
36163
|
});
|
|
36214
36164
|
};
|
|
36215
36165
|
var renderVoiceReconnectContractHTML = (report) => {
|
|
36216
|
-
const issues = report.issues.map((issue) => `<li class="${
|
|
36217
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Voice Reconnect Contract</title><style>body{background:#0d1117;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,.card{background:#151b23;border:1px solid #30363d;border-radius:18px;margin-bottom:16px;padding:20px}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.25rem,7vw,4.5rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0d1117;border:1px solid #30363d;border-radius:999px;padding:7px 10px}.pass{color:#86efac}.fail,.error{color:#fca5a5}.warning{color:#fde68a}li{margin:8px 0}</style></head><body><main><section class="hero"><p class="eyebrow">Reconnect Resume Proof</p><h1>Voice reconnect contract</h1><div class="summary"><span class="pill ${report.pass ? "pass" : "fail"}">${report.pass ? "pass" : "fail"}</span><span class="pill">${String(report.snapshotCount)} snapshots</span><span class="pill">${String(report.summary.attempts)} attempts</span><span class="pill">statuses ${
|
|
36166
|
+
const issues = report.issues.map((issue) => `<li class="${escapeHtml(issue.severity)}"><strong>${escapeHtml(issue.code)}</strong>: ${escapeHtml(issue.message)}</li>`).join("");
|
|
36167
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>Voice Reconnect Contract</title><style>body{background:#0d1117;color:#f8fafc;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,.card{background:#151b23;border:1px solid #30363d;border-radius:18px;margin-bottom:16px;padding:20px}.eyebrow{color:#7dd3fc;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}h1{font-size:clamp(2.25rem,7vw,4.5rem);letter-spacing:-.06em;line-height:.92;margin:.2rem 0 1rem}.summary{display:flex;flex-wrap:wrap;gap:10px}.pill{background:#0d1117;border:1px solid #30363d;border-radius:999px;padding:7px 10px}.pass{color:#86efac}.fail,.error{color:#fca5a5}.warning{color:#fde68a}li{margin:8px 0}</style></head><body><main><section class="hero"><p class="eyebrow">Reconnect Resume Proof</p><h1>Voice reconnect contract</h1><div class="summary"><span class="pill ${report.pass ? "pass" : "fail"}">${report.pass ? "pass" : "fail"}</span><span class="pill">${String(report.snapshotCount)} snapshots</span><span class="pill">${String(report.summary.attempts)} attempts</span><span class="pill">statuses ${escapeHtml(report.statuses.join(", ") || "none")}</span></div></section><section class="card"><h2>Summary</h2><p>reconnected ${String(report.summary.reconnected)} \xB7 resumed ${String(report.summary.resumed)} \xB7 exhausted ${String(report.summary.exhausted)} \xB7 duplicate turns ${String(report.summary.duplicateTurnIds.length)}</p></section><section class="card"><h2>Issues</h2>${issues ? `<ul>${issues}</ul>` : '<p class="pass">No contract issues.</p>'}</section></main></body></html>`;
|
|
36218
36168
|
};
|
|
36219
36169
|
var createVoiceReconnectContractRoutes = (options) => {
|
|
36220
36170
|
const path = options.path ?? "/api/voice/reconnect-contract";
|
|
@@ -36461,7 +36411,6 @@ var createDefaultTurn = (caseId) => ({
|
|
|
36461
36411
|
});
|
|
36462
36412
|
var defaultApi = {};
|
|
36463
36413
|
var sameJSON = (left, right) => JSON.stringify(left) === JSON.stringify(right);
|
|
36464
|
-
var escapeHtml54 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
36465
36414
|
var resolveSessionHref5 = (value, sessionId) => {
|
|
36466
36415
|
if (value === false) {
|
|
36467
36416
|
return;
|
|
@@ -36712,7 +36661,7 @@ var assertVoiceToolContractEvidence = (report, input = {}) => {
|
|
|
36712
36661
|
};
|
|
36713
36662
|
var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
36714
36663
|
const title = options.title ?? "Voice Tool Contracts";
|
|
36715
|
-
const snippet =
|
|
36664
|
+
const snippet = escapeHtml(`app.use(
|
|
36716
36665
|
createVoiceToolContractRoutes({
|
|
36717
36666
|
htmlPath: '/tool-contracts',
|
|
36718
36667
|
path: '/api/tool-contracts',
|
|
@@ -36738,20 +36687,20 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
36738
36687
|
);`);
|
|
36739
36688
|
const contracts = report.contracts.map((contract) => {
|
|
36740
36689
|
const cases = contract.cases.map((testCase) => `<tr>
|
|
36741
|
-
<td>${testCase.operationsRecordHref ? `<a href="${
|
|
36690
|
+
<td>${testCase.operationsRecordHref ? `<a href="${escapeHtml(testCase.operationsRecordHref)}">${escapeHtml(testCase.label ?? testCase.caseId)}</a>` : escapeHtml(testCase.label ?? testCase.caseId)}</td>
|
|
36742
36691
|
<td class="${testCase.pass ? "pass" : "fail"}">${testCase.pass ? "pass" : "fail"}</td>
|
|
36743
|
-
<td>${
|
|
36744
|
-
<td>${
|
|
36692
|
+
<td>${escapeHtml(testCase.status)}</td>
|
|
36693
|
+
<td>${escapeHtml(testCase.sessionId)}</td>
|
|
36745
36694
|
<td>${String(testCase.attempts)}</td>
|
|
36746
36695
|
<td>${String(testCase.elapsedMs)}ms</td>
|
|
36747
36696
|
<td>${testCase.timedOut ? "yes" : "no"}</td>
|
|
36748
|
-
<td>${
|
|
36697
|
+
<td>${escapeHtml(testCase.issues.map((issue) => issue.message).join(" ") || testCase.error || "")}</td>
|
|
36749
36698
|
</tr>`).join("");
|
|
36750
36699
|
return `<section class="contract ${contract.pass ? "pass" : "fail"}">
|
|
36751
36700
|
<div class="contract-header">
|
|
36752
36701
|
<div>
|
|
36753
|
-
<p class="eyebrow">${
|
|
36754
|
-
<h2>${
|
|
36702
|
+
<p class="eyebrow">${escapeHtml(contract.toolName)}</p>
|
|
36703
|
+
<h2>${escapeHtml(contract.label ?? contract.contractId)}</h2>
|
|
36755
36704
|
</div>
|
|
36756
36705
|
<strong class="${contract.pass ? "pass" : "fail"}">${contract.pass ? "Passing" : "Failing"}</strong>
|
|
36757
36706
|
</div>
|
|
@@ -36761,7 +36710,7 @@ var renderVoiceToolContractHTML = (report, options = {}) => {
|
|
|
36761
36710
|
</table>
|
|
36762
36711
|
</section>`;
|
|
36763
36712
|
}).join("");
|
|
36764
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
36713
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><div class="summary"><span class="pill ${report.status === "pass" ? "pass" : "fail"}">${escapeHtml(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>`;
|
|
36765
36714
|
};
|
|
36766
36715
|
var createVoiceToolContractJSONHandler = (options) => () => runVoiceToolContractSuite(options);
|
|
36767
36716
|
var createVoiceToolContractHTMLHandler = (options) => async () => {
|
|
@@ -36788,7 +36737,6 @@ var createVoiceToolContractRoutes = (options) => {
|
|
|
36788
36737
|
};
|
|
36789
36738
|
|
|
36790
36739
|
// src/simulationSuite.ts
|
|
36791
|
-
var escapeHtml55 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
36792
36740
|
var summarizeSection = (report) => ({
|
|
36793
36741
|
failed: report.failed,
|
|
36794
36742
|
passed: report.passed,
|
|
@@ -36984,15 +36932,15 @@ var renderSection = (label, summary) => {
|
|
|
36984
36932
|
if (!summary) {
|
|
36985
36933
|
return "";
|
|
36986
36934
|
}
|
|
36987
|
-
return `<article class="${
|
|
36935
|
+
return `<article class="${escapeHtml(summary.status)}"><span>${escapeHtml(label)}</span><strong>${escapeHtml(summary.status)}</strong><p>${summary.passed}/${summary.total} passed, ${summary.failed} failed.</p></article>`;
|
|
36988
36936
|
};
|
|
36989
36937
|
var renderAction = (action) => {
|
|
36990
|
-
const content = `<strong>${
|
|
36991
|
-
return action.href ? `<a class="action" href="${
|
|
36938
|
+
const content = `<strong>${escapeHtml(action.label)}</strong><p>${escapeHtml(action.description)}</p><span>${escapeHtml(action.section)} / ${escapeHtml(action.severity)}</span>`;
|
|
36939
|
+
return action.href ? `<a class="action" href="${escapeHtml(action.href)}">${content}</a>` : `<article class="action">${content}</article>`;
|
|
36992
36940
|
};
|
|
36993
36941
|
var renderVoiceSimulationSuiteHTML = (report, options = {}) => {
|
|
36994
36942
|
const title = options.title ?? "Voice Simulation Suite";
|
|
36995
|
-
const snippet =
|
|
36943
|
+
const snippet = escapeHtml(`app.use(
|
|
36996
36944
|
createVoiceSimulationSuiteRoutes({
|
|
36997
36945
|
htmlPath: '/voice/simulations',
|
|
36998
36946
|
path: '/api/voice/simulations',
|
|
@@ -37025,7 +36973,7 @@ app.use(
|
|
|
37025
36973
|
store: traceStore
|
|
37026
36974
|
})
|
|
37027
36975
|
);`);
|
|
37028
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
36976
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p>One report for session quality, scenario evals, fixture simulations, tool contracts, and outcome contracts.</p><p class="badge ${escapeHtml(report.status)}">Status: ${escapeHtml(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>${escapeHtml(JSON.stringify({ summary: report.summary, actions: report.actions }, null, 2))}</pre></main></body></html>`;
|
|
37029
36977
|
};
|
|
37030
36978
|
var createVoiceSimulationSuiteRoutes = (options) => {
|
|
37031
36979
|
const path = options.path ?? "/api/voice/simulations";
|
|
@@ -37231,7 +37179,6 @@ var buildVoiceSloReadinessThresholdReport = (input, options = {}) => {
|
|
|
37231
37179
|
};
|
|
37232
37180
|
};
|
|
37233
37181
|
var escapeMarkdown3 = (value) => value.replaceAll("|", "\\|");
|
|
37234
|
-
var escapeHtml56 = (value) => String(value).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37235
37182
|
var formatMs5 = (value) => value === undefined ? "n/a" : `${value.toLocaleString()} ms`;
|
|
37236
37183
|
var readinessThresholdRows = (report) => [
|
|
37237
37184
|
{
|
|
@@ -37322,10 +37269,10 @@ ${report.issues.map((issue) => `- ${issue}`).join(`
|
|
|
37322
37269
|
};
|
|
37323
37270
|
var renderVoiceSloReadinessThresholdHTML = (report, options = {}) => {
|
|
37324
37271
|
const title = options.title ?? "Calibration -> Active Readiness Gate";
|
|
37325
|
-
const rows = readinessThresholdRows(report).map((row) => `<tr><td>${
|
|
37326
|
-
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${
|
|
37327
|
-
const sources = report.sources.length === 0 ? "<li>n/a</li>" : report.sources.map((source) => `<li><code>${
|
|
37328
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${
|
|
37272
|
+
const rows = readinessThresholdRows(report).map((row) => `<tr><td>${escapeHtml(row.control)}</td><td>${escapeHtml(formatMs5(row.value))}</td><td>${escapeHtml(row.usedBy)}</td></tr>`).join("");
|
|
37273
|
+
const issues = report.issues.length === 0 ? "<li>None</li>" : report.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("");
|
|
37274
|
+
const sources = report.sources.length === 0 ? "<li>n/a</li>" : report.sources.map((source) => `<li><code>${escapeHtml(source)}</code></li>`).join("");
|
|
37275
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" /><title>${escapeHtml(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:1040px;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}.status{font-size:1.6rem;font-weight:800;text-transform:uppercase}</style></head><body><main><h1>${escapeHtml(title)}</h1><p>This page shows the calibrated thresholds currently driving production readiness gates.</p><section class="summary"><div class="card"><strong>Status</strong><br><span class="status">${escapeHtml(report.status)}</span></div><div class="card"><strong>Live evidence max age</strong><br>${escapeHtml(formatMs5(report.liveLatencyMaxAgeMs))}</div><div class="card"><strong>Provider p95 gate</strong><br>${escapeHtml(formatMs5(report.providerSlo.llm?.maxP95ElapsedMs))}</div><div class="card"><strong>Barge-in gate</strong><br>${escapeHtml(formatMs5(report.bargeIn.thresholdMs))}</div></section><h2>Active Readiness Thresholds</h2><table><thead><tr><th>Threshold</th><th>Active value</th><th>Used by</th></tr></thead><tbody>${rows}</tbody></table><h2>Sources</h2><ul>${sources}</ul><h2>Issues</h2><ul>${issues}</ul></main></body></html>`;
|
|
37329
37276
|
};
|
|
37330
37277
|
var createVoiceSloCalibrationRoutes = (options) => {
|
|
37331
37278
|
const path = options.path ?? "/api/voice/slo-calibration";
|
|
@@ -37483,7 +37430,6 @@ var byteLength2 = (audio) => {
|
|
|
37483
37430
|
}
|
|
37484
37431
|
return audio instanceof ArrayBuffer ? audio.byteLength : audio.byteLength;
|
|
37485
37432
|
};
|
|
37486
|
-
var escapeHtml57 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37487
37433
|
var buildVoiceTelephonyMediaReport = (input = {}) => {
|
|
37488
37434
|
const carriers = input.carriers ?? [
|
|
37489
37435
|
{ carrier: "twilio" },
|
|
@@ -37572,8 +37518,8 @@ var getLatestVoiceTelephonyMediaReport = async (options) => {
|
|
|
37572
37518
|
};
|
|
37573
37519
|
var renderVoiceTelephonyMediaHTML = (report, options = {}) => {
|
|
37574
37520
|
const title = options.title ?? "Voice Telephony Media Proof";
|
|
37575
|
-
const rows = report.carriers.map((carrier) => `<tr><td>${
|
|
37576
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
37521
|
+
const rows = report.carriers.map((carrier) => `<tr><td>${escapeHtml(carrier.carrier)}</td><td>${escapeHtml(carrier.status)}</td><td>${String(carrier.audioBytes)}</td><td>${String(carrier.lifecycle.mediaEvents)}</td><td>${escapeHtml(carrier.lifecycle.started ? "yes" : "no")}</td><td>${escapeHtml(carrier.lifecycle.stopped ? "yes" : "no")}</td><td>${escapeHtml(carrier.frame?.kind ?? "missing")}</td><td>${escapeHtml(carrier.frame?.format?.encoding ?? "missing")}</td><td>${escapeHtml(carrier.issues.join(" ") || "none")}</td></tr>`).join("");
|
|
37522
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#111827;color:#e5e7eb;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:980px;padding:32px}.hero,table{background:#0f172a;border:1px solid #334155;border-radius:20px;margin-bottom:16px}.hero{padding:22px}.eyebrow{color:#67e8f9;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,6vw,4.5rem);line-height:.92;margin:.2rem 0 1rem}.status{border:1px solid #64748b;border-radius:999px;display:inline-flex;font-weight:900;padding:8px 12px}.pass{color:#86efac}.fail{color:#fecaca}code{color:#bfdbfe}table{border-collapse:collapse;overflow:hidden;width:100%}td,th{border-bottom:1px solid #334155;padding:10px;text-align:left}</style></head><body><main><section class="hero"><p class="eyebrow">Carrier media serializer proof</p><h1>${escapeHtml(title)}</h1><p class="status ${escapeHtml(report.status)}">Status: ${escapeHtml(report.status)}</p><p>Twilio, Telnyx, and Plivo media payload envelopes are parsed into generic <code>MediaFrame</code> objects, serialized back into carrier envelopes, and checked for start/media/stop lifecycle sequencing by <code>@absolutejs/media</code>.</p></section><table><thead><tr><th>Carrier</th><th>Status</th><th>Audio bytes</th><th>Media events</th><th>Started</th><th>Stopped</th><th>Frame kind</th><th>Encoding</th><th>Issues</th></tr></thead><tbody>${rows}</tbody></table></main></body></html>`;
|
|
37577
37523
|
};
|
|
37578
37524
|
var createVoiceTelephonyMediaRoutes = (options = {}) => {
|
|
37579
37525
|
const path = options.path ?? "/api/voice/telephony/media";
|
|
@@ -37604,7 +37550,6 @@ var createVoiceTelephonyMediaRoutes = (options = {}) => {
|
|
|
37604
37550
|
|
|
37605
37551
|
// src/traceDeliveryRoutes.ts
|
|
37606
37552
|
import { Elysia as Elysia66 } from "elysia";
|
|
37607
|
-
var escapeHtml58 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37608
37553
|
var getString19 = (value) => typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
37609
37554
|
var getNumber12 = (value) => {
|
|
37610
37555
|
if (typeof value === "number" && Number.isFinite(value)) {
|
|
@@ -37685,14 +37630,14 @@ var renderSinkResults2 = (delivery) => {
|
|
|
37685
37630
|
if (entries.length === 0) {
|
|
37686
37631
|
return "<p>No sink delivery attempts recorded yet.</p>";
|
|
37687
37632
|
}
|
|
37688
|
-
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${
|
|
37633
|
+
return `<ul>${entries.map(([sinkId, result]) => `<li><strong>${escapeHtml(sinkId)}</strong>: ${escapeHtml(result.status)}${result.deliveredTo ? ` to ${escapeHtml(result.deliveredTo)}` : ""}${result.error ? ` (${escapeHtml(result.error)})` : ""}</li>`).join("")}</ul>`;
|
|
37689
37634
|
};
|
|
37690
|
-
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${
|
|
37635
|
+
var renderEventList2 = (delivery) => delivery.events.length === 0 ? "<p>No trace events in this delivery.</p>" : `<ul>${delivery.events.map((event) => `<li>${escapeHtml(event.type)} <small>${escapeHtml(event.id)}</small>${event.sessionId ? ` session=${escapeHtml(event.sessionId)}` : ""}</li>`).join("")}</ul>`;
|
|
37691
37636
|
var renderVoiceTraceDeliveryHTML = (report, options = {}) => {
|
|
37692
37637
|
const title = options.title ?? "AbsoluteJS Voice Trace Deliveries";
|
|
37693
|
-
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${
|
|
37694
|
-
const rows = report.deliveries.map((delivery) => `<article class="delivery ${
|
|
37695
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
37638
|
+
const drainAction = options.workerPath === false ? "" : `<form method="post" action="${escapeHtml(options.workerPath ?? "/api/voice-trace-deliveries/drain")}"><button type="submit">Drain trace deliveries</button></form>`;
|
|
37639
|
+
const rows = report.deliveries.map((delivery) => `<article class="delivery ${escapeHtml(delivery.deliveryStatus)}"><div class="head"><div><span>${escapeHtml(delivery.deliveryStatus)}</span><h2>${escapeHtml(delivery.id)}</h2><p>${escapeHtml(new Date(delivery.createdAt).toLocaleString())}${delivery.deliveredAt ? ` \xB7 delivered ${escapeHtml(new Date(delivery.deliveredAt).toLocaleString())}` : ""}</p></div><strong>${String(delivery.deliveryAttempts ?? 0)} attempt(s)</strong></div>${delivery.deliveryError ? `<p class="error">${escapeHtml(delivery.deliveryError)}</p>` : ""}<h3>Sinks</h3>${renderSinkResults2(delivery)}<h3>Events</h3>${renderEventList2(delivery)}</article>`).join("");
|
|
37640
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(title)}</title><style>body{background:#0f1318;color:#f4efe1;font-family:ui-sans-serif,system-ui,sans-serif;margin:0}main{margin:auto;max-width:1120px;padding:32px}.hero{background:linear-gradient(135deg,rgba(34,197,94,.16),rgba(14,165,233,.14));border:1px solid #26313d;border-radius:28px;margin-bottom:18px;padding:28px}.eyebrow{color:#86efac;font-weight:900;letter-spacing:.12em;text-transform:uppercase}h1{font-size:clamp(2.2rem,5vw,4.8rem);line-height:.92;margin:.2rem 0 1rem}.grid{display:grid;gap:12px;grid-template-columns:repeat(4,1fr);margin-bottom:16px}.grid article,.delivery{background:#151b22;border:1px solid #26313d;border-radius:22px;padding:18px}.grid span,.delivery span{color:#86efac;font-size:.78rem;font-weight:900;letter-spacing:.08em;text-transform:uppercase}.grid strong{display:block;font-size:2rem}.deliveries{display:grid;gap:14px}.delivery.failed{border-color:rgba(239,68,68,.75)}.delivery.pending{border-color:rgba(245,158,11,.7)}.delivery.delivered{border-color:rgba(34,197,94,.55)}.delivery.skipped{border-color:rgba(148,163,184,.6)}.head{align-items:start;display:flex;gap:14px;justify-content:space-between}.delivery h2{font-size:1.05rem;margin:.3rem 0;overflow-wrap:anywhere}.delivery h3{margin:1rem 0 .3rem}.delivery p,.delivery li{color:#c8d0d8}.error{color:#fecaca!important}button{background:#86efac;border:0;border-radius:999px;color:#07111f;cursor:pointer;font-weight:900;margin-top:12px;padding:10px 14px}@media(max-width:760px){main{padding:20px}.grid{grid-template-columns:1fr 1fr}.head{display:block}}</style></head><body><main><section class="hero"><p class="eyebrow">Trace export health</p><h1>${escapeHtml(title)}</h1><p>Checked ${escapeHtml(new Date(report.checkedAt).toLocaleString())}. Showing ${String(report.deliveries.length)} delivery item(s).</p>${drainAction}</section>${renderMetricGrid3(report)}<section class="deliveries">${rows || "<p>No trace deliveries match this filter.</p>"}</section></main></body></html>`;
|
|
37696
37641
|
};
|
|
37697
37642
|
var createVoiceTraceDeliveryJSONHandler = (options) => async ({ query }) => buildVoiceTraceDeliveryReport(options, resolveVoiceTraceDeliveryFilter(query, options.filter));
|
|
37698
37643
|
var createVoiceTraceDeliveryHTMLHandler = (options) => async ({ query }) => {
|
|
@@ -37734,7 +37679,6 @@ var createVoiceTraceDeliveryRoutes = (options) => {
|
|
|
37734
37679
|
import { Elysia as Elysia67 } from "elysia";
|
|
37735
37680
|
var DEFAULT_WARN_AFTER_MS2 = 1800;
|
|
37736
37681
|
var DEFAULT_FAIL_AFTER_MS2 = 3200;
|
|
37737
|
-
var escapeHtml59 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37738
37682
|
var firstNumber3 = (values) => values.filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
37739
37683
|
var getString20 = (value) => typeof value === "string" && value.trim() ? value : undefined;
|
|
37740
37684
|
var createTraceStageIndex = (events) => {
|
|
@@ -37870,11 +37814,11 @@ await traceStore.append({
|
|
|
37870
37814
|
turnId,
|
|
37871
37815
|
type: 'turn_latency.stage'
|
|
37872
37816
|
});`;
|
|
37873
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
37874
|
-
<header><div><p class="eyebrow">${
|
|
37875
|
-
<dl>${turn.stages.map((stage) => `<div><dt>${
|
|
37817
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml(turn.status)}">
|
|
37818
|
+
<header><div><p class="eyebrow">${escapeHtml(turn.sessionId)} \xB7 ${escapeHtml(turn.turnId)}</p><h2>${escapeHtml(turn.text || "Empty turn")}</h2></div><strong>${escapeHtml(turn.status)}</strong></header>
|
|
37819
|
+
<dl>${turn.stages.map((stage) => `<div><dt>${escapeHtml(stage.label)}</dt><dd>${escapeHtml(formatMs6(stage.valueMs))}</dd></div>`).join("")}</dl>
|
|
37876
37820
|
</article>`).join("");
|
|
37877
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
37821
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><div class="summary"><span class="pill ${escapeHtml(report.status)}">${escapeHtml(report.status)}</span><span class="pill">${String(report.total)} turns</span><span class="pill">avg ${escapeHtml(formatMs6(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>${escapeHtml(snippet)}</code></pre></section>${turns || '<section class="turn"><p>No committed turns found.</p></section>'}</main></body></html>`;
|
|
37878
37822
|
};
|
|
37879
37823
|
var createVoiceTurnLatencyJSONHandler = (options) => async () => summarizeVoiceTurnLatency(options);
|
|
37880
37824
|
var createVoiceTurnLatencyHTMLHandler = (options) => async () => {
|
|
@@ -37903,7 +37847,6 @@ var createVoiceTurnLatencyRoutes = (options) => {
|
|
|
37903
37847
|
// src/turnQuality.ts
|
|
37904
37848
|
import { Elysia as Elysia68 } from "elysia";
|
|
37905
37849
|
var DEFAULT_CONFIDENCE_WARN_THRESHOLD = 0.72;
|
|
37906
|
-
var escapeHtml60 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
37907
37850
|
var getTurnLatencyMs = (turn) => {
|
|
37908
37851
|
const firstTranscriptAt = turn.transcripts.map((transcript) => transcript.endedAtMs ?? transcript.startedAtMs).filter((value) => typeof value === "number").sort((left, right) => left - right)[0];
|
|
37909
37852
|
if (firstTranscriptAt === undefined) {
|
|
@@ -37973,24 +37916,24 @@ var summarizeVoiceTurnQuality = async (options) => {
|
|
|
37973
37916
|
};
|
|
37974
37917
|
var renderVoiceTurnQualityHTML = (report, options = {}) => {
|
|
37975
37918
|
const title = options.title ?? "Voice Turn Quality";
|
|
37976
|
-
const turns = report.turns.map((turn) => `<article class="turn ${
|
|
37919
|
+
const turns = report.turns.map((turn) => `<article class="turn ${escapeHtml(turn.status)}">
|
|
37977
37920
|
<div class="turn-header">
|
|
37978
37921
|
<div>
|
|
37979
|
-
<p class="eyebrow">${
|
|
37980
|
-
<h2>${
|
|
37922
|
+
<p class="eyebrow">${escapeHtml(turn.sessionId)} \xB7 ${escapeHtml(turn.turnId)}</p>
|
|
37923
|
+
<h2>${escapeHtml(turn.text || "Empty turn")}</h2>
|
|
37981
37924
|
</div>
|
|
37982
|
-
<strong>${
|
|
37925
|
+
<strong>${escapeHtml(turn.status)}</strong>
|
|
37983
37926
|
</div>
|
|
37984
37927
|
<dl>
|
|
37985
|
-
<div><dt>Source</dt><dd>${
|
|
37928
|
+
<div><dt>Source</dt><dd>${escapeHtml(turn.source ?? "unknown")}</dd></div>
|
|
37986
37929
|
<div><dt>Confidence</dt><dd>${turn.averageConfidence === undefined ? "n/a" : `${Math.round(turn.averageConfidence * 100)}%`}</dd></div>
|
|
37987
|
-
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${
|
|
37988
|
-
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${
|
|
37930
|
+
<div><dt>Fallback</dt><dd>${turn.fallbackUsed ? `yes (${escapeHtml(turn.fallbackSelectionReason ?? "selected")})` : "no"}</dd></div>
|
|
37931
|
+
<div><dt>Correction</dt><dd>${turn.correctionChanged ? `changed${turn.correctionProvider ? ` by ${escapeHtml(turn.correctionProvider)}` : ""}` : "none"}</dd></div>
|
|
37989
37932
|
<div><dt>Transcripts</dt><dd>${String(turn.selectedTranscriptCount)} selected \xB7 ${String(turn.finalTranscriptCount)} final \xB7 ${String(turn.partialTranscriptCount)} partial</dd></div>
|
|
37990
37933
|
<div><dt>Cost</dt><dd>${turn.costUnits === undefined ? "n/a" : String(turn.costUnits)}</dd></div>
|
|
37991
37934
|
</dl>
|
|
37992
37935
|
</article>`).join("");
|
|
37993
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
37936
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><div class="summary"><span class="pill ${escapeHtml(report.status)}">${escapeHtml(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>`;
|
|
37994
37937
|
};
|
|
37995
37938
|
var createVoiceTurnQualityJSONHandler = (options) => async () => summarizeVoiceTurnQuality(options);
|
|
37996
37939
|
var createVoiceTurnQualityHTMLHandler = (options) => async () => {
|
|
@@ -38018,9 +37961,8 @@ var createVoiceTurnQualityRoutes = (options) => {
|
|
|
38018
37961
|
|
|
38019
37962
|
// src/voiceMonitoring.ts
|
|
38020
37963
|
import { Elysia as Elysia69 } from "elysia";
|
|
38021
|
-
var escapeHtml61 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
38022
37964
|
var issueIdForRun = (run) => `voice-monitor:${run.id}:${run.impactedSessions?.[0] ?? "global"}`;
|
|
38023
|
-
var rollupStatus5 = (runs) => runs.
|
|
37965
|
+
var rollupStatus5 = (runs) => worstVoiceStatus(runs.map((run) => run.status));
|
|
38024
37966
|
var createVoiceMemoryMonitorIssueStore = (initial = []) => {
|
|
38025
37967
|
const issues = new Map(initial.map((issue) => [issue.id, { ...issue }]));
|
|
38026
37968
|
return {
|
|
@@ -38271,14 +38213,14 @@ ${rows || "| none | pass | info | | | No monitors configured. |"}
|
|
|
38271
38213
|
};
|
|
38272
38214
|
var renderVoiceMonitorHTML = (report, options = {}) => {
|
|
38273
38215
|
const title = options.title ?? "Voice Monitors";
|
|
38274
|
-
const runs = report.runs.map((run) => `<tr><td>${
|
|
38275
|
-
const issues = report.issues.map((issue) => `<li><strong>${
|
|
38276
|
-
const snippet =
|
|
38216
|
+
const runs = report.runs.map((run) => `<tr><td>${escapeHtml(run.label)}</td><td class="${escapeHtml(run.status)}">${escapeHtml(run.status)}</td><td>${escapeHtml(run.severity)}</td><td>${escapeHtml(String(run.value ?? ""))}</td><td>${escapeHtml(String(run.threshold ?? ""))}</td><td>${escapeHtml(run.detail ?? "")}</td></tr>`).join("");
|
|
38217
|
+
const issues = report.issues.map((issue) => `<li><strong>${escapeHtml(issue.label)}</strong> <span class="${escapeHtml(issue.status)}">${escapeHtml(issue.status)}</span> ${escapeHtml(issue.detail ?? "")}</li>`).join("");
|
|
38218
|
+
const snippet = escapeHtml(`app.use(createVoiceMonitorRoutes({
|
|
38277
38219
|
evidence,
|
|
38278
38220
|
issueStore,
|
|
38279
38221
|
monitors: [defineVoiceMonitor(...)]
|
|
38280
38222
|
}));`);
|
|
38281
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
38223
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(title)}</h1><p class="pill ${escapeHtml(report.status)}">Status: ${escapeHtml(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>`;
|
|
38282
38224
|
};
|
|
38283
38225
|
var actorFromRequest = async (request) => {
|
|
38284
38226
|
if (!request.headers.get("content-type")?.includes("application/json")) {
|
|
@@ -38391,9 +38333,6 @@ var loadHTMXBootstrap = (() => {
|
|
|
38391
38333
|
return cached;
|
|
38392
38334
|
}
|
|
38393
38335
|
cached = (async () => {
|
|
38394
|
-
if (HTMX_BOOTSTRAP_BUNDLE) {
|
|
38395
|
-
return HTMX_BOOTSTRAP_BUNDLE;
|
|
38396
|
-
}
|
|
38397
38336
|
for (const candidate of HTMX_BOOTSTRAP_DIST_CANDIDATES) {
|
|
38398
38337
|
const asset = Bun.file(candidate);
|
|
38399
38338
|
if (await asset.exists()) {
|
|
@@ -40574,9 +40513,7 @@ var createVoiceLLMJudge = (options) => ({
|
|
|
40574
40513
|
rubric: options.rubric
|
|
40575
40514
|
});
|
|
40576
40515
|
var createVoiceAIJudgeCompletion = (options) => async ({ prompt, systemPrompt }) => {
|
|
40577
|
-
const messages = [
|
|
40578
|
-
{ content: prompt, role: "user" }
|
|
40579
|
-
];
|
|
40516
|
+
const messages = [{ content: prompt, role: "user" }];
|
|
40580
40517
|
const stream = options.provider.stream({
|
|
40581
40518
|
messages,
|
|
40582
40519
|
model: options.model,
|
|
@@ -40704,7 +40641,13 @@ var successRateFor = (calls, successOutcomes) => {
|
|
|
40704
40641
|
return successes / flagged.length;
|
|
40705
40642
|
};
|
|
40706
40643
|
var buildVoiceVariableAnalytics = (input) => {
|
|
40707
|
-
const successOutcomes = new Set((input.successOutcomes ?? [
|
|
40644
|
+
const successOutcomes = new Set((input.successOutcomes ?? [
|
|
40645
|
+
"won",
|
|
40646
|
+
"resolved",
|
|
40647
|
+
"qualified",
|
|
40648
|
+
"completed",
|
|
40649
|
+
"booked"
|
|
40650
|
+
]).map((outcome) => outcome.toLowerCase()));
|
|
40708
40651
|
const totalCalls = input.calls.length;
|
|
40709
40652
|
const overall = {
|
|
40710
40653
|
avgCostUsd: mean(input.calls.map((call) => call.costUsd).filter((cost) => typeof cost === "number")),
|
|
@@ -43219,7 +43162,6 @@ var PHONE_AGENT_LIFECYCLE_STAGES = [
|
|
|
43219
43162
|
"completed",
|
|
43220
43163
|
"failed"
|
|
43221
43164
|
];
|
|
43222
|
-
var escapeHtml62 = (value) => value.replaceAll("&", "&").replaceAll('"', """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
43223
43165
|
var loadRouteJson = async (input) => {
|
|
43224
43166
|
const response = await input.app.handle(new Request(new URL(input.path, input.origin).toString(), {
|
|
43225
43167
|
headers: {
|
|
@@ -43457,10 +43399,10 @@ var renderVoicePhoneAgentSetupHTML = (report) => {
|
|
|
43457
43399
|
const entry = findCarrierMatrixEntry(report.matrix, carrier);
|
|
43458
43400
|
const urls = entry?.setup.urls;
|
|
43459
43401
|
const primaryUrl = carrier.provider === "plivo" ? urls?.twiml : urls?.twiml;
|
|
43460
|
-
return `<tr><td>${
|
|
43402
|
+
return `<tr><td>${escapeHtml(carrier.name ?? carrier.provider)}</td><td>${escapeHtml(carrier.provider)}</td><td><code>${escapeHtml(carrier.setupPath || "disabled")}</code></td><td><code>${escapeHtml(carrier.smokePath || "disabled")}</code></td><td>${entry ? `<span class="${escapeHtml(entry.status)}">${escapeHtml(entry.status.toUpperCase())}</span>` : "unknown"}</td><td>${primaryUrl ? `<code>${escapeHtml(primaryUrl)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.webhook ? `<code>${escapeHtml(urls.webhook)}</code>` : '<span class="muted">missing</span>'}</td><td>${urls?.stream ? `<code>${escapeHtml(urls.stream)}</code>` : '<span class="muted">missing</span>'}</td></tr>`;
|
|
43461
43403
|
}).join("");
|
|
43462
|
-
const stageList = report.lifecycleStages.map((stage) => `<li><code>${
|
|
43463
|
-
const snippet =
|
|
43404
|
+
const stageList = report.lifecycleStages.map((stage) => `<li><code>${escapeHtml(stage)}</code></li>`).join("");
|
|
43405
|
+
const snippet = escapeHtml(`const phoneAgent = createVoicePhoneAgent({
|
|
43464
43406
|
carriers: [
|
|
43465
43407
|
{
|
|
43466
43408
|
provider: 'twilio',
|
|
@@ -43494,11 +43436,11 @@ app.use(
|
|
|
43494
43436
|
);`);
|
|
43495
43437
|
const checklist = report.carriers.map((carrier) => {
|
|
43496
43438
|
const instruction = report.setupInstructions.find((candidate) => candidate.provider === carrier.provider && candidate.carrierName === (carrier.name ?? carrier.provider));
|
|
43497
|
-
const issueList = instruction?.issues.map((issue) => `<li>${
|
|
43498
|
-
const steps = instruction?.steps.map((step) => `<li>${
|
|
43499
|
-
return `<article><h3>${
|
|
43439
|
+
const issueList = instruction?.issues.map((issue) => `<li>${escapeHtml(issue)}</li>`).join("") ?? "";
|
|
43440
|
+
const steps = instruction?.steps.map((step) => `<li>${escapeHtml(step)}</li>`).join("") ?? "";
|
|
43441
|
+
return `<article><h3>${escapeHtml(carrier.name ?? carrier.provider)}</h3><ol>${steps}</ol>${issueList ? `<ul class="issues">${issueList}</ul>` : '<p class="pass">No carrier contract issues.</p>'}</article>`;
|
|
43500
43442
|
}).join("");
|
|
43501
|
-
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${
|
|
43443
|
+
return `<!doctype html><html lang="en"><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" /><title>${escapeHtml(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>${escapeHtml(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="${escapeHtml(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>`;
|
|
43502
43444
|
};
|
|
43503
43445
|
var createVoicePhoneAgent = (options) => {
|
|
43504
43446
|
const carrierSummaries = options.carriers.map((carrier) => ({
|
|
@@ -47134,367 +47076,6 @@ var createVoiceSTTRoutingCorrectionHandler = (mode = "generic") => {
|
|
|
47134
47076
|
}
|
|
47135
47077
|
return createPhraseHintCorrectionHandler();
|
|
47136
47078
|
};
|
|
47137
|
-
// src/testing/telephony.ts
|
|
47138
|
-
var DEFAULT_PCM16_FORMAT = {
|
|
47139
|
-
channels: 1,
|
|
47140
|
-
container: "raw",
|
|
47141
|
-
encoding: "pcm_s16le",
|
|
47142
|
-
sampleRateHz: 16000
|
|
47143
|
-
};
|
|
47144
|
-
var DEFAULT_SCENARIOS = [
|
|
47145
|
-
{
|
|
47146
|
-
expectClear: false,
|
|
47147
|
-
expectMark: true,
|
|
47148
|
-
expectOutboundMedia: true,
|
|
47149
|
-
id: "telephony-turn",
|
|
47150
|
-
title: "Telephony bridge streams assistant audio and mark"
|
|
47151
|
-
},
|
|
47152
|
-
{
|
|
47153
|
-
expectClear: true,
|
|
47154
|
-
expectMark: true,
|
|
47155
|
-
expectOutboundMedia: true,
|
|
47156
|
-
id: "telephony-barge-in",
|
|
47157
|
-
secondInboundDelayMs: 5,
|
|
47158
|
-
title: "Telephony bridge clears queued outbound audio on barge-in"
|
|
47159
|
-
},
|
|
47160
|
-
{
|
|
47161
|
-
expectClear: true,
|
|
47162
|
-
expectMark: true,
|
|
47163
|
-
expectOutboundMedia: true,
|
|
47164
|
-
id: "telephony-streaming",
|
|
47165
|
-
secondInboundDelayMs: 8,
|
|
47166
|
-
title: "Telephony bridge keeps streaming chunks and still clears on re-entry",
|
|
47167
|
-
ttsChunkCount: 3,
|
|
47168
|
-
ttsChunkDelayMs: 4
|
|
47169
|
-
}
|
|
47170
|
-
];
|
|
47171
|
-
var waitFor = async (check, timeoutMs, intervalMs = 5) => {
|
|
47172
|
-
const deadline = Date.now() + timeoutMs;
|
|
47173
|
-
while (Date.now() < deadline) {
|
|
47174
|
-
if (check()) {
|
|
47175
|
-
return true;
|
|
47176
|
-
}
|
|
47177
|
-
await Bun.sleep(intervalMs);
|
|
47178
|
-
}
|
|
47179
|
-
return check();
|
|
47180
|
-
};
|
|
47181
|
-
var toUint8Array2 = (audio) => audio instanceof Uint8Array ? audio : audio instanceof ArrayBuffer ? new Uint8Array(audio) : new Uint8Array(audio.buffer, audio.byteOffset, audio.byteLength);
|
|
47182
|
-
var createFakeSTTAdapter = (inputSpy, sttDelayMs) => ({
|
|
47183
|
-
kind: "stt",
|
|
47184
|
-
open: (_options) => {
|
|
47185
|
-
const listeners = {
|
|
47186
|
-
close: new Set,
|
|
47187
|
-
endOfTurn: new Set,
|
|
47188
|
-
error: new Set,
|
|
47189
|
-
final: new Set,
|
|
47190
|
-
partial: new Set
|
|
47191
|
-
};
|
|
47192
|
-
let delivered = false;
|
|
47193
|
-
return {
|
|
47194
|
-
close: async () => {
|
|
47195
|
-
for (const handler of listeners.close) {
|
|
47196
|
-
handler({ type: "close" });
|
|
47197
|
-
}
|
|
47198
|
-
},
|
|
47199
|
-
on: (event, handler) => {
|
|
47200
|
-
listeners[event].add(handler);
|
|
47201
|
-
return () => {
|
|
47202
|
-
listeners[event].delete(handler);
|
|
47203
|
-
};
|
|
47204
|
-
},
|
|
47205
|
-
send: async (audio) => {
|
|
47206
|
-
inputSpy.push(toUint8Array2(audio));
|
|
47207
|
-
if (delivered) {
|
|
47208
|
-
return;
|
|
47209
|
-
}
|
|
47210
|
-
delivered = true;
|
|
47211
|
-
if (sttDelayMs > 0) {
|
|
47212
|
-
await Bun.sleep(sttDelayMs);
|
|
47213
|
-
}
|
|
47214
|
-
const receivedAt = Date.now();
|
|
47215
|
-
for (const handler of listeners.final) {
|
|
47216
|
-
handler({
|
|
47217
|
-
receivedAt,
|
|
47218
|
-
transcript: {
|
|
47219
|
-
id: "telephony-benchmark-final",
|
|
47220
|
-
isFinal: true,
|
|
47221
|
-
text: "hello from twilio"
|
|
47222
|
-
},
|
|
47223
|
-
type: "final"
|
|
47224
|
-
});
|
|
47225
|
-
}
|
|
47226
|
-
for (const handler of listeners.endOfTurn) {
|
|
47227
|
-
handler({
|
|
47228
|
-
receivedAt,
|
|
47229
|
-
reason: "vendor",
|
|
47230
|
-
type: "endOfTurn"
|
|
47231
|
-
});
|
|
47232
|
-
}
|
|
47233
|
-
}
|
|
47234
|
-
};
|
|
47235
|
-
}
|
|
47236
|
-
});
|
|
47237
|
-
var createFakeTTSAdapter = (chunkCount, chunkDelayMs) => ({
|
|
47238
|
-
kind: "tts",
|
|
47239
|
-
open: () => {
|
|
47240
|
-
const listeners = {
|
|
47241
|
-
audio: new Set,
|
|
47242
|
-
close: new Set,
|
|
47243
|
-
error: new Set
|
|
47244
|
-
};
|
|
47245
|
-
return {
|
|
47246
|
-
close: async () => {
|
|
47247
|
-
for (const handler of listeners.close) {
|
|
47248
|
-
handler({ type: "close" });
|
|
47249
|
-
}
|
|
47250
|
-
},
|
|
47251
|
-
on: (event, handler) => {
|
|
47252
|
-
listeners[event].add(handler);
|
|
47253
|
-
return () => {
|
|
47254
|
-
listeners[event].delete(handler);
|
|
47255
|
-
};
|
|
47256
|
-
},
|
|
47257
|
-
send: async () => {
|
|
47258
|
-
for (let index = 0;index < chunkCount; index += 1) {
|
|
47259
|
-
if (chunkDelayMs > 0) {
|
|
47260
|
-
await Bun.sleep(chunkDelayMs);
|
|
47261
|
-
}
|
|
47262
|
-
const chunk = new Uint8Array(320);
|
|
47263
|
-
for (let byteIndex = 0;byteIndex < chunk.length; byteIndex += 2) {
|
|
47264
|
-
chunk[byteIndex] = 255;
|
|
47265
|
-
chunk[byteIndex + 1] = 31;
|
|
47266
|
-
}
|
|
47267
|
-
for (const handler of listeners.audio) {
|
|
47268
|
-
handler({
|
|
47269
|
-
chunk,
|
|
47270
|
-
format: DEFAULT_PCM16_FORMAT,
|
|
47271
|
-
receivedAt: Date.now(),
|
|
47272
|
-
type: "audio"
|
|
47273
|
-
});
|
|
47274
|
-
}
|
|
47275
|
-
}
|
|
47276
|
-
}
|
|
47277
|
-
};
|
|
47278
|
-
}
|
|
47279
|
-
});
|
|
47280
|
-
var defaultOperationsRecordHref = ({
|
|
47281
|
-
sessionId
|
|
47282
|
-
}) => `/voice-operations/${encodeURIComponent(sessionId)}`;
|
|
47283
|
-
var resolveOperationsRecordHref3 = (href, input) => typeof href === "function" ? href(input) : href === undefined ? defaultOperationsRecordHref(input) : href;
|
|
47284
|
-
var runVoiceTelephonyMediaOperationsSmoke = async (options = {}) => {
|
|
47285
|
-
const timeoutMs = options.timeoutMs ?? 5000;
|
|
47286
|
-
const sessionId = options.sessionId ?? `telephony-media-ops-${Date.now()}`;
|
|
47287
|
-
const streamSid = options.streamSid ?? "telephony-media-ops-stream";
|
|
47288
|
-
const callSid = options.callSid ?? "telephony-media-ops-call";
|
|
47289
|
-
const scenarioId = options.scenarioId ?? "telephony-media-operations-smoke";
|
|
47290
|
-
const trace = options.store ?? createVoiceMemoryTraceEventStore();
|
|
47291
|
-
const sentEvents = [];
|
|
47292
|
-
const receivedAudio = [];
|
|
47293
|
-
const bridge = createTwilioMediaStreamBridge({
|
|
47294
|
-
close: () => {},
|
|
47295
|
-
send: (data) => {
|
|
47296
|
-
sentEvents.push(JSON.parse(data));
|
|
47297
|
-
}
|
|
47298
|
-
}, {
|
|
47299
|
-
context: {},
|
|
47300
|
-
onComplete: async () => {},
|
|
47301
|
-
onTurn: async () => ({
|
|
47302
|
-
assistantText: "Confirmed. Two way carrier media is visible."
|
|
47303
|
-
}),
|
|
47304
|
-
session: createVoiceMemoryStore(),
|
|
47305
|
-
stt: createFakeSTTAdapter(receivedAudio, 0),
|
|
47306
|
-
trace,
|
|
47307
|
-
tts: createFakeTTSAdapter(1, 0),
|
|
47308
|
-
turnDetection: {
|
|
47309
|
-
transcriptStabilityMs: 0
|
|
47310
|
-
}
|
|
47311
|
-
});
|
|
47312
|
-
const payload = encodeTwilioMulawBase64(new Int16Array([500, -500, 1500, -1500, 2500, -2500]));
|
|
47313
|
-
await bridge.handleMessage({
|
|
47314
|
-
event: "start",
|
|
47315
|
-
start: {
|
|
47316
|
-
callSid,
|
|
47317
|
-
customParameters: {
|
|
47318
|
-
scenarioId,
|
|
47319
|
-
sessionId
|
|
47320
|
-
},
|
|
47321
|
-
streamSid
|
|
47322
|
-
},
|
|
47323
|
-
streamSid
|
|
47324
|
-
});
|
|
47325
|
-
await bridge.handleMessage({
|
|
47326
|
-
event: "media",
|
|
47327
|
-
media: {
|
|
47328
|
-
payload,
|
|
47329
|
-
track: "inbound"
|
|
47330
|
-
},
|
|
47331
|
-
streamSid
|
|
47332
|
-
});
|
|
47333
|
-
await waitFor(() => sentEvents.some((message) => message.event === "media"), timeoutMs);
|
|
47334
|
-
await bridge.handleMessage({
|
|
47335
|
-
event: "media",
|
|
47336
|
-
media: {
|
|
47337
|
-
payload,
|
|
47338
|
-
track: "inbound"
|
|
47339
|
-
},
|
|
47340
|
-
streamSid
|
|
47341
|
-
});
|
|
47342
|
-
await waitFor(() => sentEvents.some((message) => message.event === "clear"), timeoutMs);
|
|
47343
|
-
await bridge.handleMessage({
|
|
47344
|
-
event: "stop",
|
|
47345
|
-
stop: {
|
|
47346
|
-
callSid
|
|
47347
|
-
},
|
|
47348
|
-
streamSid
|
|
47349
|
-
});
|
|
47350
|
-
await bridge.close("telephony-media-operations-smoke-complete");
|
|
47351
|
-
const operationsRecord = await buildVoiceOperationsRecord({
|
|
47352
|
-
sessionId,
|
|
47353
|
-
store: trace
|
|
47354
|
-
});
|
|
47355
|
-
const media = operationsRecord.telephonyMedia;
|
|
47356
|
-
const issues = [
|
|
47357
|
-
media.starts < 1 ? "Missing telephony media start event." : undefined,
|
|
47358
|
-
media.stops < 1 ? "Missing telephony media stop event." : undefined,
|
|
47359
|
-
media.inbound < 2 ? "Expected at least two inbound telephony media events." : undefined,
|
|
47360
|
-
media.outbound < 2 ? "Expected outbound assistant media/control evidence." : undefined,
|
|
47361
|
-
media.media < 3 ? "Expected inbound and outbound telephony media packet evidence." : undefined,
|
|
47362
|
-
media.clears < 1 ? "Missing outbound clear evidence." : undefined,
|
|
47363
|
-
media.audioBytes <= 0 ? "Missing telephony media audio bytes." : undefined,
|
|
47364
|
-
media.carriers.includes("twilio") ? undefined : "Missing Twilio carrier evidence.",
|
|
47365
|
-
media.streamIds.includes(streamSid) ? undefined : "Missing telephony media stream ID evidence."
|
|
47366
|
-
].filter((issue) => typeof issue === "string");
|
|
47367
|
-
return {
|
|
47368
|
-
issues,
|
|
47369
|
-
ok: issues.length === 0,
|
|
47370
|
-
operationsRecord,
|
|
47371
|
-
operationsRecordHref: resolveOperationsRecordHref3(options.operationsRecordHref, { sessionId, streamSid }),
|
|
47372
|
-
sentEvents: sentEvents.map((message) => message.event).filter((event) => typeof event === "string"),
|
|
47373
|
-
sessionId,
|
|
47374
|
-
streamSid,
|
|
47375
|
-
telephonyMedia: media
|
|
47376
|
-
};
|
|
47377
|
-
};
|
|
47378
|
-
var getDefaultVoiceTelephonyBenchmarkScenarios = () => DEFAULT_SCENARIOS.map((scenario) => ({ ...scenario }));
|
|
47379
|
-
var runVoiceTelephonyBenchmarkScenario = async (scenario, options = {}) => {
|
|
47380
|
-
const timeoutMs = options.timeoutMs ?? 1000;
|
|
47381
|
-
const sentEvents = [];
|
|
47382
|
-
const receivedAudio = [];
|
|
47383
|
-
const bridge = createTwilioMediaStreamBridge({
|
|
47384
|
-
close: () => {},
|
|
47385
|
-
send: (data) => {
|
|
47386
|
-
sentEvents.push({
|
|
47387
|
-
at: Date.now(),
|
|
47388
|
-
event: JSON.parse(data)
|
|
47389
|
-
});
|
|
47390
|
-
}
|
|
47391
|
-
}, {
|
|
47392
|
-
context: {},
|
|
47393
|
-
onComplete: async () => {},
|
|
47394
|
-
onTurn: async () => ({
|
|
47395
|
-
assistantText: "Copy that."
|
|
47396
|
-
}),
|
|
47397
|
-
session: createVoiceMemoryStore(),
|
|
47398
|
-
stt: createFakeSTTAdapter(receivedAudio, scenario.sttDelayMs ?? 0),
|
|
47399
|
-
tts: createFakeTTSAdapter(scenario.ttsChunkCount ?? 2, scenario.ttsChunkDelayMs ?? 0),
|
|
47400
|
-
turnDetection: {
|
|
47401
|
-
transcriptStabilityMs: 0
|
|
47402
|
-
}
|
|
47403
|
-
});
|
|
47404
|
-
const startedAt = Date.now();
|
|
47405
|
-
let secondInboundAt;
|
|
47406
|
-
try {
|
|
47407
|
-
await bridge.handleMessage({
|
|
47408
|
-
event: "start",
|
|
47409
|
-
start: {
|
|
47410
|
-
callSid: "CA-benchmark",
|
|
47411
|
-
customParameters: {
|
|
47412
|
-
scenarioId: scenario.id,
|
|
47413
|
-
sessionId: `phone-${scenario.id}`
|
|
47414
|
-
},
|
|
47415
|
-
streamSid: "MZ-benchmark"
|
|
47416
|
-
},
|
|
47417
|
-
streamSid: "MZ-benchmark"
|
|
47418
|
-
});
|
|
47419
|
-
await bridge.handleMessage({
|
|
47420
|
-
event: "media",
|
|
47421
|
-
media: {
|
|
47422
|
-
payload: encodeTwilioMulawBase64(new Int16Array([500, -500, 1500, -1500, 2500, -2500])),
|
|
47423
|
-
track: "inbound"
|
|
47424
|
-
},
|
|
47425
|
-
streamSid: "MZ-benchmark"
|
|
47426
|
-
});
|
|
47427
|
-
const sawOutboundMedia = await waitFor(() => sentEvents.some((entry) => entry.event.event === "media"), timeoutMs);
|
|
47428
|
-
if (scenario.expectClear) {
|
|
47429
|
-
if (scenario.secondInboundDelayMs) {
|
|
47430
|
-
await Bun.sleep(scenario.secondInboundDelayMs);
|
|
47431
|
-
}
|
|
47432
|
-
secondInboundAt = Date.now();
|
|
47433
|
-
await bridge.handleMessage({
|
|
47434
|
-
event: "media",
|
|
47435
|
-
media: {
|
|
47436
|
-
payload: encodeTwilioMulawBase64(new Int16Array([200, -200, 200, -200])),
|
|
47437
|
-
track: "inbound"
|
|
47438
|
-
},
|
|
47439
|
-
streamSid: "MZ-benchmark"
|
|
47440
|
-
});
|
|
47441
|
-
}
|
|
47442
|
-
await waitFor(() => (!scenario.expectOutboundMedia || sawOutboundMedia) && (!scenario.expectMark || sentEvents.some((entry) => entry.event.event === "mark")) && (!scenario.expectClear || sentEvents.some((entry) => entry.event.event === "clear")), timeoutMs);
|
|
47443
|
-
} finally {
|
|
47444
|
-
await bridge.close("telephony-benchmark");
|
|
47445
|
-
}
|
|
47446
|
-
const outboundMediaEvents = sentEvents.filter((entry) => entry.event.event === "media");
|
|
47447
|
-
const markEvents = sentEvents.filter((entry) => entry.event.event === "mark");
|
|
47448
|
-
const clearEvents = sentEvents.filter((entry) => entry.event.event === "clear");
|
|
47449
|
-
const firstOutboundMediaAt = outboundMediaEvents[0]?.at;
|
|
47450
|
-
const firstMarkAt = markEvents[0]?.at;
|
|
47451
|
-
const firstClearAt = clearEvents[0]?.at;
|
|
47452
|
-
const passes = (!scenario.expectOutboundMedia || outboundMediaEvents.length > 0) && (!scenario.expectMark || markEvents.length > 0) && (!scenario.expectClear || clearEvents.length > 0);
|
|
47453
|
-
return {
|
|
47454
|
-
clearCount: clearEvents.length,
|
|
47455
|
-
clearLatencyMs: secondInboundAt !== undefined && firstClearAt !== undefined ? firstClearAt - secondInboundAt : undefined,
|
|
47456
|
-
elapsedMs: Date.now() - startedAt,
|
|
47457
|
-
expectClear: scenario.expectClear,
|
|
47458
|
-
expectMark: scenario.expectMark,
|
|
47459
|
-
expectOutboundMedia: scenario.expectOutboundMedia,
|
|
47460
|
-
fixtureId: scenario.id,
|
|
47461
|
-
firstOutboundMediaLatencyMs: firstOutboundMediaAt !== undefined ? firstOutboundMediaAt - startedAt : undefined,
|
|
47462
|
-
markCount: markEvents.length,
|
|
47463
|
-
markLatencyMs: firstMarkAt !== undefined ? firstMarkAt - startedAt : undefined,
|
|
47464
|
-
outboundMediaCount: outboundMediaEvents.length,
|
|
47465
|
-
passes,
|
|
47466
|
-
receivedAudioBytes: receivedAudio.reduce((sum, chunk) => sum + chunk.byteLength, 0),
|
|
47467
|
-
title: scenario.title
|
|
47468
|
-
};
|
|
47469
|
-
};
|
|
47470
|
-
var summarizeVoiceTelephonyBenchmark = (fixtures) => {
|
|
47471
|
-
const scenarioCount = fixtures.length;
|
|
47472
|
-
const firstMediaSamples = fixtures.filter((fixture) => typeof fixture.firstOutboundMediaLatencyMs === "number");
|
|
47473
|
-
const markSamples = fixtures.filter((fixture) => typeof fixture.markLatencyMs === "number");
|
|
47474
|
-
const clearSamples = fixtures.filter((fixture) => typeof fixture.clearLatencyMs === "number");
|
|
47475
|
-
const passCount = fixtures.filter((fixture) => fixture.passes).length;
|
|
47476
|
-
return {
|
|
47477
|
-
averageClearLatencyMs: clearSamples.length > 0 ? clearSamples.reduce((sum, fixture) => sum + fixture.clearLatencyMs, 0) / clearSamples.length : undefined,
|
|
47478
|
-
averageElapsedMs: scenarioCount > 0 ? fixtures.reduce((sum, fixture) => sum + fixture.elapsedMs, 0) / scenarioCount : 0,
|
|
47479
|
-
averageFirstOutboundMediaLatencyMs: firstMediaSamples.length > 0 ? firstMediaSamples.reduce((sum, fixture) => sum + fixture.firstOutboundMediaLatencyMs, 0) / firstMediaSamples.length : undefined,
|
|
47480
|
-
averageMarkLatencyMs: markSamples.length > 0 ? markSamples.reduce((sum, fixture) => sum + fixture.markLatencyMs, 0) / markSamples.length : undefined,
|
|
47481
|
-
passCount,
|
|
47482
|
-
passRate: scenarioCount > 0 ? passCount / scenarioCount : 0,
|
|
47483
|
-
scenarioCount,
|
|
47484
|
-
totalOutboundMediaCount: fixtures.reduce((sum, fixture) => sum + fixture.outboundMediaCount, 0)
|
|
47485
|
-
};
|
|
47486
|
-
};
|
|
47487
|
-
var runVoiceTelephonyBenchmark = async (scenarios = getDefaultVoiceTelephonyBenchmarkScenarios(), options = {}) => {
|
|
47488
|
-
const fixtures = [];
|
|
47489
|
-
for (const scenario of scenarios) {
|
|
47490
|
-
fixtures.push(await runVoiceTelephonyBenchmarkScenario(scenario, options));
|
|
47491
|
-
}
|
|
47492
|
-
return {
|
|
47493
|
-
fixtures,
|
|
47494
|
-
generatedAt: Date.now(),
|
|
47495
|
-
summary: summarizeVoiceTelephonyBenchmark(fixtures)
|
|
47496
|
-
};
|
|
47497
|
-
};
|
|
47498
47079
|
// src/telephony/response.ts
|
|
47499
47080
|
var normalizeWhitespace = (value) => value.replace(/\s+/g, " ").trim();
|
|
47500
47081
|
var DEFAULT_MAX_WORDS = 12;
|
|
@@ -47564,22 +47145,27 @@ var DEFAULT_AUDIO_FORMAT = {
|
|
|
47564
47145
|
};
|
|
47565
47146
|
var DEFAULT_TELEPHONY_SAMPLE_RATE_HZ = 8000;
|
|
47566
47147
|
var DEFAULT_MULTI_SPEAKER_SILENCE_MS = 350;
|
|
47567
|
-
var FIXTURE_DIR_CANDIDATES = [
|
|
47568
|
-
resolve2(import.meta.dir, "..", "..", "fixtures"),
|
|
47569
|
-
resolve2(import.meta.dir, "..", "..", "..", "fixtures"),
|
|
47570
|
-
resolve2(import.meta.dir, "..", "..", "..", "..", "fixtures")
|
|
47571
|
-
];
|
|
47572
47148
|
var EXTERNAL_FIXTURE_ENV_KEYS = [
|
|
47573
47149
|
"VOICE_FIXTURE_DIR",
|
|
47574
47150
|
"VOICE_FIXTURE_DIRS"
|
|
47575
47151
|
];
|
|
47152
|
+
var resolveDefaultFixtureCandidates = () => {
|
|
47153
|
+
const envDirs = EXTERNAL_FIXTURE_ENV_KEYS.flatMap((key) => (process.env[key] ?? "").split(/[\n,]/).map((entry) => entry.trim()).filter((entry) => entry.length > 0));
|
|
47154
|
+
return [
|
|
47155
|
+
...envDirs.map((dir) => resolve2(dir)),
|
|
47156
|
+
resolve2(process.cwd(), "fixtures"),
|
|
47157
|
+
resolve2(process.cwd(), "test", "fixtures", "audio"),
|
|
47158
|
+
resolve2(import.meta.dir, "..", "..", "test", "fixtures", "audio"),
|
|
47159
|
+
resolve2(import.meta.dir, "..", "..", "..", "test", "fixtures", "audio")
|
|
47160
|
+
];
|
|
47161
|
+
};
|
|
47576
47162
|
var resolveFixtureDirectory = async () => {
|
|
47577
|
-
for (const candidate of
|
|
47163
|
+
for (const candidate of resolveDefaultFixtureCandidates()) {
|
|
47578
47164
|
if (await Bun.file(resolve2(candidate, "manifest.json")).exists()) {
|
|
47579
47165
|
return candidate;
|
|
47580
47166
|
}
|
|
47581
47167
|
}
|
|
47582
|
-
throw new Error("Unable to locate
|
|
47168
|
+
throw new Error("Unable to locate voice test fixtures. Set VOICE_FIXTURE_DIR/VOICE_FIXTURE_DIRS, or provide a fixtures/ directory (with manifest.json) in the working directory.");
|
|
47583
47169
|
};
|
|
47584
47170
|
var getVoiceFixtureDirectory = async () => resolveFixtureDirectory();
|
|
47585
47171
|
var toUniqueDirectories = (directories) => directories.filter((directory, index, list) => directory.trim().length > 0 && list.indexOf(directory) === index);
|
|
@@ -49610,10 +49196,18 @@ var createVoiceCallDispositionTagger = (options = {}) => {
|
|
|
49610
49196
|
};
|
|
49611
49197
|
// src/retryPolicy.ts
|
|
49612
49198
|
var DEFAULT_RULES = [
|
|
49613
|
-
{
|
|
49199
|
+
{
|
|
49200
|
+
action: "retry",
|
|
49201
|
+
cooldownMs: 4 * 60 * 60 * 1000,
|
|
49202
|
+
disposition: "voicemail-left"
|
|
49203
|
+
},
|
|
49614
49204
|
{ action: "retry", cooldownMs: 30 * 60 * 1000, disposition: "no-answer" },
|
|
49615
49205
|
{ action: "retry", cooldownMs: 10 * 60 * 1000, disposition: "busy" },
|
|
49616
|
-
{
|
|
49206
|
+
{
|
|
49207
|
+
action: "retry",
|
|
49208
|
+
cooldownMs: 24 * 60 * 60 * 1000,
|
|
49209
|
+
disposition: "callback-requested"
|
|
49210
|
+
},
|
|
49617
49211
|
{ action: "abandon", disposition: "do-not-call" },
|
|
49618
49212
|
{ action: "abandon", disposition: "not-interested" },
|
|
49619
49213
|
{ action: "abandon", disposition: "wrong-number" },
|
|
@@ -49874,7 +49468,10 @@ var createVoiceLiveCoach = (options) => {
|
|
|
49874
49468
|
const now = options.now ?? (() => Date.now());
|
|
49875
49469
|
const generateId = options.generateId ?? (() => `nudge_${Math.random().toString(36).slice(2, 10)}`);
|
|
49876
49470
|
const role = options.injectionRole ?? "system";
|
|
49877
|
-
const templates = {
|
|
49471
|
+
const templates = {
|
|
49472
|
+
...DEFAULT_TEMPLATES,
|
|
49473
|
+
...options.templateForKind ?? {}
|
|
49474
|
+
};
|
|
49878
49475
|
const nudges = [];
|
|
49879
49476
|
const listeners = new Set;
|
|
49880
49477
|
const push = (input) => {
|
|
@@ -50339,7 +49936,10 @@ var createVoiceBookingFlow = (options) => {
|
|
|
50339
49936
|
...options.maxSlotsPerDay !== undefined ? { maxSlots: options.maxSlotsPerDay } : {},
|
|
50340
49937
|
toMs: input.toMs
|
|
50341
49938
|
});
|
|
50342
|
-
setState({
|
|
49939
|
+
setState({
|
|
49940
|
+
proposedSlots: slots,
|
|
49941
|
+
step: slots.length > 0 ? "ask-time" : "ask-date"
|
|
49942
|
+
});
|
|
50343
49943
|
return slots;
|
|
50344
49944
|
};
|
|
50345
49945
|
const chooseSlot = (slotIndex) => {
|
|
@@ -51303,11 +50903,24 @@ var buildPendingActions = (state, slots, pathway, emit2) => {
|
|
|
51303
50903
|
}
|
|
51304
50904
|
if (action.kind === "transfer") {
|
|
51305
50905
|
emit2({ destination: action.destination, type: "transfer" });
|
|
51306
|
-
return {
|
|
50906
|
+
return {
|
|
50907
|
+
actions: pending,
|
|
50908
|
+
awaitingSlotId: null,
|
|
50909
|
+
ended: true,
|
|
50910
|
+
reason: `transfer:${action.destination}`
|
|
50911
|
+
};
|
|
51307
50912
|
}
|
|
51308
50913
|
if (action.kind === "end-call") {
|
|
51309
|
-
emit2({
|
|
51310
|
-
|
|
50914
|
+
emit2({
|
|
50915
|
+
...action.reason !== undefined ? { reason: action.reason } : {},
|
|
50916
|
+
type: "end-call"
|
|
50917
|
+
});
|
|
50918
|
+
return {
|
|
50919
|
+
actions: pending,
|
|
50920
|
+
awaitingSlotId: null,
|
|
50921
|
+
ended: true,
|
|
50922
|
+
...action.reason !== undefined ? { reason: action.reason } : {}
|
|
50923
|
+
};
|
|
51311
50924
|
}
|
|
51312
50925
|
}
|
|
51313
50926
|
return { actions: pending, awaitingSlotId, ended: false };
|
|
@@ -51336,7 +50949,11 @@ var createVoicePathwayRuntime = (options) => {
|
|
|
51336
50949
|
const enter = (stateId) => {
|
|
51337
50950
|
const target = findVoicePathwayState(options.pathway, stateId);
|
|
51338
50951
|
if (!target) {
|
|
51339
|
-
state = {
|
|
50952
|
+
state = {
|
|
50953
|
+
...state,
|
|
50954
|
+
lastError: `Unknown state ${stateId}`,
|
|
50955
|
+
status: "errored"
|
|
50956
|
+
};
|
|
51340
50957
|
emit2({ message: state.lastError, type: "errored" });
|
|
51341
50958
|
return;
|
|
51342
50959
|
}
|
|
@@ -51463,7 +51080,11 @@ var parseNumber = (raw, slot) => {
|
|
|
51463
51080
|
const min = slot.validation?.min ?? Number.NEGATIVE_INFINITY;
|
|
51464
51081
|
const max = slot.validation?.max ?? Number.POSITIVE_INFINITY;
|
|
51465
51082
|
if (value < min || value > max) {
|
|
51466
|
-
return {
|
|
51083
|
+
return {
|
|
51084
|
+
hint: `Expected ${min}\u2013${max}`,
|
|
51085
|
+
ok: false,
|
|
51086
|
+
reason: "out-of-range"
|
|
51087
|
+
};
|
|
51467
51088
|
}
|
|
51468
51089
|
return { normalized: String(value), ok: true, value };
|
|
51469
51090
|
};
|
|
@@ -51525,7 +51146,11 @@ var parseEmail = (raw) => {
|
|
|
51525
51146
|
const ok = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/iu.test(collapsed);
|
|
51526
51147
|
if (!ok)
|
|
51527
51148
|
return { ok: false, reason: "type-mismatch" };
|
|
51528
|
-
return {
|
|
51149
|
+
return {
|
|
51150
|
+
normalized: collapsed.toLowerCase(),
|
|
51151
|
+
ok: true,
|
|
51152
|
+
value: collapsed.toLowerCase()
|
|
51153
|
+
};
|
|
51529
51154
|
};
|
|
51530
51155
|
var parseCurrency = (raw, slot) => {
|
|
51531
51156
|
const trimmed = raw.trim().toLowerCase();
|
|
@@ -51538,7 +51163,11 @@ var parseCurrency = (raw, slot) => {
|
|
|
51538
51163
|
const min = slot.validation?.min ?? 0;
|
|
51539
51164
|
const max = slot.validation?.max ?? Number.POSITIVE_INFINITY;
|
|
51540
51165
|
if (value < min || value > max) {
|
|
51541
|
-
return {
|
|
51166
|
+
return {
|
|
51167
|
+
hint: `Expected ${min}\u2013${max}`,
|
|
51168
|
+
ok: false,
|
|
51169
|
+
reason: "out-of-range"
|
|
51170
|
+
};
|
|
51542
51171
|
}
|
|
51543
51172
|
return { normalized: value.toFixed(2), ok: true, value };
|
|
51544
51173
|
};
|
|
@@ -52153,7 +51782,6 @@ export {
|
|
|
52153
51782
|
summarizeVoiceTraceTimeline,
|
|
52154
51783
|
summarizeVoiceTraceSinkDeliveries,
|
|
52155
51784
|
summarizeVoiceTrace,
|
|
52156
|
-
summarizeVoiceTelephonyBenchmark,
|
|
52157
51785
|
summarizeVoiceSessions,
|
|
52158
51786
|
summarizeVoiceSessionReplay,
|
|
52159
51787
|
summarizeVoiceRoutingSessions,
|
|
@@ -52201,9 +51829,6 @@ export {
|
|
|
52201
51829
|
saveVoiceIncidentBundleArtifact,
|
|
52202
51830
|
runVoiceToolContractSuite,
|
|
52203
51831
|
runVoiceToolContract,
|
|
52204
|
-
runVoiceTelephonyMediaOperationsSmoke,
|
|
52205
|
-
runVoiceTelephonyBenchmarkScenario,
|
|
52206
|
-
runVoiceTelephonyBenchmark,
|
|
52207
51832
|
runVoiceSimulationSuite,
|
|
52208
51833
|
runVoiceSessionEvals,
|
|
52209
51834
|
runVoiceScenarioFixtureEvals,
|
|
@@ -52344,8 +51969,6 @@ export {
|
|
|
52344
51969
|
renderVoiceCompetitiveCoverageHTML,
|
|
52345
51970
|
renderVoiceCampaignsHTML,
|
|
52346
51971
|
renderVoiceCampaignObservabilityHTML,
|
|
52347
|
-
renderVoiceCallReviewMarkdown,
|
|
52348
|
-
renderVoiceCallReviewHTML,
|
|
52349
51972
|
renderVoiceCallDebuggerHTML,
|
|
52350
51973
|
renderVoiceBrowserMediaHTML,
|
|
52351
51974
|
renderVoiceBrowserCallProfileMarkdown,
|
|
@@ -52411,7 +52034,6 @@ export {
|
|
|
52411
52034
|
getVoiceCampaignDialerProofStatus,
|
|
52412
52035
|
getLatestVoiceTelephonyMediaReport,
|
|
52413
52036
|
getLatestVoiceBrowserMediaReport,
|
|
52414
|
-
getDefaultVoiceTelephonyBenchmarkScenarios,
|
|
52415
52037
|
generateVoicePathwayFromPrompt,
|
|
52416
52038
|
generateVoiceCalendarSlots,
|
|
52417
52039
|
fromVapiAssistantConfig,
|
|
@@ -52805,9 +52427,7 @@ export {
|
|
|
52805
52427
|
createVoiceCallingWindow,
|
|
52806
52428
|
createVoiceCallerMemoryNamespace,
|
|
52807
52429
|
createVoiceCallerCRMLinker,
|
|
52808
|
-
createVoiceCallReviewRecorder,
|
|
52809
52430
|
createVoiceCallReviewFromSession,
|
|
52810
|
-
createVoiceCallReviewFromLiveTelephonyReport,
|
|
52811
52431
|
createVoiceCallPlayer,
|
|
52812
52432
|
createVoiceCallDispositionTagger,
|
|
52813
52433
|
createVoiceCallDebuggerRoutes,
|