@kianwoon/modelweaver 0.3.19 → 0.3.20

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/index.js CHANGED
@@ -1,13 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import{a as ee}from"./chunk-OMWFRIHF.js";import{d as te}from"./chunk-Q3RNBHCF.js";import{b as ne,e as oe,g as D,k as re}from"./chunk-BXLGDD7N.js";import{createAdaptorServer as Te}from"@hono/node-server";import{readFileSync as rt}from"fs";import{Hono as Je}from"hono";var $=new Map;function se(){$.clear()}function Re(t,e){for(let[r,n]of e)for(let l of n)if(t.includes(l))return r;return null}function Se(t,e){return e.get(t)||[]}function ie(t,e,r,n){let l=$.get(t);if(l)return $.delete(t),$.set(t,l),{requestId:e,model:t,tier:l.tier,providerChain:l.providerChain,startTime:Date.now(),rawBody:n};let c,a,o=r.modelRouting.get(t);if(o&&o.length>0)c="(modelRouting)",a=o;else{let u=Re(t,r.tierPatterns);if(!u)return null;c=u,a=Se(c,r.routing)}if($.size>=200){let u=$.keys().next().value;u!==void 0&&$.delete(u)}return $.set(t,{tier:c,providerChain:a}),{requestId:e,model:t,tier:c,providerChain:a,startTime:Date.now(),rawBody:n}}import{request as Ie}from"undici";import{PassThrough as Ae}from"stream";import me from"fs";import fe from"path";import qe from"os";var H=class{samples=new Map;maxSize;MAX_PROVIDERS=50;constructor(e=20){this.maxSize=e}record(e,r){if(this.samples.size>=this.MAX_PROVIDERS&&!this.samples.has(e)){let l=this.samples.keys().next().value;l!==void 0&&this.samples.delete(l)}let n=this.samples.get(e);n||(n=[],this.samples.set(e,n)),n.push({ttfbMs:r,timestamp:Date.now()}),n.length>this.maxSize&&n.splice(0,n.length-this.maxSize)}getCV(e){let r=this.samples.get(e);if(!r||r.length<3)return 0;let n=r.map(a=>a.ttfbMs),l=n.reduce((a,o)=>a+o,0)/n.length;if(l===0)return 0;let c=n.reduce((a,o)=>a+(o-l)**2,0)/n.length;return Math.sqrt(c)/l}getStats(e){let r=this.samples.get(e);if(!r||r.length===0)return{count:0,mean:0,cv:0};let n=r.map(c=>c.ttfbMs),l=n.reduce((c,a)=>c+a,0)/n.length;return{count:n.length,mean:Math.round(l),cv:Math.round(this.getCV(e)*100)/100}}clear(e){this.samples.delete(e)}prune(e){let r=new Set(e);for(let n of this.samples.keys())r.has(n)||this.samples.delete(n)}},z=class{counts=new Map;increment(e){let r=(this.counts.get(e)??0)+1;return this.counts.set(e,r),r}decrement(e){let r=Math.max(0,(this.counts.get(e)??0)-1);return this.counts.set(e,r),r}get(e){return this.counts.get(e)??0}},L=new H,N=new z;function ae(t){let e=L.getCV(t.name),r=N.get(t.name),n=t.concurrentLimit??1,l=Math.max(1,n-r),c=Math.max(1,Math.floor(e*2+.5));return Math.min(c,l)}import{WebSocketServer as _e}from"ws";var Me=3e4,Ce=2,le=64*1024,xe=500,Ee=500,Pe=1e4,ce=new WeakMap,B=new WeakMap,J=null,de=0,ue=0;function pe(t){let e=Date.now();e-ue>=Pe&&(console.warn(`[ws] Backpressure: dropping ${t} events (total dropped stream events: ${de})`),ue=e)}function G(t,e){let r=new _e({server:t,path:"/ws"});J=r,r.on("connection",n=>{let c={type:"summary",data:e.getSummary()};n.send(JSON.stringify(c));let a,o=0,u=()=>n.readyState===n.OPEN,h=e.onRecord(m=>{if(u()){if(n.bufferedAmount>le){s(),pe("metrics");return}setImmediate(()=>{if(!u())return;let f={type:"request",data:m};n.send(JSON.stringify(f))}),s()}});function s(){a||(a=setTimeout(()=>{if(a=void 0,!u())return;let m={type:"summary",data:e.getSummary()};n.send(JSON.stringify(m))},xe))}let i=setInterval(()=>{if(!u()){clearInterval(i);return}if(o>=Ce){d(),n.terminate();return}n.ping(),o++},Me);n.on("pong",()=>{o=0});let p=!1,d=()=>{if(p)return;p=!0,clearInterval(i),a&&clearTimeout(a);let m=B.get(n);m&&(clearTimeout(m.timer),B.delete(n)),h()};n.on("close",d),n.on("error",d)})}function A(t){if(!J)return;let e=t.state==="streaming",r=t.state==="complete"||t.state==="error",n=Date.now(),l;for(let c of J.clients){if(c.readyState!==c.OPEN)continue;if(e){let o=ce.get(c)??0;if(n-o<Ee)continue;ce.set(c,n)}l||(l=JSON.stringify({type:"stream",data:t}));let a=l;if(c.bufferedAmount>le){if(r){let o=B.get(c);if(o)o.queue.push(a);else{let u=[a],h=()=>{if(B.delete(c),c.readyState===c.OPEN)for(let i of u)c.send(i)},s=setTimeout(()=>{if(B.delete(c),c.removeListener("drain",h),c.readyState===c.OPEN)for(let i of u)c.send(i)},5e3).unref();B.set(c,{timer:s,queue:u}),c.once("drain",h)}continue}de++,pe("stream");continue}setImmediate(()=>{c.readyState===c.OPEN&&c.send(a)})}}function ye(t){let e={...t};return Array.isArray(e.messages)&&(e.messages=[...e.messages]),e}var Oe=new Set(["anthropic-version","anthropic-beta","content-type","accept"]),$e=/\/+/g,Le=/^https?:\/\/[^/]+/,F=/"model"\s*:\s*"([^"]*)"/,W=/"max_tokens"\s*:\s*(\d+)/,O=new TextEncoder,Ne=3e3;function De(t){return t===429||t>=500}var Be=["context window","context_limit","token limit","prompt is too long","max tokens","input too large","too many tokens"];function je(t,e){if(t!==400&&t!==413)return!1;let r=e.toLowerCase();return Be.some(n=>r.includes(n))}function We(t,e){if(!je(t,e))return null;console.warn("[context-compact] Upstream context window limit detected");try{let n=fe.join(qe.homedir(),".claude","state");me.mkdirSync(n,{recursive:!0}),me.writeFileSync(fe.join(n,"context-compact-needed"),Date.now().toString())}catch{}let r=JSON.stringify({type:"error",error:{type:"invalid_request_error",message:"Context window limit reached. Run /compact to reduce conversation size, then retry."}});return new Response(r,{status:413,headers:{"content-type":"application/json"}})}function Ue(t,e){let r="",n=t,l=t.indexOf("/",t.indexOf("//")+2);l!==-1&&(n=t.substring(0,l),r=t.substring(l));let c="",a=e,o=e.indexOf("?");o!==-1&&(a=e.substring(0,o),c=e.substring(o));let u;return r.endsWith("/v1")&&a.startsWith("/v1")?u=r+a.substring(3):u=r+a,u=u.replace($e,"/"),n+u+c}function He(t,e,r){let n=new Headers;for(let c of Oe){let a=t.get(c);a&&n.set(c,a)}e.authType==="bearer"?n.set("Authorization",`Bearer ${e.apiKey}`):n.set("x-api-key",e.apiKey),n.set("x-request-id",r);let l=e._cachedHost;if(l)n.set("host",l);else try{let c=new URL(e.baseUrl);n.set("host",c.host)}catch{}return n}function we(t){let e=t.messages;if(!Array.isArray(e))return;let r=new Set,n=new Set;for(let u=0;u<e.length;u++){let h=e[u];if(Array.isArray(h.content))for(let s of h.content)s.type==="tool_use"&&s.id?r.add(String(s.id)):s.type==="tool_result"&&s.tool_use_id&&n.add(String(s.tool_use_id))}let l=new Set,c=new Set;for(let u of r)n.has(u)||l.add(u);for(let u of n)r.has(u)||c.add(u);if(l.size===0&&c.size===0)return;let a=!1,o=e.map(u=>{if(!Array.isArray(u.content))return u;let h=u.content.filter(s=>!(s.type==="tool_use"&&l.has(String(s.id))||s.type==="tool_result"&&c.has(String(s.tool_use_id))));return h.length<u.content.length?(a=!0,{...u,content:h}):u});a&&(t.messages=o)}function ze(t,e,r,n,l){if(l){let y=ye(n);if(e.model&&(y.model=e.model),we(y),r.modelLimits){let{maxOutputTokens:_}=r.modelLimits,b=typeof y.max_tokens=="number"?y.max_tokens:_;(y.max_tokens===void 0||b>_)&&(y.max_tokens=Math.min(b,_))}return JSON.stringify(y)}let c=!!(e.model&&n.model!==e.model),a=!1,o=!1,u=0;if(r.modelLimits){u=r.modelLimits.maxOutputTokens;let y=W.exec(t);y?a=parseInt(y[1],10)>u:typeof n.max_tokens!="number"&&(o=!0)}if(!c&&!a&&!o)return t;if(o){let y={...n};return e.model&&(y.model=e.model),y.max_tokens=u,JSON.stringify(y)}let h=[];c&&h.push(F.source),a&&h.push(W.source);let s=new RegExp(h.join("|"),"g"),i=c?`"model":"${e.model}"`:null,p=a?`"max_tokens":${u}`:null,d=n.model,m=!1;return t.replace(s,y=>i&&F.test(y)?(F.lastIndex=0,!m&&d&&(console.warn(`Routing override: ${d} -> ${e.model} via ${r.name}`),m=!0),i):p&&W.test(y)?(W.lastIndex=0,p):y)}async function ge(t,e,r,n,l,c=0){let a=n.url.replace(Le,"");e.model&&(r.actualModel=e.model);let o=Ue(t.baseUrl,a),u;if((n.headers.get("content-type")||"").includes("application/json"))try{let g=r.parsedBody??JSON.parse(r.rawBody),S=!1;e.model&&g.model!==e.model&&(S=!0);let M=c>0;if(M&&(S=!0),t.modelLimits){let{maxOutputTokens:T}=t.modelLimits,C=typeof g.max_tokens=="number"?g.max_tokens:T;(g.max_tokens===void 0||C>T)&&(S=!0)}if(S)if(c===0&&!M)u=ze(r.rawBody,e,t,g,!1);else{let T=ye(g);if(e.model){let C=T.model;T.model=e.model,C&&C!==e.model&&console.warn(`Routing override: ${C} -> ${e.model} via ${t.name}`)}if(M&&we(T),t.modelLimits){let{maxOutputTokens:C}=t.modelLimits,R=typeof T.max_tokens=="number"?T.max_tokens:C;(T.max_tokens===void 0||R>C)&&(T.max_tokens=Math.min(R,C))}u=JSON.stringify(T)}else u=r.rawBody}catch{u=r.rawBody}else u=r.rawBody;let s=He(n.headers,t,r.requestId);s.set("content-length",Buffer.byteLength(u,"utf-8").toString());let i=new AbortController,p=setTimeout(()=>i.abort(),t.timeout),d=t.ttfbTimeout??15e3,m=!1,f=null,y=new Promise((g,S)=>{f=setTimeout(()=>{m=!0,i.abort(),S(new Error(`TTFB timeout after ${d}ms`))},d)}),_,b,v,w;if(l){if(l.aborted){clearTimeout(p),f&&clearTimeout(f);let M=JSON.stringify({type:"error",error:{type:"overloaded_error",message:`Provider "${t.name}" cancelled by race winner`}});return new Response(M,{status:502,headers:{"content-type":"application/json","content-length":O.encode(M).byteLength.toString()}})}let g=l.getMaxListeners?.()??10;l.setMaxListeners?.(g+1);let S=()=>{clearTimeout(p),f&&clearTimeout(f),w&&clearTimeout(w),setImmediate(()=>{if(b&&!b.destroyed&&!b.readableEnded)try{b.destroy().catch?.(()=>{})}catch{}v&&!v.destroyed&&v.destroy(new Error("Cancelled"))})};l.addEventListener("abort",S,{once:!0}),_=()=>{l.removeEventListener("abort",S),l.setMaxListeners?.(l.getMaxListeners?.()-1)}}try{let g=await Promise.race([Ie(o,{method:"POST",headers:s,body:u,signal:i.signal,dispatcher:t._agent}),y]);if(f&&clearTimeout(f),b=g.body,b.on("error",()=>{clearTimeout(w)}),g.statusCode>=400){clearTimeout(p);let C=await g.body.text();return new Response(C,{status:g.statusCode,statusText:g.statusText,headers:g.headers})}let S=t.stallTimeout??3e4;v=new Ae;let M=`Body stalled: no data after ${S}ms`;if(w=setTimeout(()=>{A({requestId:r.requestId,model:String(r.actualModel??e.model??""),tier:"",state:"error",message:M,timestamp:Date.now()});try{(b?.destroy(new Error(M))).catch?.(()=>{})}catch{}v.destroy(new Error(M))},S),v.on("data",()=>{clearTimeout(w),w=setTimeout(()=>{A({requestId:r.requestId,model:String(r.actualModel??e.model??""),tier:"",state:"error",message:M,timestamp:Date.now()});try{(b?.destroy(new Error(M))).catch?.(()=>{})}catch{}v.destroy(new Error(M))},S)}),v.on("end",()=>{clearTimeout(w)}),v.on("error",()=>{clearTimeout(w);try{v.destroy()}catch{}}),!g.body||typeof g.body.pipe!="function"){let C=g.body?new ReadableStream({start(R){R.enqueue(new TextEncoder().encode(String(g.body))),R.close()}}):new ReadableStream({start(R){R.close()}});return v.end(),clearTimeout(w),new Response(C,{status:g.statusCode,headers:g.headers})}g.body.pipe(v);let T=new Response(v,{status:g.statusCode,headers:g.headers});return clearTimeout(p),T}catch(g){clearTimeout(p),f&&clearTimeout(f),w&&clearTimeout(w);let S=m?`Provider "${t.name}" timed out waiting for first byte after ${d}ms`:g instanceof DOMException&&g.name==="AbortError"?`Provider "${t.name}" timed out after ${t.timeout}ms`:`Provider "${t.name}" connection failed: ${g.message}`,M=JSON.stringify({type:"error",error:{type:"overloaded_error",message:S}});return new Response(M,{status:502,headers:{"content-type":"application/json","content-length":O.encode(M).byteLength.toString()}})}finally{_?.()}}async function he(t,e,r,n,l,c,a){let o=ae(t);if(o<=1){N.increment(t.name);let m=Date.now();try{let f=await ge(t,e,r,n,l,c);return L.record(t.name,Date.now()-m),f}finally{N.decrement(t.name)}}a?.warn("Hedging request",{requestId:r.requestId,provider:t.name,count:o,cv:Math.round(L.getCV(t.name)*100)/100,inFlight:N.get(t.name),maxConcurrent:t.concurrentLimit});let u=Date.now(),h=[],s=new AbortController;for(let m=0;m<o;m++){N.increment(t.name);let f=l?AbortSignal.any([l,s.signal]):s.signal;h.push(ge(t,e,r,n,f,c).finally(()=>N.decrement(t.name)))}let i=h.map((m,f)=>m.then(y=>({response:y,hedgeIndex:f}))),p=new Set,d=[];try{for(;p.size<i.length;){let m=i.filter((y,_)=>!p.has(_));if(m.length===0)break;let f=await Promise.race(m);if(p.add(f.hedgeIndex),t._circuitBreaker&&t._circuitBreaker.recordResult(f.response.status),f.response.status>=200&&f.response.status<300){L.record(t.name,Date.now()-u),s.abort();for(let y=0;y<i.length;y++)p.has(y)||(t._circuitBreaker&&t._circuitBreaker.recordResult(502),i[y].then(_=>{try{_.response.body?.cancel()}catch{}}));for(let y of d)try{y.body?.cancel()}catch{}return f.response}d.push(f.response)}s.abort();for(let m of d)try{m.body?.cancel()}catch{}return d[0]??new Response(JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${t.name}" all hedged requests failed`}}),{status:502,headers:{"content-type":"application/json"}})}catch{s.abort();for(let m of d)try{m.body?.cancel()}catch{}return d[0]??new Response(JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${t.name}" hedging failed`}}),{status:502,headers:{"content-type":"application/json"}})}}async function be(t,e,r,n,l,c){if(e.length<=1){let i=e[0],p=t.get(i.provider);if(!p){let m=JSON.stringify({type:"error",error:{type:"api_error",message:`Unknown provider: ${i.provider}`}});return new Response(m,{status:502,headers:{"content-type":"application/json","content-length":O.encode(m).byteLength.toString()}})}if(p._circuitBreaker&&!p._circuitBreaker.canProceed().allowed){c?.warn("Provider skipped by circuit breaker",{requestId:r.requestId,provider:i.provider});let f=JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${i.provider}" skipped by circuit breaker`}});return new Response(f,{status:502,headers:{"content-type":"application/json","content-length":O.encode(f).byteLength.toString()}})}return l?.(i.provider,0),await he(p,i,r,n,void 0,0,c)}let a=new AbortController,o=new Set,u=[];async function h(i){let p=e[i],d=t.get(p.provider);if(!d){let f=JSON.stringify({type:"error",error:{type:"api_error",message:`Unknown provider: ${p.provider}`}});return{response:new Response(f,{status:502,headers:{"content-type":"application/json","content-length":O.encode(f).byteLength.toString()}}),index:i}}let m;if(d._circuitBreaker){let f=d._circuitBreaker.canProceed();if(!f.allowed){c?.warn("Provider skipped by circuit breaker",{requestId:r.requestId,provider:p.provider});let y=JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${p.provider}" skipped by circuit breaker`}});return{response:new Response(y,{status:502,headers:{"content-type":"application/json","content-length":O.encode(y).byteLength.toString()}}),index:i}}m=f.probeId}l?.(p.provider,i);try{return{response:await he(d,p,r,n,a.signal,i,c),index:i}}catch{d._circuitBreaker&&d._circuitBreaker.recordResult(502,m);let f=JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${p.provider}" failed`}});return{response:new Response(f,{status:502,headers:{"content-type":"application/json","content-length":O.encode(f).byteLength.toString()}}),index:i}}}let s=[];for(let i=0;i<e.length;i++)i===0?s.push(h(0)):s.push(new Promise(p=>{setTimeout(()=>{if(a.signal.aborted){let d=JSON.stringify({type:"error",error:{type:"api_error",message:"Cancelled by race winner"}});p({response:new Response(d,{status:502,headers:{"content-type":"application/json"}}),index:i});return}h(i).then(p)},Ne)}));try{for(;o.size<s.length;){let p=s.filter((m,f)=>!o.has(f));if(p.length===0)break;let d=await Promise.race(p);if(o.add(d.index),d.response.status>=200&&d.response.status<300){a.abort();for(let m of u)try{m.response.body?.cancel()}catch{}return d.response}if(!De(d.response.status)){if(a.abort(),(d.response.status===400||d.response.status===413)&&d.response.body)try{let m=await d.response.text(),f=We(d.response.status,m);return f||new Response(m,{status:d.response.status,statusText:d.response.statusText,headers:d.response.headers})}catch{return d.response}return d.response}u.push(d)}if(a.abort(),u.length>0)return u[0].response;let i=JSON.stringify({type:"error",error:{type:"overloaded_error",message:"All providers failed"}});return new Response(i,{status:502,headers:{"content-type":"application/json","content-length":O.encode(i).byteLength.toString()}})}catch{a.abort();let i=JSON.stringify({type:"error",error:{type:"overloaded_error",message:"All providers failed"}});return new Response(i,{status:502,headers:{"content-type":"application/json","content-length":O.encode(i).byteLength.toString()}})}}import{randomUUID as Ge}from"crypto";import{gzip as Fe}from"zlib";import{promisify as Ke}from"util";var Ve=Ke(Fe),K={"claude-opus-4-6":2e5,"claude-sonnet-4-6":2e5,"claude-haiku-4-5-20251001":2e5,"claude-3-5-sonnet":2e5,"claude-3-5-haiku":2e5,"glm-4.7":128e3,"glm-5-turbo":128e3};function V(t){if(K[t])return K[t];for(let[e,r]of Object.entries(K))if(t.startsWith(e))return r;return 0}function X(t,e,r){let n=r+t+e;return n<=0?0:Math.round(t/n*1e3)/10}function Z(t,e,r,n,l){if(l<=0)return 0;let c=t+e+r+n;return Math.round(c/l*1e3)/10}function Q(t,e,r){return new Response(JSON.stringify({type:"error",error:{type:t,message:e}}),{status:502,headers:{"content-type":"application/json","x-request-id":r}})}function Xe(t){let e=t.message?.usage??t.usage;if(!e)return{inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0};let r=e.input_tokens??e.prompt_tokens??0,n=e.output_tokens??e.completion_tokens??0,l=e.cache_read_input_tokens??0,c=e.cache_creation_input_tokens??0;return{inputTokens:r,outputTokens:n,cacheReadTokens:l,cacheCreationTokens:c}}function Ze(t,e,r,n,l,c){let a=new TextDecoder,o={input:0,output:0,cacheRead:0,cacheCreation:0},u="",h="",s=4096,i=0,p=0,d=0,m=0,f="",y=null,_=250,b=0,v=!0,w="",g=100,S=R=>{for(let P of R.split(`
2
+ import{a as te}from"./chunk-OMWFRIHF.js";import{d as ne}from"./chunk-Q3RNBHCF.js";import{b as oe,e as re,g as B,k as se}from"./chunk-BXLGDD7N.js";import{createAdaptorServer as Me}from"@hono/node-server";import{readFileSync as ct}from"fs";import{Hono as Ve}from"hono";var $=new Map;function ie(){$.clear()}function Ce(t,e){for(let[r,n]of e)for(let l of n)if(t.includes(l))return r;return null}function xe(t,e){return e.get(t)||[]}function ae(t,e,r,n){let l=$.get(t);if(l)return $.delete(t),$.set(t,l),{requestId:e,model:t,tier:l.tier,providerChain:l.providerChain,startTime:Date.now(),rawBody:n};let a,s,o=r.modelRouting.get(t);if(o&&o.length>0)a="(modelRouting)",s=o;else{let u=Ce(t,r.tierPatterns);if(!u)return null;a=u,s=xe(a,r.routing)}if($.size>=200){let u=$.keys().next().value;u!==void 0&&$.delete(u)}return $.set(t,{tier:a,providerChain:s}),{requestId:e,model:t,tier:a,providerChain:s,startTime:Date.now(),rawBody:n}}import{request as $e}from"undici";import{PassThrough as De}from"stream";import fe from"fs";import ge from"path";import Le from"os";var z=class{samples=new Map;maxSize;MAX_PROVIDERS=50;constructor(e=20){this.maxSize=e}record(e,r){if(this.samples.size>=this.MAX_PROVIDERS&&!this.samples.has(e)){let l=this.samples.keys().next().value;l!==void 0&&this.samples.delete(l)}let n=this.samples.get(e);n||(n=[],this.samples.set(e,n)),n.push({ttfbMs:r,timestamp:Date.now()}),n.length>this.maxSize&&n.splice(0,n.length-this.maxSize)}getCV(e){let r=this.samples.get(e);if(!r||r.length<3)return 0;let n=r.map(s=>s.ttfbMs),l=n.reduce((s,o)=>s+o,0)/n.length;if(l===0)return 0;let a=n.reduce((s,o)=>s+(o-l)**2,0)/n.length;return Math.sqrt(a)/l}getStats(e){let r=this.samples.get(e);if(!r||r.length===0)return{count:0,mean:0,cv:0};let n=r.map(a=>a.ttfbMs),l=n.reduce((a,s)=>a+s,0)/n.length;return{count:n.length,mean:Math.round(l),cv:Math.round(this.getCV(e)*100)/100}}clear(e){this.samples.delete(e)}prune(e){let r=new Set(e);for(let n of this.samples.keys())r.has(n)||this.samples.delete(n)}},J=class{counts=new Map;increment(e){let r=(this.counts.get(e)??0)+1;return this.counts.set(e,r),r}decrement(e){let r=Math.max(0,(this.counts.get(e)??0)-1);return this.counts.set(e,r),r}get(e){return this.counts.get(e)??0}},D=new z,L=new J;function ce(t){let e=D.getCV(t.name),r=L.get(t.name),n=t.concurrentLimit??1,l=Math.max(1,n-r),a=Math.max(1,Math.floor(e*2+.5));return Math.min(a,l)}import{WebSocketServer as Ee}from"ws";var Pe=3e4,Ie=2,de=64*1024,Ae=500,Oe=500,qe=1e4,ue=new WeakMap,j=new WeakMap,F=null,pe=0,le=0;function me(t){let e=Date.now();e-le>=qe&&(console.warn(`[ws] Backpressure: dropping ${t} events (total dropped stream events: ${pe})`),le=e)}function G(t,e){let r=new Ee({server:t,path:"/ws"});F=r,r.on("connection",n=>{let a={type:"summary",data:e.getSummary()};n.send(JSON.stringify(a));let s,o=0,u=()=>n.readyState===n.OPEN,m=e.onRecord(f=>{if(u()){if(n.bufferedAmount>de){c(),me("metrics");return}setImmediate(()=>{if(!u())return;let h={type:"request",data:f};n.send(JSON.stringify(h))}),c()}});function c(){s||(s=setTimeout(()=>{if(s=void 0,!u())return;let f={type:"summary",data:e.getSummary()};n.send(JSON.stringify(f))},Ae))}let i=setInterval(()=>{if(!u()){clearInterval(i);return}if(o>=Ie){d(),n.terminate();return}n.ping(),o++},Pe);n.on("pong",()=>{o=0});let p=!1,d=()=>{if(p)return;p=!0,clearInterval(i),s&&clearTimeout(s);let f=j.get(n);f&&(clearTimeout(f.timer),j.delete(n)),m()};n.on("close",d),n.on("error",d)})}function q(t){if(!F)return;let e=t.state==="streaming",r=t.state==="complete"||t.state==="error",n=Date.now(),l;for(let a of F.clients){if(a.readyState!==a.OPEN)continue;if(e){let o=ue.get(a)??0;if(n-o<Oe)continue;ue.set(a,n)}l||(l=JSON.stringify({type:"stream",data:t}));let s=l;if(a.bufferedAmount>de){if(r){let o=j.get(a);if(o)o.queue.push(s);else{let u=[s],m=()=>{if(j.delete(a),a.readyState===a.OPEN)for(let i of u)a.send(i)},c=setTimeout(()=>{if(j.delete(a),a.removeListener("drain",m),a.readyState===a.OPEN)for(let i of u)a.send(i)},5e3).unref();j.set(a,{timer:c,queue:u}),a.once("drain",m)}continue}pe++,me("stream");continue}setImmediate(()=>{a.readyState===a.OPEN&&a.send(s)})}}function ve(t){let e={...t};return Array.isArray(e.messages)&&(e.messages=[...e.messages]),e}var Ne=new Set(["anthropic-version","anthropic-beta","content-type","accept"]),Be=/\/+/g,je=/^https?:\/\/[^/]+/,K=/"model"\s*:\s*"([^"]*)"/,W=/"max_tokens"\s*:\s*(\d+)/,N=new TextEncoder,ke=Object.freeze({"content-type":"application/json"});function he(t){let e=JSON.stringify({type:"error",error:{type:"api_error",message:`Unknown provider: ${t}`}});return new Response(e,{status:502,headers:{...ke,"content-length":String(N.encode(e).byteLength)}})}function ye(t){let e=JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${t}" skipped by circuit breaker`}});return new Response(e,{status:502,headers:{...ke,"content-length":String(N.encode(e).byteLength)}})}var We=3e3;function Ue(t){return t===429||t>=500}var He=["context window","context_limit","token limit","prompt is too long","max tokens","input too large","too many tokens"];function ze(t,e){if(t!==400&&t!==413)return!1;let r=e.toLowerCase();return He.some(n=>r.includes(n))}function Je(t,e){if(!ze(t,e))return null;console.warn("[context-compact] Upstream context window limit detected");try{let n=ge.join(Le.homedir(),".claude","state");fe.mkdirSync(n,{recursive:!0}),fe.writeFileSync(ge.join(n,"context-compact-needed"),Date.now().toString())}catch{}let r=JSON.stringify({type:"error",error:{type:"invalid_request_error",message:"Context window limit reached. Run /compact to reduce conversation size, then retry."}});return new Response(r,{status:413,headers:{"content-type":"application/json"}})}function Fe(t,e){let r,n,l;typeof t=="object"&&t!==null?(r=t.baseUrl,n=t._cachedOrigin,l=t._cachedPathname):r=t;let a="",s=r;if(n&&l!==void 0)s=n,a=l;else{let i=r.indexOf("/",r.indexOf("//")+2);i!==-1&&(s=r.substring(0,i),a=r.substring(i))}let o="",u=e,m=e.indexOf("?");m!==-1&&(u=e.substring(0,m),o=e.substring(m));let c;return a.endsWith("/v1")&&u.startsWith("/v1")?c=a+u.substring(3):c=a+u,c=c.replace(Be,"/"),s+c+o}function Ge(t,e,r){let n=new Headers;for(let a of Ne){let s=t.get(a);s&&n.set(a,s)}e.authType==="bearer"?n.set("Authorization",`Bearer ${e.apiKey}`):n.set("x-api-key",e.apiKey),n.set("x-request-id",r);let l=e._cachedHost;if(l)n.set("host",l);else try{let a=new URL(e.baseUrl);n.set("host",a.host)}catch{}return n}function Re(t){let e=t.messages;if(!Array.isArray(e))return;let r=!1;for(let s=0;s<e.length;s++){let o=e[s];if(Array.isArray(o.content)){for(let u of o.content)if(u.type==="tool_use"||u.type==="tool_result"){r=!0;break}if(r)break}}if(!r)return;let n=new Set,l=new Set;for(let s=0;s<e.length;s++){let o=e[s];if(Array.isArray(o.content))for(let u of o.content)u.type==="tool_use"&&u.id?n.add(String(u.id)):u.type==="tool_result"&&u.tool_use_id&&l.add(String(u.tool_use_id))}let a=!1;for(let s of n)if(!l.has(s)){a=!0;break}if(!a){for(let s of l)if(!n.has(s)){a=!0;break}}if(a)for(let s=0;s<e.length;s++){let o=e[s];if(!Array.isArray(o.content))continue;let u=0;for(let m of o.content)m.type==="tool_use"&&!l.has(String(m.id))||m.type==="tool_result"&&!n.has(String(m.tool_use_id))||(o.content[u]=m,u++);u<o.content.length&&(o.content.length=u)}}function Ke(t,e,r,n,l){if(l){let b=ve(n);if(e.model&&(b.model=e.model),Re(b),r.modelLimits){let{maxOutputTokens:S}=r.modelLimits,w=typeof b.max_tokens=="number"?b.max_tokens:S;(b.max_tokens===void 0||w>S)&&(b.max_tokens=Math.min(w,S))}return JSON.stringify(b)}let a=!!(e.model&&n.model!==e.model),s=!1,o=!1,u=0;if(r.modelLimits){u=r.modelLimits.maxOutputTokens;let b=W.exec(t);b?s=parseInt(b[1],10)>u:typeof n.max_tokens!="number"&&(o=!0)}if(!a&&!s&&!o)return t;if(o){let b={...n};return e.model&&(b.model=e.model),b.max_tokens=u,JSON.stringify(b)}let m=[];a&&m.push(K.source),s&&m.push(W.source);let c=new RegExp(m.join("|"),"g"),i=a?`"model":"${e.model}"`:null,p=s?`"max_tokens":${u}`:null,d=n.model,f=!1;return t.replace(c,b=>i&&K.test(b)?(K.lastIndex=0,!f&&d&&(console.warn(`Routing override: ${d} -> ${e.model} via ${r.name}`),f=!0),i):p&&W.test(b)?(W.lastIndex=0,p):b)}async function be(t,e,r,n,l,a=0){let s=n.url.replace(je,"");e.model&&(r.actualModel=e.model);let o=Fe(t,s),u;if((n.headers.get("content-type")||"").includes("application/json"))try{let g=r.parsedBody??JSON.parse(r.rawBody),R=!1;e.model&&g.model!==e.model&&(R=!0);let x=a>0;if(x&&(R=!0),t.modelLimits){let{maxOutputTokens:k}=t.modelLimits,M=typeof g.max_tokens=="number"?g.max_tokens:k;(g.max_tokens===void 0||M>k)&&(R=!0)}if(R)if(a===0&&!x)u=Ke(r.rawBody,e,t,g,!1);else{let k=ve(g);if(e.model){let M=k.model;k.model=e.model,M&&M!==e.model&&console.warn(`Routing override: ${M} -> ${e.model} via ${t.name}`)}if(x&&Re(k),t.modelLimits){let{maxOutputTokens:M}=t.modelLimits,_=typeof k.max_tokens=="number"?k.max_tokens:M;(k.max_tokens===void 0||_>M)&&(k.max_tokens=Math.min(_,M))}u=JSON.stringify(k)}else u=r.rawBody}catch{u=r.rawBody}else u=r.rawBody;let c=Ge(n.headers,t,r.requestId);c.set("content-length",Buffer.byteLength(u,"utf-8").toString());let i=new AbortController,p=setTimeout(()=>i.abort(),t.timeout),d=t.ttfbTimeout??15e3,f=!1,h=null,b=new Promise((g,R)=>{h=setTimeout(()=>{f=!0,i.abort(),R(new Error(`TTFB timeout after ${d}ms`))},d)}),S,w,v,y;if(l){if(l.aborted){clearTimeout(p),h&&clearTimeout(h);let x=JSON.stringify({type:"error",error:{type:"overloaded_error",message:`Provider "${t.name}" cancelled by race winner`}});return new Response(x,{status:502,headers:{"content-type":"application/json","content-length":N.encode(x).byteLength.toString()}})}let g=l.getMaxListeners?.()??10;l.setMaxListeners?.(g+1);let R=()=>{clearTimeout(p),h&&clearTimeout(h),y&&clearInterval(y),setImmediate(()=>{if(w&&!w.destroyed&&!w.readableEnded)try{w.destroy().catch?.(()=>{})}catch{}v&&!v.destroyed&&v.destroy(new Error("Cancelled"))})};l.addEventListener("abort",R,{once:!0}),S=()=>{l.removeEventListener("abort",R),l.setMaxListeners?.(l.getMaxListeners?.()-1)}}try{let g=await Promise.race([$e(o,{method:"POST",headers:c,body:u,signal:i.signal,dispatcher:t._agent}),b]);if(h&&clearTimeout(h),w=g.body,w.on("error",()=>{y&&clearInterval(y)}),g.statusCode>=400){clearTimeout(p);let C=await g.body.text();return new Response(C,{status:g.statusCode,statusText:g.statusText,headers:g.headers})}let R=t.stallTimeout??3e4;v=new De;let x=`Body stalled: no data after ${R}ms`,k=Date.now(),M=()=>{q({requestId:r.requestId,model:String(r.actualModel??e.model??""),tier:"",state:"error",message:x,timestamp:Date.now()});try{(w?.destroy(new Error(x))).catch?.(()=>{})}catch{}v.destroy(new Error(x))};if(y=setInterval(()=>{Date.now()-k>=R&&(clearInterval(y),y=void 0,M())},Math.min(R/4,5e3)),v.on("data",()=>{k=Date.now()}),v.on("end",()=>{y&&(clearInterval(y),y=void 0)}),v.on("error",()=>{y&&(clearInterval(y),y=void 0);try{v.destroy()}catch{}}),!g.body||typeof g.body.pipe!="function"){let C=g.body?new ReadableStream({start(T){T.enqueue(new TextEncoder().encode(String(g.body))),T.close()}}):new ReadableStream({start(T){T.close()}});return v.end(),y&&clearInterval(y),new Response(C,{status:g.statusCode,headers:g.headers})}g.body.pipe(v);let _=new Response(v,{status:g.statusCode,headers:g.headers});return clearTimeout(p),_}catch(g){clearTimeout(p),h&&clearTimeout(h),y&&clearInterval(y);let R=f?`Provider "${t.name}" timed out waiting for first byte after ${d}ms`:g instanceof DOMException&&g.name==="AbortError"?`Provider "${t.name}" timed out after ${t.timeout}ms`:`Provider "${t.name}" connection failed: ${g.message}`,x=JSON.stringify({type:"error",error:{type:"overloaded_error",message:R}});return new Response(x,{status:502,headers:{"content-type":"application/json","content-length":N.encode(x).byteLength.toString()}})}finally{S?.()}}async function we(t,e,r,n,l,a,s){let o=ce(t);if(o<=1){L.increment(t.name);let f=Date.now();try{let h=await be(t,e,r,n,l,a);return D.record(t.name,Date.now()-f),h}finally{L.decrement(t.name)}}s?.warn("Hedging request",{requestId:r.requestId,provider:t.name,count:o,cv:Math.round(D.getCV(t.name)*100)/100,inFlight:L.get(t.name),maxConcurrent:t.concurrentLimit});let u=Date.now(),m=[],c=new AbortController;for(let f=0;f<o;f++){L.increment(t.name);let h=l?AbortSignal.any([l,c.signal]):c.signal;m.push(be(t,e,r,n,h,a).finally(()=>L.decrement(t.name)))}let i=m.map((f,h)=>f.then(b=>({response:b,hedgeIndex:h}))),p=new Set,d=[];try{for(;p.size<i.length;){let f=i.filter((b,S)=>!p.has(S));if(f.length===0)break;let h=await Promise.race(f);if(p.add(h.hedgeIndex),t._circuitBreaker&&t._circuitBreaker.recordResult(h.response.status),h.response.status>=200&&h.response.status<300){D.record(t.name,Date.now()-u),c.abort();for(let b=0;b<i.length;b++)p.has(b)||(t._circuitBreaker&&t._circuitBreaker.recordResult(502),i[b].then(S=>{try{S.response.body?.cancel()}catch{}}));for(let b of d)try{b.body?.cancel()}catch{}return h.response}d.push(h.response)}c.abort();for(let f of d)try{f.body?.cancel()}catch{}return d[0]??new Response(JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${t.name}" all hedged requests failed`}}),{status:502,headers:{"content-type":"application/json"}})}catch{c.abort();for(let f of d)try{f.body?.cancel()}catch{}return d[0]??new Response(JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${t.name}" hedging failed`}}),{status:502,headers:{"content-type":"application/json"}})}}async function Te(t,e,r,n,l,a){if(e.length<=1){let i=e[0],p=t.get(i.provider);return p?p._circuitBreaker&&!p._circuitBreaker.canProceed().allowed?(a?.warn("Provider skipped by circuit breaker",{requestId:r.requestId,provider:i.provider}),ye(i.provider)):(l?.(i.provider,0),await we(p,i,r,n,void 0,0,a)):he(i.provider)}let s=new AbortController,o=new Set,u=[];async function m(i){let p=e[i],d=t.get(p.provider);if(!d)return{response:he(p.provider),index:i};let f;if(d._circuitBreaker){let h=d._circuitBreaker.canProceed();if(!h.allowed)return a?.warn("Provider skipped by circuit breaker",{requestId:r.requestId,provider:p.provider}),{response:ye(p.provider),index:i};f=h.probeId}l?.(p.provider,i);try{return{response:await we(d,p,r,n,s.signal,i,a),index:i}}catch{d._circuitBreaker&&d._circuitBreaker.recordResult(502,f);let h=JSON.stringify({type:"error",error:{type:"api_error",message:`Provider "${p.provider}" failed`}});return{response:new Response(h,{status:502,headers:{"content-type":"application/json","content-length":N.encode(h).byteLength.toString()}}),index:i}}}let c=[];for(let i=0;i<e.length;i++)i===0?c.push(m(0)):c.push(new Promise(p=>{setTimeout(()=>{if(s.signal.aborted){let d=JSON.stringify({type:"error",error:{type:"api_error",message:"Cancelled by race winner"}});p({response:new Response(d,{status:502,headers:{"content-type":"application/json"}}),index:i});return}m(i).then(p)},We)}));try{for(;o.size<c.length;){let p=c.filter((f,h)=>!o.has(h));if(p.length===0)break;let d=await Promise.race(p);if(o.add(d.index),d.response.status>=200&&d.response.status<300){s.abort();for(let f of u)try{f.response.body?.cancel()}catch{}return d.response}if(!Ue(d.response.status)){if(s.abort(),(d.response.status===400||d.response.status===413)&&d.response.body)try{let f=await d.response.text(),h=Je(d.response.status,f);return h||new Response(f,{status:d.response.status,statusText:d.response.statusText,headers:d.response.headers})}catch{return d.response}return d.response}u.push(d)}if(s.abort(),u.length>0)return u[0].response;let i=JSON.stringify({type:"error",error:{type:"overloaded_error",message:"All providers failed"}});return new Response(i,{status:502,headers:{"content-type":"application/json","content-length":N.encode(i).byteLength.toString()}})}catch{s.abort();let i=JSON.stringify({type:"error",error:{type:"overloaded_error",message:"All providers failed"}});return new Response(i,{status:502,headers:{"content-type":"application/json","content-length":N.encode(i).byteLength.toString()}})}}import{randomUUID as Xe}from"crypto";import{gzip as Ze}from"zlib";import{promisify as Qe}from"util";var Ye=Qe(Ze),V={"claude-opus-4-6":2e5,"claude-sonnet-4-6":2e5,"claude-haiku-4-5-20251001":2e5,"claude-3-5-sonnet":2e5,"claude-3-5-haiku":2e5,"glm-4.7":128e3,"glm-5-turbo":128e3};function X(t){if(V[t])return V[t];for(let[e,r]of Object.entries(V))if(t.startsWith(e))return r;return 0}function Z(t,e,r){let n=r+t+e;return n<=0?0:Math.round(t/n*1e3)/10}function Q(t,e,r,n,l){if(l<=0)return 0;let a=t+e+r+n;return Math.round(a/l*1e3)/10}function Y(t,e,r){return new Response(JSON.stringify({type:"error",error:{type:t,message:e}}),{status:502,headers:{"content-type":"application/json","x-request-id":r}})}function et(t){let e=t.message?.usage??t.usage;if(!e)return{inputTokens:0,outputTokens:0,cacheReadTokens:0,cacheCreationTokens:0};let r=e.input_tokens??e.prompt_tokens??0,n=e.output_tokens??e.completion_tokens??0,l=e.cache_read_input_tokens??0,a=e.cache_creation_input_tokens??0;return{inputTokens:r,outputTokens:n,cacheReadTokens:l,cacheCreationTokens:a}}function tt(t,e,r,n,l,a){let s=new TextDecoder,o={input:0,output:0,cacheRead:0,cacheCreation:0},u="",m="",c=4096,i=0,p=0,d=0,f=0,h="",b=null,S=250,w=0,v=!0,y="",g=100,R=_=>{for(let C of _.split(`
3
3
 
4
- `)){if(!P)continue;let E=P.split(`
5
- `).find(x=>x.startsWith("data:"));if(E)try{let x=JSON.parse(E.slice(5));if(E.includes('"usage"')){let k=Xe(x);k.inputTokens>o.input&&(o.input=k.inputTokens),k.outputTokens>o.output&&(o.output=k.outputTokens),k.cacheReadTokens>o.cacheRead&&(o.cacheRead=k.cacheReadTokens),k.cacheCreationTokens>o.cacheCreation&&(o.cacheCreation=k.cacheCreationTokens)}let I=x.delta;I&&typeof I.text=="string"&&(w+=I.text,w.length>g&&(w=w.slice(-g)));let q=x.choices;if(q?.[0]){let k=q[0].delta;k&&typeof k.content=="string"&&(w+=k.content,w.length>g&&(w=w.slice(-g)))}}catch{}}},M=R=>{if(!R.includes('"usage"')){let k=[...R.matchAll(/"text"\s*:\s*"((?:[^"\\]|\\.)*)"/g)];if(k.length>0){let j=k[k.length-1][1].replace(/\\n/g,`
6
- `).replace(/\\"/g,'"').replace(/\\\\/g,"\\");w+=j,w.length>g&&(w=w.slice(-g))}return}let P=[...R.matchAll(/"(?:input_tokens|prompt_tokens)"\s*:\s*(\d+)/g)],E=[...R.matchAll(/"cache_read_input_tokens"\s*:\s*(\d+)/g)],x=[...R.matchAll(/"cache_creation_input_tokens"\s*:\s*(\d+)/g)],I=[...R.matchAll(/"(?:output_tokens|completion_tokens)"\s*:\s*(\d+)/g)];if(P.length>0){let k=parseInt(P[P.length-1][1],10);k>i&&(i=k)}if(E.length>0){let k=parseInt(E[E.length-1][1],10);k>p&&(p=k)}if(x.length>0){let k=parseInt(x[x.length-1][1],10);k>d&&(d=k)}if(I.length>0){let k=parseInt(I[I.length-1][1],10);k>m&&(m=k)}let q=[...R.matchAll(/"text"\s*:\s*"((?:[^"\\]|\\.)*)"/g)];if(q.length>0){let k=q[q.length-1][1].replace(/\\n/g,`
7
- `).replace(/\\"/g,'"').replace(/\\\\/g,"\\");w+=k,w.length>g&&(w=w.slice(-g))}},T=(R,P,E=0,x=0)=>{try{let I=Date.now()-t.startTime,q=I/1e3,k=q>0?P/q:0;n.recordRequest({requestId:t.requestId,model:t.model,actualModel:t.actualModel||t.model,tier:t.tier,provider:e,targetProvider:r,status:l,inputTokens:R,outputTokens:P,latencyMs:I,tokensPerSec:Math.round(k*10)/10,timestamp:Date.now(),fallbackMode:t.fallbackMode,cacheReadTokens:E,cacheCreationTokens:x});let j=V(t.actualModel||t.model);setImmediate(()=>{A({requestId:t.requestId,model:t.model,tier:t.tier,state:"complete",status:l,latencyMs:Date.now()-t.startTime,inputTokens:R,outputTokens:P,tokensPerSec:Math.round(k*10)/10,timestamp:Date.now(),cacheReadTokens:E,cacheCreationTokens:x,cacheHitRate:X(E,x,R),contextPercent:Z(R,E,x,P,j),contextWindowSize:j||void 0})})}catch{}},C=(R,P)=>{if(y===null&&(y=c.includes("text/event-stream")||R.startsWith("event:")),y){u+=R;let E=u.split(`
8
- `);u=E.pop();for(let I of E)I===""?h&&(S(h),h=""):h+=(h?`
9
- `:"")+I;P&&h.trim()&&S(h);let x=Date.now();if(v||x-b>=_){b=x,v=!1;let I=V(t.actualModel||t.model);setImmediate(()=>{A({requestId:t.requestId,model:t.model,tier:t.tier,state:"streaming",outputTokens:o.output,timestamp:x,preview:w,cacheHitRate:X(o.cacheRead,o.cacheCreation,o.input),contextPercent:Z(o.input,o.cacheRead,o.cacheCreation,o.output,I),contextWindowSize:I||void 0})})}P&&T(o.input,o.output,o.cacheRead,o.cacheCreation)}else{f+=R,f.length>s&&(f=f.slice(-s)),M(f);let E=Date.now();if(v||E-b>=_){b=E,v=!1;let x=V(t.actualModel||t.model);setImmediate(()=>{A({requestId:t.requestId,model:t.model,tier:t.tier,state:"streaming",outputTokens:m,timestamp:E,preview:w,cacheHitRate:X(p,d,i),contextPercent:Z(i,p,d,m,x),contextWindowSize:x||void 0})})}P&&T(i,m,p,d)}};return new TransformStream({transform(R,P){P.enqueue(R),C(a.decode(R,{stream:!0}),!1)},flush(){C("",!0)}})}function ve(t){let e=t._cachedOrigin,r=t.poolSize??10;return`${e??"unknown"}:${r}`}function Y(t,e,r){let n=t,l=ee(e),c=new Je;return c.onError((a,o)=>(console.error(`[server] Unhandled error: ${a.message}`),o.json({type:"error",error:{type:"api_error",message:"Internal proxy error"}},{status:500,headers:{"content-type":"application/json"}}))),c.use("/api/*",async(a,o)=>{a.header("Access-Control-Allow-Origin","*"),await o()}),c.options("/api/*",a=>(a.header("Access-Control-Allow-Origin","*"),a.header("Access-Control-Allow-Methods","GET, POST, OPTIONS"),a.header("Access-Control-Allow-Headers","Content-Type, Authorization, anthropic-version, x-api-key"),a.body("",200))),c.post("/v1/messages",async a=>{let o=Ge(),u,h;try{h=await a.req.text(),u=JSON.parse(h)}catch{return Q("invalid_request_error","Invalid JSON body",o)}let s=u.model;if(!s)return Q("invalid_request_error","Missing 'model' field in request body",o);let i=ie(s,o,n,h);if(i&&(i.parsedBody=u),!i){l.info("No tier match",{requestId:o,model:s});let b=n.modelRouting.size>0?` Configured model routes: ${[...n.modelRouting.keys()].join(", ")}.`:"";return Q("invalid_request_error",`No route matches model "${s}". Configured tiers: ${[...n.tierPatterns.keys()].join(", ")}.${b}`,o)}l.info("Routing request",{requestId:o,model:s,tier:i.tier,providers:i.providerChain.map(b=>b.provider)}),A({requestId:o,model:s,tier:i.tier,state:"start",provider:i.providerChain[0]?.provider??"unknown",timestamp:Date.now()});let p="unknown",d;try{if(d=await be(n.providers,i.providerChain,i,a.req.raw,(b,v)=>{l.info("Attempting provider",{requestId:o,provider:b,index:v,tier:i.tier}),p||(p=b)},l),d.status<400){let b=17;d.headers.forEach((v,w)=>{b+=w.length+v.length+4}),b+=2,setImmediate(()=>{A({requestId:o,model:s,tier:i.tier,state:"ttfb",status:d.status,headerSize:b,timestamp:Date.now()})})}}catch(b){let v=b instanceof Error?b.message:String(b);return l.error("Forward failed",{requestId:o,error:v}),setImmediate(()=>{A({requestId:o,model:s,tier:i.tier,state:"error",status:502,message:v,timestamp:Date.now()})}),a.json({type:"error",error:{type:"api_error",message:"Upstream request failed: "+v}},502)}d.status>=400&&setImmediate(()=>{A({requestId:o,model:s,tier:i.tier,state:"error",status:d.status,message:`HTTP ${d.status}`,timestamp:Date.now()})});let m=d.body;if(d.body&&d.status>=200&&d.status<300&&r){let b=i.providerChain.length>0?i.providerChain[0].provider:p,v=Ze(i,p,b,r,d.status,d.headers.get("content-type")||"");m=d.body.pipeThrough(v)}let f=new Headers(d.headers);f.set("x-request-id",o);let y=new Response(m,{status:d.status,statusText:d.statusText,headers:f}),_=Date.now()-i.startTime;return l.info("Request completed",{requestId:o,model:s,tier:i.tier,status:y.status,latencyMs:_}),y}),c.get("/api/metrics/summary",async a=>{if(!r)return a.json({error:"Metrics not enabled"},503);let o=r.getSummary(),u=JSON.stringify(o);if((a.req.header("accept-encoding")||"").includes("gzip")&&u.length>=1024){let s=await Ve(Buffer.from(u));return new Response(s,{status:200,headers:{"content-type":"application/json","content-encoding":"gzip",vary:"accept-encoding"}})}return a.json(o)}),c.get("/api/circuit-breaker",a=>{let o={};for(let[u,h]of n.providers){let s=h._circuitBreaker;if(s){let i=s.getStatus();o[u]={state:i.state,failures:i.failures,lastFailure:i.lastFailure?new Date(i.lastFailure).toISOString():null}}}return a.json(o)}),{app:c,getConfig:()=>n,setConfig:async a=>{let o=new Map;for(let s of n.providers.values())s._agent&&o.set(ve(s),s._agent);let u=new Set;for(let s of a.providers.values()){let i=ve(s),p=o.get(i);p&&(s._agent=p,u.add(i))}let h=[];for(let[s,i]of o)u.has(s)||h.push(i.close().catch(()=>{}));n=a,se(),await Promise.all(h)},closeAgents:async()=>{let a=[];for(let o of n.providers.values())o._agent&&a.push(o._agent.close().catch(()=>{}));await Promise.all(a)}}}var U=class t{static MAX_MAP_SIZE=200;buffer;maxSize;head=0;count=0;_lifetimeRequests=0;subscribers;createdAt;_totalInputTokens=0;_totalOutputTokens=0;_totalTokensPerSec=0;_totalCacheReadTokens=0;_totalCacheCreationTokens=0;_modelMap=new Map;_providerMap=new Map;constructor(e=1e3){this.buffer=new Array(e).fill(null),this.maxSize=e,this.subscribers=new Set,this.createdAt=Date.now()}pruneMap(e,r){if(e.size<=t.MAX_MAP_SIZE)return;let n="",l=1/0;for(let[c,a]of e){let o=r(a);o<l&&(l=o,n=c)}n&&e.delete(n)}recordRequest(e){let r=this.head%this.maxSize,n=this.count>=this.maxSize?this.buffer[r]:null;if(n!==null){this._totalInputTokens-=n.inputTokens??0,this._totalOutputTokens-=n.outputTokens??0,this._totalTokensPerSec-=n.tokensPerSec??0,this._totalCacheReadTokens-=n.cacheReadTokens??0,this._totalCacheCreationTokens-=n.cacheCreationTokens??0;let o=n.model,u=this._modelMap.get(o);u&&(u.count--,u.count<=0&&this._modelMap.delete(o));let h=n.targetProvider??n.provider,s=this._providerMap.get(h)??0;s<=1?this._providerMap.delete(h):this._providerMap.set(h,s-1)}this._totalInputTokens+=e.inputTokens??0,this._totalOutputTokens+=e.outputTokens??0,this._totalTokensPerSec+=e.tokensPerSec??0,this._totalCacheReadTokens+=e.cacheReadTokens??0,this._totalCacheCreationTokens+=e.cacheCreationTokens??0;let l=e.model,c=this._modelMap.get(l);c?(c.count++,e.timestamp>c.lastSeen&&(c.lastSeen=e.timestamp),c.actualModel=e.actualModel):this._modelMap.set(l,{actualModel:e.actualModel,count:1,lastSeen:e.timestamp});let a=e.targetProvider??e.provider;this._providerMap.set(a,(this._providerMap.get(a)??0)+1),this.pruneMap(this._modelMap,o=>o.count),this.pruneMap(this._providerMap,o=>o),this.buffer[r]=e,this.head++,this.count<this.maxSize&&this.count++,this._lifetimeRequests++;for(let o of this.subscribers)try{o(e)}catch{}}getSummary(){let e=this.getRecentRequests(),r=[...this._modelMap.entries()].map(([a,{actualModel:o,count:u,lastSeen:h}])=>({model:a,actualModel:o,count:u,lastSeen:h})).sort((a,o)=>o.count-a.count),n=[...this._providerMap.entries()].map(([a,o])=>({provider:a,count:o})).sort((a,o)=>o.count-a.count),l=0,c=0;for(let a of e){let o=(a.inputTokens??0)+(a.cacheReadTokens??0)+(a.cacheCreationTokens??0);o>0&&(a.cacheReadTokens??0)>0&&(l+=a.cacheReadTokens/o*100,c++)}return{totalRequests:this._lifetimeRequests,totalInputTokens:this._totalInputTokens,totalOutputTokens:this._totalOutputTokens,avgTokensPerSec:this.count>0?Math.round(this._totalTokensPerSec/this.count*10)/10:0,totalCacheReadTokens:this._totalCacheReadTokens,totalCacheCreationTokens:this._totalCacheCreationTokens,avgCacheHitRate:c>0?Math.round(l/c*10)/10:0,activeModels:r,providerDistribution:n,recentRequests:e,uptimeSeconds:Math.floor((Date.now()-this.createdAt)/1e3)}}onRecord(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}getRecentRequests(){if(this.count===0)return[];let e=Math.min(this.count,50),r=[];for(let n=0;n<e;n++){let l=((this.head-1-n)%this.maxSize+this.maxSize)%this.maxSize,c=this.buffer[l];c!==null&&r.push(c)}return r.reverse(),r}};import{spawn as Qe}from"child_process";import{existsSync as Ye,unlinkSync as et}from"fs";import{dirname as tt,join as nt}from"path";import{fileURLToPath as ot}from"url";async function ke(t){let e=ne();Ye(e)&&et(e),await oe(process.pid);let r=process.argv[1]||nt(tt(ot(import.meta.url)),"index.js");process.on("uncaughtException",m=>{console.error(`[monitor] Uncaught exception: ${m.message}`)}),process.on("unhandledRejection",m=>{console.error(`[monitor] Unhandled rejection: ${m}`)});let n=10,l=1e3,c=3e4,a=6e4,o=0,u=null,h=null,s=!1,i=!1,p=null;async function d(){let m=await import("net"),{execFileSync:f}=await import("child_process"),y=t.port??3456,_=()=>new Promise(v=>{let w=m.createServer();w.once("error",()=>v(!0)),w.once("listening",()=>{w.close(()=>v(!1))}),w.listen(y)});if(await _()){console.warn(`[monitor] Port ${y} in use by orphaned process \u2014 cleaning up`);try{let v=f("lsof",["-ti",`:${y}`],{encoding:"utf8"}).trim();if(v){let w=v.split(`
10
- `).map(Number).filter(Boolean);for(let g of w)try{process.kill(g,"SIGTERM"),console.warn(`[monitor] Sent SIGTERM to orphaned process ${g}`)}catch{}for(let g=0;g<10;g++)if(await new Promise(S=>setTimeout(S,500)),!await _()){console.warn(`[monitor] Port ${y} freed \u2014 spawning new daemon`);break}if(await _()){for(let g of w)try{process.kill(g,"SIGKILL")}catch{}await new Promise(g=>setTimeout(g,500))}}}catch{try{f("pkill",["-f","modelweaver.*dist/index.*--daemon"],{stdio:"ignore"}),await new Promise(v=>setTimeout(v,1e3))}catch{}}if(await _()){console.error(`[monitor] Port ${y} still in use after cleanup \u2014 skipping worker spawn`);return}}let b=[r,"--daemon"];t.config&&b.push("--config",t.config),t.port&&b.push("--port",String(t.port)),t.verbose&&b.push("--verbose"),p=Qe(process.execPath,b,{detached:!0,stdio:"ignore",env:{...process.env}}),u&&clearTimeout(u),u=setTimeout(()=>{o>0&&console.error(`[monitor] Worker stable for ${a}ms, resetting restart counter`),o=0,u=null},a),p.on("exit",async v=>{p=null,u&&(clearTimeout(u),u=null),await re(),v===0&&!i&&(await D(),process.exit(0)),i=!1,s&&(console.error("[monitor] Worker exited during shutdown, monitor exiting"),await D(),process.exit(0));let w=o;w>=n&&(console.error(`[monitor] Max restart attempts exhausted (${n}), monitor exiting`),await D(),process.exit(1));let g=Math.min(l*2**w,c);o++,console.error(`[monitor] Worker died (code ${v}), restarting in ${g}ms (attempt ${o}/${n})`),h=setTimeout(d,g)})}process.on("SIGTERM",()=>{if(s=!0,h&&(clearTimeout(h),h=null),u&&(clearTimeout(u),u=null),p){try{p.kill("SIGTERM")}catch{}setTimeout(()=>{console.error("[monitor] Child did not exit within 5 s, forcing exit"),process.exit(0)},5e3)}else D().then(()=>process.exit(0))}),process.on("SIGINT",()=>{if(s=!0,h&&(clearTimeout(h),h=null),u&&(clearTimeout(u),u=null),p){try{p.kill("SIGTERM")}catch{}setTimeout(()=>{console.error("[monitor] Child did not exit within 5 s, forcing exit"),process.exit(0)},5e3)}else D().then(()=>process.exit(0))}),process.on("SIGHUP",()=>{if(console.log("[monitor] Received reload signal, restarting worker..."),i=!0,h&&(clearTimeout(h),h=null),p)try{p.kill("SIGTERM")}catch{}o=0}),d()}var st=JSON.parse(rt(new URL("../package.json",import.meta.url),"utf-8")).version;function it(t){let e={verbose:!1,help:!1,daemon:!1,monitor:!1,gui:!1};for(let r=2;r<t.length;r++)switch(t[r]){case"-p":case"--port":let n=t[++r];(!n||isNaN(parseInt(n,10)))&&(console.error("Error: -p/--port requires a number"),process.exit(1)),e.port=parseInt(n,10);break;case"-c":case"--config":let l=t[++r];l||(console.error("Error: -c/--config requires a path"),process.exit(1)),e.config=l;break;case"-v":case"--verbose":e.verbose=!0;break;case"-h":case"--help":e.help=!0;break;case"--daemon":e.daemon=!0;break;case"--monitor":e.monitor=!0;break}return e}function at(){console.log(`
4
+ `)){if(!C)continue;let T=C.split(`
5
+ `).find(A=>A.startsWith("data:"));if(!T)continue;let E=T.indexOf('"usage"')!==-1,I=T.indexOf('"delta"')!==-1,O=T.indexOf('"choices"')!==-1;if(!(!E&&!I&&!O))try{let A=JSON.parse(T.slice(5));if(E){let P=et(A);P.inputTokens>o.input&&(o.input=P.inputTokens),P.outputTokens>o.output&&(o.output=P.outputTokens),P.cacheReadTokens>o.cacheRead&&(o.cacheRead=P.cacheReadTokens),P.cacheCreationTokens>o.creationTokens&&(o.cacheCreation=P.cacheCreationTokens)}if(I){let P=A.delta;P&&typeof P.text=="string"&&(y+=P.text,y.length>g&&(y=y.slice(-g)))}if(O){let P=A.choices;if(P?.[0]){let H=P[0].delta;H&&typeof H.content=="string"&&(y+=H.content,y.length>g&&(y=y.slice(-g)))}}}catch{}}},x=_=>{let C=_.includes('"usage"'),T=/"(input_tokens|prompt_tokens|cache_read_input_tokens|cache_creation_input_tokens|output_tokens|completion_tokens)"\s*:\s*(\d+)/g;if(C){let I;for(;(I=T.exec(_))!==null;){let O=parseInt(I[2],10),A=I[1];A==="input_tokens"||A==="prompt_tokens"?O>i&&(i=O):A==="cache_read_input_tokens"?O>p&&(p=O):A==="cache_creation_input_tokens"?O>d&&(d=O):(A==="output_tokens"||A==="completion_tokens")&&O>f&&(f=O)}}let E=[..._.matchAll(/"text"\s*:\s*"((?:[^"\\]|\\.)*)"/g)];if(E.length>0){let I=E[E.length-1][1].replace(/\\n/g,`
6
+ `).replace(/\\"/g,'"').replace(/\\\\/g,"\\");y+=I,y.length>g&&(y=y.slice(-g))}},k=(_,C,T=0,E=0)=>{try{let I=Date.now()-t.startTime,O=I/1e3,A=O>0?C/O:0;n.recordRequest({requestId:t.requestId,model:t.model,actualModel:t.actualModel||t.model,tier:t.tier,provider:e,targetProvider:r,status:l,inputTokens:_,outputTokens:C,latencyMs:I,tokensPerSec:Math.round(A*10)/10,timestamp:Date.now(),fallbackMode:t.fallbackMode,cacheReadTokens:T,cacheCreationTokens:E});let P=X(t.actualModel||t.model);setImmediate(()=>{q({requestId:t.requestId,model:t.model,tier:t.tier,state:"complete",status:l,latencyMs:Date.now()-t.startTime,inputTokens:_,outputTokens:C,tokensPerSec:Math.round(A*10)/10,timestamp:Date.now(),cacheReadTokens:T,cacheCreationTokens:E,cacheHitRate:Z(T,E,_),contextPercent:Q(_,T,E,C,P),contextWindowSize:P||void 0})})}catch{}},M=(_,C)=>{if(b===null&&(b=a.includes("text/event-stream")||_.startsWith("event:")),b){u+=_;let T=u.split(`
7
+ `);u=T.pop();for(let I of T)I===""?m&&(R(m),m=""):m+=(m?`
8
+ `:"")+I;C&&m.trim()&&R(m);let E=Date.now();if(v||E-w>=S){w=E,v=!1;let I=X(t.actualModel||t.model);setImmediate(()=>{q({requestId:t.requestId,model:t.model,tier:t.tier,state:"streaming",outputTokens:o.output,timestamp:E,preview:y,cacheHitRate:Z(o.cacheRead,o.cacheCreation,o.input),contextPercent:Q(o.input,o.cacheRead,o.cacheCreation,o.output,I),contextWindowSize:I||void 0})})}C&&k(o.input,o.output,o.cacheRead,o.cacheCreation)}else{h+=_,h.length>c&&(h=h.slice(-c)),x(h);let T=Date.now();if(v||T-w>=S){w=T,v=!1;let E=X(t.actualModel||t.model);setImmediate(()=>{q({requestId:t.requestId,model:t.model,tier:t.tier,state:"streaming",outputTokens:f,timestamp:T,preview:y,cacheHitRate:Z(p,d,i),contextPercent:Q(i,p,d,f,E),contextWindowSize:E||void 0})})}C&&k(i,f,p,d)}};return new TransformStream({transform(_,C){C.enqueue(_),M(s.decode(_,{stream:!0}),!1)},flush(){M("",!0)}})}function Se(t){let e=t._cachedOrigin,r=t.poolSize??10;return`${e??"unknown"}:${r}`}function ee(t,e,r){let n=t,l=te(e),a=new Ve;return a.onError((s,o)=>(console.error(`[server] Unhandled error: ${s.message}`),o.json({type:"error",error:{type:"api_error",message:"Internal proxy error"}},{status:500,headers:{"content-type":"application/json"}}))),a.use("/api/*",async(s,o)=>{s.header("Access-Control-Allow-Origin","*"),await o()}),a.options("/api/*",s=>(s.header("Access-Control-Allow-Origin","*"),s.header("Access-Control-Allow-Methods","GET, POST, OPTIONS"),s.header("Access-Control-Allow-Headers","Content-Type, Authorization, anthropic-version, x-api-key"),s.body("",200))),a.post("/v1/messages",async s=>{let o=Xe(),u,m;try{m=await s.req.text(),u=JSON.parse(m)}catch{return Y("invalid_request_error","Invalid JSON body",o)}let c=u.model;if(!c)return Y("invalid_request_error","Missing 'model' field in request body",o);let i=ae(c,o,n,m);if(i&&(i.parsedBody=u),!i){l.info("No tier match",{requestId:o,model:c});let w=n.modelRouting.size>0?` Configured model routes: ${[...n.modelRouting.keys()].join(", ")}.`:"";return Y("invalid_request_error",`No route matches model "${c}". Configured tiers: ${[...n.tierPatterns.keys()].join(", ")}.${w}`,o)}l.info("Routing request",{requestId:o,model:c,tier:i.tier,providers:i.providerChain.map(w=>w.provider)}),q({requestId:o,model:c,tier:i.tier,state:"start",provider:i.providerChain[0]?.provider??"unknown",timestamp:Date.now()});let p="unknown",d;try{if(d=await Te(n.providers,i.providerChain,i,s.req.raw,(w,v)=>{l.info("Attempting provider",{requestId:o,provider:w,index:v,tier:i.tier}),p||(p=w)},l),d.status<400){let w=17;d.headers.forEach((v,y)=>{w+=y.length+v.length+4}),w+=2,setImmediate(()=>{q({requestId:o,model:c,tier:i.tier,state:"ttfb",status:d.status,headerSize:w,timestamp:Date.now()})})}}catch(w){let v=w instanceof Error?w.message:String(w);return l.error("Forward failed",{requestId:o,error:v}),setImmediate(()=>{q({requestId:o,model:c,tier:i.tier,state:"error",status:502,message:v,timestamp:Date.now()})}),s.json({type:"error",error:{type:"api_error",message:"Upstream request failed: "+v}},502)}d.status>=400&&setImmediate(()=>{q({requestId:o,model:c,tier:i.tier,state:"error",status:d.status,message:`HTTP ${d.status}`,timestamp:Date.now()})});let f=d.body;if(d.body&&d.status>=200&&d.status<300&&r){let w=i.providerChain.length>0?i.providerChain[0].provider:p,v=tt(i,p,w,r,d.status,d.headers.get("content-type")||"");f=d.body.pipeThrough(v)}let h=new Headers(d.headers);h.set("x-request-id",o);let b=new Response(f,{status:d.status,statusText:d.statusText,headers:h}),S=Date.now()-i.startTime;return l.info("Request completed",{requestId:o,model:c,tier:i.tier,status:b.status,latencyMs:S}),b}),a.get("/api/metrics/summary",async s=>{if(!r)return s.json({error:"Metrics not enabled"},503);let o=r.getSummary(),u=JSON.stringify(o);if((s.req.header("accept-encoding")||"").includes("gzip")&&u.length>=1024){let c=await Ye(Buffer.from(u));return new Response(c,{status:200,headers:{"content-type":"application/json","content-encoding":"gzip",vary:"accept-encoding"}})}return s.json(o)}),a.get("/api/circuit-breaker",s=>{let o={};for(let[u,m]of n.providers){let c=m._circuitBreaker;if(c){let i=c.getStatus();o[u]={state:i.state,failures:i.failures,lastFailure:i.lastFailure?new Date(i.lastFailure).toISOString():null}}}return s.json(o)}),{app:a,getConfig:()=>n,setConfig:async s=>{let o=new Map;for(let c of n.providers.values())c._agent&&o.set(Se(c),c._agent);let u=new Set;for(let c of s.providers.values()){let i=Se(c),p=o.get(i);p&&(c._agent=p,u.add(i))}let m=[];for(let[c,i]of o)u.has(c)||m.push(i.close().catch(()=>{}));n=s,ie(),await Promise.all(m)},closeAgents:async()=>{let s=[];for(let o of n.providers.values())o._agent&&s.push(o._agent.close().catch(()=>{}));await Promise.all(s)}}}var U=class t{static MAX_MAP_SIZE=200;buffer;maxSize;head=0;count=0;_lifetimeRequests=0;subscribers;createdAt;_totalInputTokens=0;_totalOutputTokens=0;_totalTokensPerSec=0;_totalCacheReadTokens=0;_totalCacheCreationTokens=0;_modelMap=new Map;_providerMap=new Map;constructor(e=1e3){this.buffer=new Array(e).fill(null),this.maxSize=e,this.subscribers=new Set,this.createdAt=Date.now()}pruneMap(e,r){if(e.size<=t.MAX_MAP_SIZE)return;let n="",l=1/0;for(let[a,s]of e){let o=r(s);o<l&&(l=o,n=a)}n&&e.delete(n)}recordRequest(e){let r=this.head%this.maxSize,n=this.count>=this.maxSize?this.buffer[r]:null;if(n!==null){this._totalInputTokens-=n.inputTokens??0,this._totalOutputTokens-=n.outputTokens??0,this._totalTokensPerSec-=n.tokensPerSec??0,this._totalCacheReadTokens-=n.cacheReadTokens??0,this._totalCacheCreationTokens-=n.cacheCreationTokens??0;let o=n.model,u=this._modelMap.get(o);u&&(u.count--,u.count<=0&&this._modelMap.delete(o));let m=n.targetProvider??n.provider,c=this._providerMap.get(m)??0;c<=1?this._providerMap.delete(m):this._providerMap.set(m,c-1)}this._totalInputTokens+=e.inputTokens??0,this._totalOutputTokens+=e.outputTokens??0,this._totalTokensPerSec+=e.tokensPerSec??0,this._totalCacheReadTokens+=e.cacheReadTokens??0,this._totalCacheCreationTokens+=e.cacheCreationTokens??0;let l=e.model,a=this._modelMap.get(l);a?(a.count++,e.timestamp>a.lastSeen&&(a.lastSeen=e.timestamp),a.actualModel=e.actualModel):this._modelMap.set(l,{actualModel:e.actualModel,count:1,lastSeen:e.timestamp});let s=e.targetProvider??e.provider;this._providerMap.set(s,(this._providerMap.get(s)??0)+1),this.pruneMap(this._modelMap,o=>o.count),this.pruneMap(this._providerMap,o=>o),this.buffer[r]=e,this.head++,this.count<this.maxSize&&this.count++,this._lifetimeRequests++;for(let o of this.subscribers)try{o(e)}catch{}}getSummary(){let e=this.getRecentRequests(),r=[...this._modelMap.entries()].map(([s,{actualModel:o,count:u,lastSeen:m}])=>({model:s,actualModel:o,count:u,lastSeen:m})).sort((s,o)=>o.count-s.count),n=[...this._providerMap.entries()].map(([s,o])=>({provider:s,count:o})).sort((s,o)=>o.count-s.count),l=0,a=0;for(let s of e){let o=(s.inputTokens??0)+(s.cacheReadTokens??0)+(s.cacheCreationTokens??0);o>0&&(s.cacheReadTokens??0)>0&&(l+=s.cacheReadTokens/o*100,a++)}return{totalRequests:this._lifetimeRequests,totalInputTokens:this._totalInputTokens,totalOutputTokens:this._totalOutputTokens,avgTokensPerSec:this.count>0?Math.round(this._totalTokensPerSec/this.count*10)/10:0,totalCacheReadTokens:this._totalCacheReadTokens,totalCacheCreationTokens:this._totalCacheCreationTokens,avgCacheHitRate:a>0?Math.round(l/a*10)/10:0,activeModels:r,providerDistribution:n,recentRequests:e,uptimeSeconds:Math.floor((Date.now()-this.createdAt)/1e3)}}onRecord(e){return this.subscribers.add(e),()=>{this.subscribers.delete(e)}}getRecentRequests(){if(this.count===0)return[];let e=Math.min(this.count,50),r=[];for(let n=0;n<e;n++){let l=((this.head-1-n)%this.maxSize+this.maxSize)%this.maxSize,a=this.buffer[l];a!==null&&r.push(a)}return r.reverse(),r}};import{spawn as nt}from"child_process";import{existsSync as ot,unlinkSync as rt}from"fs";import{dirname as st,join as it}from"path";import{fileURLToPath as at}from"url";async function _e(t){let e=oe();ot(e)&&rt(e),await re(process.pid);let r=process.argv[1]||it(st(at(import.meta.url)),"index.js");process.on("uncaughtException",f=>{console.error(`[monitor] Uncaught exception: ${f.message}`)}),process.on("unhandledRejection",f=>{console.error(`[monitor] Unhandled rejection: ${f}`)});let n=10,l=1e3,a=3e4,s=6e4,o=0,u=null,m=null,c=!1,i=!1,p=null;async function d(){let f=await import("net"),{execFileSync:h}=await import("child_process"),b=t.port??3456,S=()=>new Promise(v=>{let y=f.createServer();y.once("error",()=>v(!0)),y.once("listening",()=>{y.close(()=>v(!1))}),y.listen(b)});if(await S()){console.warn(`[monitor] Port ${b} in use by orphaned process \u2014 cleaning up`);try{let v=h("lsof",["-ti",`:${b}`],{encoding:"utf8"}).trim();if(v){let y=v.split(`
9
+ `).map(Number).filter(Boolean);for(let g of y)try{process.kill(g,"SIGTERM"),console.warn(`[monitor] Sent SIGTERM to orphaned process ${g}`)}catch{}for(let g=0;g<10;g++)if(await new Promise(R=>setTimeout(R,500)),!await S()){console.warn(`[monitor] Port ${b} freed \u2014 spawning new daemon`);break}if(await S()){for(let g of y)try{process.kill(g,"SIGKILL")}catch{}await new Promise(g=>setTimeout(g,500))}}}catch{try{h("pkill",["-f","modelweaver.*dist/index.*--daemon"],{stdio:"ignore"}),await new Promise(v=>setTimeout(v,1e3))}catch{}}if(await S()){console.error(`[monitor] Port ${b} still in use after cleanup \u2014 skipping worker spawn`);return}}let w=[r,"--daemon"];t.config&&w.push("--config",t.config),t.port&&w.push("--port",String(t.port)),t.verbose&&w.push("--verbose"),p=nt(process.execPath,w,{detached:!0,stdio:"ignore",env:{...process.env}}),u&&clearTimeout(u),u=setTimeout(()=>{o>0&&console.error(`[monitor] Worker stable for ${s}ms, resetting restart counter`),o=0,u=null},s),p.on("exit",async v=>{p=null,u&&(clearTimeout(u),u=null),await se(),v===0&&!i&&(await B(),process.exit(0)),i=!1,c&&(console.error("[monitor] Worker exited during shutdown, monitor exiting"),await B(),process.exit(0));let y=o;y>=n&&(console.error(`[monitor] Max restart attempts exhausted (${n}), monitor exiting`),await B(),process.exit(1));let g=Math.min(l*2**y,a);o++,console.error(`[monitor] Worker died (code ${v}), restarting in ${g}ms (attempt ${o}/${n})`),m=setTimeout(d,g)})}process.on("SIGTERM",()=>{if(c=!0,m&&(clearTimeout(m),m=null),u&&(clearTimeout(u),u=null),p){try{p.kill("SIGTERM")}catch{}setTimeout(()=>{console.error("[monitor] Child did not exit within 5 s, forcing exit"),process.exit(0)},5e3)}else B().then(()=>process.exit(0))}),process.on("SIGINT",()=>{if(c=!0,m&&(clearTimeout(m),m=null),u&&(clearTimeout(u),u=null),p){try{p.kill("SIGTERM")}catch{}setTimeout(()=>{console.error("[monitor] Child did not exit within 5 s, forcing exit"),process.exit(0)},5e3)}else B().then(()=>process.exit(0))}),process.on("SIGHUP",()=>{if(console.log("[monitor] Received reload signal, restarting worker..."),i=!0,m&&(clearTimeout(m),m=null),p)try{p.kill("SIGTERM")}catch{}o=0}),d()}var ut=JSON.parse(ct(new URL("../package.json",import.meta.url),"utf-8")).version;function lt(t){let e={verbose:!1,help:!1,daemon:!1,monitor:!1,gui:!1};for(let r=2;r<t.length;r++)switch(t[r]){case"-p":case"--port":let n=t[++r];(!n||isNaN(parseInt(n,10)))&&(console.error("Error: -p/--port requires a number"),process.exit(1)),e.port=parseInt(n,10);break;case"-c":case"--config":let l=t[++r];l||(console.error("Error: -c/--config requires a path"),process.exit(1)),e.config=l;break;case"-v":case"--verbose":e.verbose=!0;break;case"-h":case"--help":e.help=!0;break;case"--daemon":e.daemon=!0;break;case"--monitor":e.monitor=!0;break}return e}function dt(){console.log(`
11
10
  ModelWeaver \u2014 Multi-provider model orchestration proxy for Claude Code
12
11
 
13
12
  Usage: modelweaver [command] [options]
@@ -32,8 +31,8 @@ Options:
32
31
  Config locations (first found wins):
33
32
  ./modelweaver.yaml
34
33
  ~/.modelweaver/config.yaml
35
- `)}async function ct(){let t=it(process.argv);try{let s=await import("dotenv"),{existsSync:i}=await import("fs"),{join:p}=await import("path"),d=process.env.HOME||process.env.USERPROFILE||"",m=[p(process.cwd(),".env"),p(d,".modelweaver",".env"),p(d,".env")];for(let f of m)if(i(f)){s.config({path:f,quiet:!0});break}}catch{}if(process.argv[2]==="init"){let s=process.argv.includes("--quick")||process.argv.includes("-q"),{runInit:i}=await import("./init-WPWG2GL3.js");await i({quick:s}),process.exit(0)}if(process.argv[2]==="start"){let{startDaemon:s}=await import("./daemon-2JXEJR35.js"),i=await s(t.config,t.port,t.verbose);console.log(` ${i.message}`),console.log(` Log file: ${i.logPath}`),process.exit(i.success?0:1)}if(process.argv[2]==="stop"){let{stopDaemon:s}=await import("./daemon-2JXEJR35.js"),i=await s();console.log(` ${i.message}`),process.exit(i.success?0:1)}if(process.argv[2]==="status"){let{statusDaemon:s}=await import("./daemon-2JXEJR35.js"),i=await s();console.log(` ${i.message}`);try{let{getService:p}=await import("./service-6EQTZJEG.js"),m=(await p()).isInstalled();console.log(m?" Service: installed":' Service: not installed (run "modelweaver install" to enable auto-start)')}catch(p){console.log(` Service: ${p instanceof Error?p.message:String(p)}`)}process.exit(0)}if(process.argv[2]==="remove"){let{removeDaemon:s}=await import("./daemon-2JXEJR35.js"),i=await s();console.log(` ${i.message}`),process.exit(i.success?0:1)}if(process.argv[2]==="install"){try{let{getService:s}=await import("./service-6EQTZJEG.js");await(await s()).install()}catch(s){console.error(` Error: ${s instanceof Error?s.message:String(s)}`),process.exit(1)}process.exit(0)}if(process.argv[2]==="uninstall"){try{let{getService:s}=await import("./service-6EQTZJEG.js");(await s()).uninstall()}catch(s){console.error(` Error: ${s instanceof Error?s.message:String(s)}`),process.exit(1)}process.exit(0)}if(process.argv[2]==="gui"){let{launchGui:s}=await import("./gui-launcher-ZVOVTD6C.js");await s(),process.exit(0)}if(process.argv[2]==="reload"){let{reloadDaemon:s}=await import("./daemon-2JXEJR35.js");await s(t.port),process.exit(0)}t.help&&(at(),process.exit(0));let e,r;try{let s=await te(t.config);e=s.config,r=s.configPath}catch(s){console.error(`Config error: ${s.message}`),process.exit(1)}let n=t.port||e.server.port,l=e.server.host,c=t.verbose?"debug":"info",a=new U;if(t.monitor){await ke(t);return}if(t.daemon){let{removeWorkerPidFile:s,writeWorkerPidFile:i,createDebouncedReload:p,getLogPath:d}=await import("./daemon-2JXEJR35.js"),{reloadConfig:m}=await import("./config-ZAAEWZUJ.js"),{createWriteStream:f,watch:y}=await import("fs"),{createLogger:_}=await import("./logger-UA2A2DVX.js"),b=_(c);process.on("uncaughtException",T=>{b.error("Uncaught exception (daemon survived)",{error:T.message,stack:T.stack})}),process.on("unhandledRejection",T=>{b.error("Unhandled rejection (daemon survived)",{reason:String(T)})}),await i(process.pid);let v=f(d(),{flags:"a"});v.on("error",()=>{}),process.stdout.write=v.write.bind(v),process.stderr.write=v.write.bind(v);let w=Y(e,c,a),g=null;if(r){let T=p(async()=>{try{let C=await m(r);await w.setConfig(C),L.prune([...C.providers.keys()]),b.info("Config reloaded",{path:r})}catch(C){b.error("Config reload failed \u2014 keeping old config",{error:C.message})}},300);try{g=y(r,()=>{T.reload()}),g.on("error",()=>{g&&(g.close(),g=null)})}catch{}}process.on("SIGUSR1",async()=>{try{let T=await m(r);await w.setConfig(T),L.prune([...T.providers.keys()]),b.info("Config reloaded (SIGUSR1)",{path:r})}catch(T){b.error("Config reload failed (SIGUSR1)",{error:T.message})}});let S=Te({fetch:w.app.fetch,hostname:l,port:n,serverOptions:{requestTimeout:3e5,headersTimeout:1e4,keepAliveTimeout:3e4}});S.on("error",T=>{T.code==="EADDRINUSE"&&(b.error(`Port ${n} already in use, exiting for monitor restart`,{port:n}),process.exit(1))}),S.listen(n,l),G(S,a);let M=async()=>{g&&(g.close(),g=null),await w.closeAgents(),await s(),v.end(),process.exit(0)};process.on("SIGTERM",M),process.on("SIGINT",M);return}let o=Y(e,c,a);console.log(`
36
- ModelWeaver v${st}`),console.log(` Listening: http://${l}:${n}`),console.log(` Config: ${r}
37
- `),console.log(" Routes:");for(let[s,i]of e.routing){let p=i.map((d,m)=>`${d.provider}${m===0?" (primary)":" (fallback)"}`).join(", ");console.log(` ${s.padEnd(8)} \u2192 ${p}`)}if(console.log(),e.modelRouting.size>0){console.log(" Model Routes:");for(let[s,i]of e.modelRouting){let p=i.map((d,m)=>`${d.provider}${m===0?" (primary)":" (fallback)"}`).join(", ");console.log(` ${s.padEnd(20)} \u2192 ${p}`)}console.log()}let u=Te({fetch:o.app.fetch,hostname:l,port:n,serverOptions:{requestTimeout:3e5,headersTimeout:1e4,keepAliveTimeout:3e4}});u.on("error",s=>{s.code==="EADDRINUSE"&&(console.error(`Port ${n} already in use, exiting for monitor restart`),process.exit(1))}),u.listen(n,l),G(u,a);let h=async()=>{console.log(`
38
- Shutting down...`),await o.closeAgents(),process.exit(0)};process.on("SIGTERM",h),process.on("SIGINT",h)}ct();
34
+ `)}async function pt(){let t=lt(process.argv);try{let c=await import("dotenv"),{existsSync:i}=await import("fs"),{join:p}=await import("path"),d=process.env.HOME||process.env.USERPROFILE||"",f=[p(process.cwd(),".env"),p(d,".modelweaver",".env"),p(d,".env")];for(let h of f)if(i(h)){c.config({path:h,quiet:!0});break}}catch{}if(process.argv[2]==="init"){let c=process.argv.includes("--quick")||process.argv.includes("-q"),{runInit:i}=await import("./init-WPWG2GL3.js");await i({quick:c}),process.exit(0)}if(process.argv[2]==="start"){let{startDaemon:c}=await import("./daemon-2JXEJR35.js"),i=await c(t.config,t.port,t.verbose);console.log(` ${i.message}`),console.log(` Log file: ${i.logPath}`),process.exit(i.success?0:1)}if(process.argv[2]==="stop"){let{stopDaemon:c}=await import("./daemon-2JXEJR35.js"),i=await c();console.log(` ${i.message}`),process.exit(i.success?0:1)}if(process.argv[2]==="status"){let{statusDaemon:c}=await import("./daemon-2JXEJR35.js"),i=await c();console.log(` ${i.message}`);try{let{getService:p}=await import("./service-6EQTZJEG.js"),f=(await p()).isInstalled();console.log(f?" Service: installed":' Service: not installed (run "modelweaver install" to enable auto-start)')}catch(p){console.log(` Service: ${p instanceof Error?p.message:String(p)}`)}process.exit(0)}if(process.argv[2]==="remove"){let{removeDaemon:c}=await import("./daemon-2JXEJR35.js"),i=await c();console.log(` ${i.message}`),process.exit(i.success?0:1)}if(process.argv[2]==="install"){try{let{getService:c}=await import("./service-6EQTZJEG.js");await(await c()).install()}catch(c){console.error(` Error: ${c instanceof Error?c.message:String(c)}`),process.exit(1)}process.exit(0)}if(process.argv[2]==="uninstall"){try{let{getService:c}=await import("./service-6EQTZJEG.js");(await c()).uninstall()}catch(c){console.error(` Error: ${c instanceof Error?c.message:String(c)}`),process.exit(1)}process.exit(0)}if(process.argv[2]==="gui"){let{launchGui:c}=await import("./gui-launcher-ZVOVTD6C.js");await c(),process.exit(0)}if(process.argv[2]==="reload"){let{reloadDaemon:c}=await import("./daemon-2JXEJR35.js");await c(t.port),process.exit(0)}t.help&&(dt(),process.exit(0));let e,r;try{let c=await ne(t.config);e=c.config,r=c.configPath}catch(c){console.error(`Config error: ${c.message}`),process.exit(1)}let n=t.port||e.server.port,l=e.server.host,a=t.verbose?"debug":"info",s=new U;if(t.monitor){await _e(t);return}if(t.daemon){let{removeWorkerPidFile:c,writeWorkerPidFile:i,createDebouncedReload:p,getLogPath:d}=await import("./daemon-2JXEJR35.js"),{reloadConfig:f}=await import("./config-ZAAEWZUJ.js"),{createWriteStream:h,watch:b}=await import("fs"),{createLogger:S}=await import("./logger-UA2A2DVX.js"),w=S(a);process.on("uncaughtException",k=>{w.error("Uncaught exception (daemon survived)",{error:k.message,stack:k.stack})}),process.on("unhandledRejection",k=>{w.error("Unhandled rejection (daemon survived)",{reason:String(k)})}),await i(process.pid);let v=h(d(),{flags:"a"});v.on("error",()=>{}),process.stdout.write=v.write.bind(v),process.stderr.write=v.write.bind(v);let y=ee(e,a,s),g=null;if(r){let k=p(async()=>{try{let M=await f(r);await y.setConfig(M),D.prune([...M.providers.keys()]),w.info("Config reloaded",{path:r})}catch(M){w.error("Config reload failed \u2014 keeping old config",{error:M.message})}},300);try{g=b(r,()=>{k.reload()}),g.on("error",()=>{g&&(g.close(),g=null)})}catch{}}process.on("SIGUSR1",async()=>{try{let k=await f(r);await y.setConfig(k),D.prune([...k.providers.keys()]),w.info("Config reloaded (SIGUSR1)",{path:r})}catch(k){w.error("Config reload failed (SIGUSR1)",{error:k.message})}});let R=Me({fetch:y.app.fetch,hostname:l,port:n,serverOptions:{requestTimeout:3e5,headersTimeout:1e4,keepAliveTimeout:3e4}});R.on("error",k=>{k.code==="EADDRINUSE"&&(w.error(`Port ${n} already in use, exiting for monitor restart`,{port:n}),process.exit(1))}),R.listen(n,l),G(R,s);let x=async()=>{g&&(g.close(),g=null),await y.closeAgents(),await c(),v.end(),process.exit(0)};process.on("SIGTERM",x),process.on("SIGINT",x);return}let o=ee(e,a,s);console.log(`
35
+ ModelWeaver v${ut}`),console.log(` Listening: http://${l}:${n}`),console.log(` Config: ${r}
36
+ `),console.log(" Routes:");for(let[c,i]of e.routing){let p=i.map((d,f)=>`${d.provider}${f===0?" (primary)":" (fallback)"}`).join(", ");console.log(` ${c.padEnd(8)} \u2192 ${p}`)}if(console.log(),e.modelRouting.size>0){console.log(" Model Routes:");for(let[c,i]of e.modelRouting){let p=i.map((d,f)=>`${d.provider}${f===0?" (primary)":" (fallback)"}`).join(", ");console.log(` ${c.padEnd(20)} \u2192 ${p}`)}console.log()}let u=Me({fetch:o.app.fetch,hostname:l,port:n,serverOptions:{requestTimeout:3e5,headersTimeout:1e4,keepAliveTimeout:3e4}});u.on("error",c=>{c.code==="EADDRINUSE"&&(console.error(`Port ${n} already in use, exiting for monitor restart`),process.exit(1))}),u.listen(n,l),G(u,s);let m=async()=>{console.log(`
37
+ Shutting down...`),await o.closeAgents(),process.exit(0)};process.on("SIGTERM",m),process.on("SIGINT",m)}pt();
39
38
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/router.ts","../src/proxy.ts","../src/hedging.ts","../src/ws.ts","../src/metrics.ts","../src/monitor.ts"],"sourcesContent":["// src/index.ts\nimport { createAdaptorServer } from \"@hono/node-server\";\nimport { readFileSync } from \"node:fs\";\nimport { createApp } from \"./server.js\";\nimport { loadConfig } from \"./config.js\";\nimport type { LogLevel } from \"./logger.js\";\nimport { MetricsStore } from \"./metrics.js\";\nimport { latencyTracker } from \"./hedging.js\";\nimport { attachWebSocket } from \"./ws.js\";\nimport { startMonitor } from \"./monitor.js\";\n\n// Read version from package.json at startup\nconst VERSION: string = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf-8\")).version;\n\nfunction parseArgs(argv: string[]): { port?: number; config?: string; verbose: boolean; help: boolean; daemon: boolean; monitor: boolean; gui: boolean } {\n const args: { port?: number; config?: string; verbose: boolean; help: boolean; daemon: boolean; monitor: boolean; gui: boolean } = { verbose: false, help: false, daemon: false, monitor: false, gui: false };\n for (let i = 2; i < argv.length; i++) {\n switch (argv[i]) {\n case \"-p\":\n case \"--port\":\n const portStr = argv[++i];\n if (!portStr || isNaN(parseInt(portStr, 10))) {\n console.error(\"Error: -p/--port requires a number\");\n process.exit(1);\n }\n args.port = parseInt(portStr, 10);\n break;\n case \"-c\":\n case \"--config\":\n const configPath = argv[++i];\n if (!configPath) {\n console.error(\"Error: -c/--config requires a path\");\n process.exit(1);\n }\n args.config = configPath;\n break;\n case \"-v\":\n case \"--verbose\":\n args.verbose = true;\n break;\n case \"-h\":\n case \"--help\":\n args.help = true;\n break;\n case \"--daemon\":\n args.daemon = true;\n break;\n case \"--monitor\":\n args.monitor = true;\n break;\n }\n }\n return args;\n}\n\nfunction printHelp() {\n console.log(`\nModelWeaver — Multi-provider model orchestration proxy for Claude Code\n\nUsage: modelweaver [command] [options]\n\nCommands:\n init [--quick] Run interactive setup wizard (--quick for express mode)\n start Start as background daemon\n stop Stop background daemon\n status Show daemon status\n remove Stop daemon and remove PID + log files\n reload Reload daemon worker (load fresh code after build)\n install Install launchd service (auto-start at login)\n uninstall Uninstall launchd service\n gui Launch the GUI (downloads if needed)\n\nOptions:\n -p, --port <number> Server port (default: from config)\n -c, --config <path> Config file path (auto-detected)\n -v, --verbose Enable debug logging (default: off)\n -h, --help Show this help\n\nConfig locations (first found wins):\n ./modelweaver.yaml\n ~/.modelweaver/config.yaml\n`);\n}\n\nasync function main() {\n const args = parseArgs(process.argv);\n\n // Load .env file if present (created by modelweaver init)\n try {\n const dotenv = await import('dotenv');\n const { existsSync } = await import('node:fs');\n const { join } = await import('node:path');\n const home = process.env.HOME || process.env.USERPROFILE || '';\n // Try cwd/.env first, then ~/.modelweaver/.env, then ~/.env\n const paths = [\n join(process.cwd(), '.env'),\n join(home, '.modelweaver', '.env'),\n join(home, '.env'),\n ];\n for (const p of paths) {\n if (existsSync(p)) {\n dotenv.config({ path: p, quiet: true });\n break;\n }\n }\n } catch {\n // dotenv not installed or .env not present — continue without it\n }\n\n // Handle 'init' subcommand — dynamic import to avoid loading prompts for normal startup\n if (process.argv[2] === 'init') {\n const quick = process.argv.includes('--quick') || process.argv.includes('-q');\n const { runInit } = await import('./init.js');\n await runInit({ quick });\n process.exit(0);\n }\n\n // Handle 'start' subcommand\n if (process.argv[2] === 'start') {\n const { startDaemon } = await import('./daemon.js');\n const result = await startDaemon(args.config, args.port, args.verbose);\n console.log(` ${result.message}`);\n console.log(` Log file: ${result.logPath}`);\n process.exit(result.success ? 0 : 1);\n }\n\n // Handle 'stop' subcommand\n if (process.argv[2] === 'stop') {\n const { stopDaemon } = await import('./daemon.js');\n const result = await stopDaemon();\n console.log(` ${result.message}`);\n process.exit(result.success ? 0 : 1);\n }\n\n // Handle 'status' subcommand\n if (process.argv[2] === 'status') {\n const { statusDaemon } = await import('./daemon.js');\n const result = await statusDaemon();\n console.log(` ${result.message}`);\n try {\n const { getService } = await import('./service.js');\n const svc = await getService();\n const installed = svc.isInstalled();\n if (installed) {\n console.log(` Service: installed`);\n } else {\n console.log(` Service: not installed (run \"modelweaver install\" to enable auto-start)`);\n }\n } catch (err) {\n console.log(` Service: ${err instanceof Error ? err.message : String(err)}`);\n }\n process.exit(0);\n }\n\n // Handle 'remove' subcommand — stop + clean up PID and log files\n if (process.argv[2] === 'remove') {\n const { removeDaemon } = await import('./daemon.js');\n const result = await removeDaemon();\n console.log(` ${result.message}`);\n process.exit(result.success ? 0 : 1);\n }\n\n // Handle 'install' subcommand — install platform service\n if (process.argv[2] === 'install') {\n try {\n const { getService } = await import('./service.js');\n const svc = await getService();\n await svc.install();\n } catch (err) {\n console.error(` Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n process.exit(0);\n }\n\n // Handle 'uninstall' subcommand — uninstall platform service\n if (process.argv[2] === 'uninstall') {\n try {\n const { getService } = await import('./service.js');\n const svc = await getService();\n svc.uninstall();\n } catch (err) {\n console.error(` Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n process.exit(0);\n }\n\n // Handle 'gui' subcommand\n if (process.argv[2] === 'gui') {\n const { launchGui } = await import('./gui-launcher.js');\n await launchGui();\n process.exit(0);\n }\n\n // Handle 'reload' subcommand\n if (process.argv[2] === 'reload') {\n const { reloadDaemon } = await import('./daemon.js');\n await reloadDaemon(args.port);\n process.exit(0);\n }\n\n if (args.help) {\n printHelp();\n process.exit(0);\n }\n\n // Load config\n let config;\n let configPath;\n try {\n const result = await loadConfig(args.config);\n config = result.config;\n configPath = result.configPath;\n } catch (error) {\n console.error(`Config error: ${(error as Error).message}`);\n process.exit(1);\n }\n\n // CLI port override\n const port = args.port || config.server.port;\n const host = config.server.host;\n const logLevel: LogLevel = args.verbose ? \"debug\" : \"info\";\n\n // Initialize metrics store\n const metricsStore = new MetricsStore();\n\n // --- Monitor mode (spawns daemon child, auto-restarts on crash) ---\n if (args.monitor) {\n await startMonitor(args);\n return;\n }\n\n // --- Daemon mode ---\n if (args.daemon) {\n const { removeWorkerPidFile, writeWorkerPidFile, createDebouncedReload, getLogPath } = await import('./daemon.js');\n const { reloadConfig } = await import('./config.js');\n const { createWriteStream, watch } = await import('node:fs');\n const { createLogger } = await import('./logger.js');\n const logger = createLogger(logLevel);\n\n // Prevent silent crashes from killing the daemon worker\n process.on('uncaughtException', (err) => {\n logger.error('Uncaught exception (daemon survived)', { error: err.message, stack: err.stack });\n });\n process.on('unhandledRejection', (reason) => {\n logger.error('Unhandled rejection (daemon survived)', { reason: String(reason) });\n });\n\n // Write worker PID file (monitor owns modelweaver.pid)\n await writeWorkerPidFile(process.pid);\n\n // Redirect stdout/stderr to log file\n const logStream = createWriteStream(getLogPath(), { flags: 'a' });\n logStream.on('error', () => { /* ignore write errors to log file */ });\n process.stdout.write = logStream.write.bind(logStream) as typeof process.stdout.write;\n process.stderr.write = logStream.write.bind(logStream) as typeof process.stderr.write;\n\n // Create app with mutable config\n const handle = createApp(config, logLevel, metricsStore);\n\n // Hot-reload: watch config file for changes\n let configWatcher: ReturnType<typeof watch> | null = null;\n if (configPath) {\n const debounced = createDebouncedReload(async () => {\n try {\n const newConfig = await reloadConfig(configPath);\n await handle.setConfig(newConfig);\n latencyTracker.prune([...newConfig.providers.keys()]);\n logger.info(\"Config reloaded\", { path: configPath });\n } catch (err) {\n logger.error(\"Config reload failed — keeping old config\", { error: (err as Error).message });\n }\n }, 300);\n\n try {\n configWatcher = watch(configPath, () => {\n debounced.reload();\n });\n configWatcher.on('error', () => {\n // fs.watch failed — silently disable hot-reload\n if (configWatcher) {\n configWatcher.close();\n configWatcher = null;\n }\n });\n } catch {\n // fs.watch not available — hot-reload disabled\n }\n }\n\n // SIGUSR1 triggers config hot-reload\n // Note: SIGUSR1 is POSIX-only; this handler is a no-op on Windows.\n process.on('SIGUSR1', async () => {\n try {\n const newConfig = await reloadConfig(configPath!);\n await handle.setConfig(newConfig);\n latencyTracker.prune([...newConfig.providers.keys()]);\n logger.info(\"Config reloaded (SIGUSR1)\", { path: configPath });\n } catch (err) {\n logger.error(\"Config reload failed (SIGUSR1)\", { error: (err as Error).message });\n }\n });\n\n // Start server — register error handler BEFORE listen() so EADDRINUSE is caught\n const server = createAdaptorServer({\n fetch: handle.app.fetch,\n hostname: host,\n port,\n serverOptions: {\n requestTimeout: 300_000, // 5 min max total request time (covers long SSE streams)\n headersTimeout: 10_000, // 10s to receive headers\n keepAliveTimeout: 30_000, // 30s keep-alive (matches undici agent)\n },\n });\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${port} already in use, exiting for monitor restart`, { port });\n process.exit(1);\n }\n });\n server.listen(port, host);\n attachWebSocket(server as any, metricsStore);\n\n // Graceful shutdown\n const shutdown = async () => {\n if (configWatcher) {\n configWatcher.close();\n configWatcher = null;\n }\n await handle.closeAgents();\n await removeWorkerPidFile();\n logStream.end();\n process.exit(0);\n };\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n return; // Don't fall through to foreground mode\n }\n\n // --- Foreground mode ---\n const handle = createApp(config, logLevel, metricsStore);\n\n // Print startup info\n console.log(`\\n ModelWeaver v${VERSION}`);\n console.log(` Listening: http://${host}:${port}`);\n console.log(` Config: ${configPath}\\n`);\n\n console.log(\" Routes:\");\n for (const [tier, entries] of config.routing) {\n const providerList = entries\n .map((e, i) => `${e.provider}${i === 0 ? \" (primary)\" : \" (fallback)\"}`)\n .join(\", \");\n console.log(` ${tier.padEnd(8)} → ${providerList}`);\n }\n console.log();\n\n if (config.modelRouting.size > 0) {\n console.log(\" Model Routes:\");\n for (const [model, entries] of config.modelRouting) {\n const providerList = entries\n .map((e, i) => `${e.provider}${i === 0 ? \" (primary)\" : \" (fallback)\"}`)\n .join(\", \");\n console.log(` ${model.padEnd(20)} → ${providerList}`);\n }\n console.log();\n }\n\n // Start server — register error handler BEFORE listen() so EADDRINUSE is caught\n const server = createAdaptorServer({\n fetch: handle.app.fetch,\n hostname: host,\n port,\n serverOptions: {\n requestTimeout: 300_000, // 5 min max total request time (covers long SSE streams)\n headersTimeout: 10_000, // 10s to receive headers\n keepAliveTimeout: 30_000, // 30s keep-alive (matches undici agent)\n },\n });\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.error(`Port ${port} already in use, exiting for monitor restart`);\n process.exit(1);\n }\n });\n server.listen(port, host);\n attachWebSocket(server as any, metricsStore);\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log(\"\\n Shutting down...\");\n await handle.closeAgents();\n process.exit(0);\n };\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n}\n\nmain();\n","// src/server.ts\nimport { Hono } from \"hono\";\nimport { resolveRequest, clearRoutingCache } from \"./router.js\";\nimport { forwardWithFallback } from \"./proxy.js\";\nimport { createLogger, type LogLevel } from \"./logger.js\";\nimport type { AppConfig, ProviderConfig, RequestContext } from \"./types.js\";\nimport { randomUUID } from \"node:crypto\";\nimport { gzip } from \"node:zlib\";\nimport { promisify } from \"node:util\";\n\nconst gzipAsync = promisify(gzip);\nimport type { MetricsStore } from \"./metrics.js\";\nimport { broadcastStreamEvent } from \"./ws.js\";\nimport type { StreamEvent } from \"./types.js\";\n\nconst MODEL_CONTEXT_WINDOWS: Record<string, number> = {\n 'claude-opus-4-6': 200000,\n 'claude-sonnet-4-6': 200000,\n 'claude-haiku-4-5-20251001': 200000,\n 'claude-3-5-sonnet': 200000,\n 'claude-3-5-haiku': 200000,\n 'glm-4.7': 128000,\n 'glm-5-turbo': 128000,\n};\n\nfunction getContextWindow(model: string): number {\n // Exact match first, then prefix match\n if (MODEL_CONTEXT_WINDOWS[model]) return MODEL_CONTEXT_WINDOWS[model];\n for (const [key, size] of Object.entries(MODEL_CONTEXT_WINDOWS)) {\n if (model.startsWith(key)) return size;\n }\n return 0;\n}\n\nfunction computeCacheHitRate(cacheRead: number, cacheCreation: number, input: number): number {\n const totalInput = input + cacheRead + cacheCreation;\n if (totalInput <= 0) return 0;\n return Math.round((cacheRead / totalInput) * 1000) / 10;\n}\n\nfunction computeContextPercent(input: number, cacheRead: number, cacheCreation: number, output: number, contextWindow: number): number {\n if (contextWindow <= 0) return 0;\n const total = input + cacheRead + cacheCreation + output;\n return Math.round((total / contextWindow) * 1000) / 10;\n}\n\nfunction anthropicError(type: string, message: string, requestId: string): Response {\n return new Response(\n JSON.stringify({ type: \"error\", error: { type, message } }),\n {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"x-request-id\": requestId,\n },\n }\n );\n}\n\n/**\n * Parse token counts from an SSE data line's JSON payload.\n * Supports both Anthropic (input_tokens/output_tokens) and OpenAI (prompt_tokens/completion_tokens) formats.\n */\nfunction parseUsageFromData(data: Record<string, unknown>): { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheCreationTokens: number } {\n const usage = (data.message as Record<string, unknown> | undefined)?.usage as Record<string, unknown> | undefined\n ?? data.usage as Record<string, unknown> | undefined;\n if (!usage) return { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0 };\n\n const inp = (usage.input_tokens as number | undefined) ?? (usage.prompt_tokens as number | undefined) ?? 0;\n const out = (usage.output_tokens as number | undefined) ?? (usage.completion_tokens as number | undefined) ?? 0;\n const cacheRead = (usage.cache_read_input_tokens as number | undefined) ?? 0;\n const cacheCreation = (usage.cache_creation_input_tokens as number | undefined) ?? 0;\n\n return { inputTokens: inp, outputTokens: out, cacheReadTokens: cacheRead, cacheCreationTokens: cacheCreation };\n}\n\n/**\n * Creates a TransformStream that forwards chunks unchanged while extracting\n * token counts for metrics inline (no tee() or separate reader needed).\n * For SSE responses, extracts token counts from usage events incrementally.\n * For non-streaming JSON responses, uses a bounded sliding-window regex scan.\n */\nfunction createMetricsTransform(\n ctx: { requestId: string; model: string; actualModel?: string; tier: string; startTime: number; fallbackMode?: \"sequential\" | \"race\" },\n provider: string,\n targetProvider: string,\n metricsStore: MetricsStore,\n status: number,\n contentType: string,\n): TransformStream<Uint8Array, Uint8Array> {\n const td = new TextDecoder();\n\n // --- SSE state ---\n const tokens = { input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };\n let lineBuf = \"\";\n let eventBuf = \"\";\n\n // --- JSON state ---\n const WINDOW_SIZE = 4096;\n let inputTokens = 0;\n let cacheReadTokens = 0;\n let cacheCreationTokens = 0;\n let outputTokens = 0;\n let windowBuf = \"\";\n\n // Detection: resolved after the first chunk arrives\n let isSSE: boolean | null = null;\n\n // Stream event throttling (~4 Hz)\n const STREAM_THROTTLE_MS = 250;\n let lastStreamEmit = 0;\n let firstChunk = true;\n\n // Response text preview (last 100 chars for progress bar tooltip)\n let responsePreview = \"\";\n const PREVIEW_MAX = 100;\n\n const drainEvents = (eventText: string) => {\n for (const event of eventText.split(\"\\n\\n\")) {\n if (!event) continue;\n const dataLine = event.split(\"\\n\").find(l => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n try {\n const data = JSON.parse(dataLine.slice(5)) as Record<string, unknown>;\n\n // Extract usage (token counts)\n if (dataLine.includes('\"usage\"')) {\n const usage = parseUsageFromData(data);\n if (usage.inputTokens > tokens.input) tokens.input = usage.inputTokens;\n if (usage.outputTokens > tokens.output) tokens.output = usage.outputTokens;\n if (usage.cacheReadTokens > tokens.cacheRead) tokens.cacheRead = usage.cacheReadTokens;\n if (usage.cacheCreationTokens > tokens.cacheCreation) tokens.cacheCreation = usage.cacheCreationTokens;\n }\n\n // Extract text content for preview\n // Anthropic format: content_block_delta with delta.text\n const delta = data.delta as Record<string, unknown> | undefined;\n if (delta && typeof delta.text === \"string\") {\n responsePreview += delta.text;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n // OpenAI format: choices[0].delta.content\n const choices = data.choices as Array<Record<string, unknown>> | undefined;\n if (choices?.[0]) {\n const choiceDelta = choices[0].delta as Record<string, unknown> | undefined;\n if (choiceDelta && typeof choiceDelta.content === \"string\") {\n responsePreview += choiceDelta.content;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n }\n } catch { /* skip malformed */ }\n }\n };\n\n const scanWindow = (text: string) => {\n // Fast bailout: most chunks don't contain usage data\n if (!text.includes('\"usage\"')) {\n // Extract text content for preview from JSON responses even without usage\n const anthContent = [...text.matchAll(/\"text\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/g)];\n if (anthContent.length > 0) {\n const lastText = anthContent[anthContent.length - 1][1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n responsePreview += lastText;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n return;\n }\n\n const inputMatches = [...text.matchAll(/\"(?:input_tokens|prompt_tokens)\"\\s*:\\s*(\\d+)/g)];\n const cacheReadMatches = [...text.matchAll(/\"cache_read_input_tokens\"\\s*:\\s*(\\d+)/g)];\n const cacheCreationMatches = [...text.matchAll(/\"cache_creation_input_tokens\"\\s*:\\s*(\\d+)/g)];\n const outputMatches = [...text.matchAll(/\"(?:output_tokens|completion_tokens)\"\\s*:\\s*(\\d+)/g)];\n\n if (inputMatches.length > 0) {\n const val = parseInt(inputMatches[inputMatches.length - 1][1], 10);\n if (val > inputTokens) inputTokens = val;\n }\n if (cacheReadMatches.length > 0) {\n const val = parseInt(cacheReadMatches[cacheReadMatches.length - 1][1], 10);\n if (val > cacheReadTokens) cacheReadTokens = val;\n }\n if (cacheCreationMatches.length > 0) {\n const val = parseInt(cacheCreationMatches[cacheCreationMatches.length - 1][1], 10);\n if (val > cacheCreationTokens) cacheCreationTokens = val;\n }\n if (outputMatches.length > 0) {\n const val = parseInt(outputMatches[outputMatches.length - 1][1], 10);\n if (val > outputTokens) outputTokens = val;\n }\n\n // Extract text content for preview from JSON responses\n // Anthropic format: \"content\":[{\"type\":\"text\",\"text\":\"...\"}]\n const anthContent = [...text.matchAll(/\"text\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/g)];\n if (anthContent.length > 0) {\n const lastText = anthContent[anthContent.length - 1][1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n responsePreview += lastText;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n };\n\n const recordMetrics = (inp: number, out: number, cacheRead: number = 0, cacheCreation: number = 0) => {\n try {\n const latencyMs = Date.now() - ctx.startTime;\n const latencySec = latencyMs / 1000;\n const tps = latencySec > 0 ? out / latencySec : 0;\n\n metricsStore.recordRequest({\n requestId: ctx.requestId,\n model: ctx.model,\n actualModel: ctx.actualModel || ctx.model,\n tier: ctx.tier,\n provider,\n targetProvider,\n status,\n inputTokens: inp,\n outputTokens: out,\n latencyMs,\n tokensPerSec: Math.round(tps * 10) / 10,\n timestamp: Date.now(),\n fallbackMode: ctx.fallbackMode,\n cacheReadTokens: cacheRead,\n cacheCreationTokens: cacheCreation,\n });\n\n // Broadcast completion event\n const contextWindow = getContextWindow(ctx.actualModel || ctx.model);\n setImmediate(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: ctx.model,\n tier: ctx.tier,\n state: \"complete\",\n status,\n latencyMs: Date.now() - ctx.startTime,\n inputTokens: inp,\n outputTokens: out,\n tokensPerSec: Math.round(tps * 10) / 10,\n timestamp: Date.now(),\n cacheReadTokens: cacheRead,\n cacheCreationTokens: cacheCreation,\n cacheHitRate: computeCacheHitRate(cacheRead, cacheCreation, inp),\n contextPercent: computeContextPercent(inp, cacheRead, cacheCreation, out, contextWindow),\n contextWindowSize: contextWindow || undefined,\n });\n });\n } catch {\n // Metrics recording errors must not affect the response stream\n }\n };\n\n const processChunk = (decoded: string, isFinal: boolean) => {\n if (isSSE === null) {\n // First chunk — detect format\n isSSE = contentType.includes(\"text/event-stream\") || decoded.startsWith(\"event:\");\n }\n\n if (isSSE) {\n lineBuf += decoded;\n const lines = lineBuf.split(\"\\n\");\n lineBuf = lines.pop()!;\n\n for (const line of lines) {\n if (line === \"\") {\n if (eventBuf) {\n drainEvents(eventBuf);\n eventBuf = \"\";\n }\n } else {\n eventBuf += (eventBuf ? \"\\n\" : \"\") + line;\n }\n }\n\n if (isFinal && eventBuf.trim()) drainEvents(eventBuf);\n\n // Emit streaming progress (throttled ~4 Hz)\n const now = Date.now();\n if (firstChunk || now - lastStreamEmit >= STREAM_THROTTLE_MS) {\n lastStreamEmit = now;\n firstChunk = false;\n const contextWindow = getContextWindow(ctx.actualModel || ctx.model);\n setImmediate(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: ctx.model,\n tier: ctx.tier,\n state: \"streaming\",\n outputTokens: tokens.output,\n timestamp: now,\n preview: responsePreview,\n cacheHitRate: computeCacheHitRate(tokens.cacheRead, tokens.cacheCreation, tokens.input),\n contextPercent: computeContextPercent(tokens.input, tokens.cacheRead, tokens.cacheCreation, tokens.output, contextWindow),\n contextWindowSize: contextWindow || undefined,\n });\n });\n }\n\n if (isFinal) {\n recordMetrics(tokens.input, tokens.output, tokens.cacheRead, tokens.cacheCreation);\n }\n } else {\n windowBuf += decoded;\n if (windowBuf.length > WINDOW_SIZE) {\n windowBuf = windowBuf.slice(-WINDOW_SIZE);\n }\n scanWindow(windowBuf);\n\n // Emit streaming progress (throttled ~4 Hz)\n const nowJson = Date.now();\n if (firstChunk || nowJson - lastStreamEmit >= STREAM_THROTTLE_MS) {\n lastStreamEmit = nowJson;\n firstChunk = false;\n const contextWindow = getContextWindow(ctx.actualModel || ctx.model);\n setImmediate(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: ctx.model,\n tier: ctx.tier,\n state: \"streaming\",\n outputTokens,\n timestamp: nowJson,\n preview: responsePreview,\n cacheHitRate: computeCacheHitRate(cacheReadTokens, cacheCreationTokens, inputTokens),\n contextPercent: computeContextPercent(inputTokens, cacheReadTokens, cacheCreationTokens, outputTokens, contextWindow),\n contextWindowSize: contextWindow || undefined,\n });\n });\n }\n\n if (isFinal) {\n recordMetrics(inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens);\n }\n }\n };\n\n return new TransformStream({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n processChunk(td.decode(chunk, { stream: true }), false);\n },\n flush() {\n processChunk(\"\", true);\n },\n });\n}\n\nexport interface AppHandle {\n app: Hono;\n getConfig: () => AppConfig;\n setConfig: (config: AppConfig) => Promise<void>;\n closeAgents: () => Promise<void>;\n}\n\nfunction agentKey(provider: ProviderConfig): string {\n const origin = provider._cachedOrigin;\n const size = provider.poolSize ?? 10;\n return `${origin ?? \"unknown\"}:${size}`;\n}\n\nexport function createApp(initConfig: AppConfig, logLevel: LogLevel, metricsStore?: MetricsStore): AppHandle {\n let config: AppConfig = initConfig;\n const logger = createLogger(logLevel);\n const app = new Hono();\n\n // Global error handler — returns Anthropic-compatible JSON error responses\n app.onError((err, c) => {\n console.error(`[server] Unhandled error: ${err.message}`);\n return c.json(\n { type: \"error\", error: { type: \"api_error\", message: \"Internal proxy error\" } },\n { status: 500, headers: { \"content-type\": \"application/json\" } }\n );\n });\n\n // CORS for GUI (Tauri WebView has origin tauri://localhost)\n app.use(\"/api/*\", async (c, next) => {\n c.header(\"Access-Control-Allow-Origin\", \"*\");\n await next();\n });\n // Handle CORS preflight for API routes only (GUI needs CORS; proxy endpoint does not)\n app.options(\"/api/*\", (c) => {\n c.header(\"Access-Control-Allow-Origin\", \"*\");\n c.header(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n c.header(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization, anthropic-version, x-api-key\");\n return c.body(\"\", 200);\n });\n\n app.post(\"/v1/messages\", async (c) => {\n const requestId = randomUUID();\n\n // Read raw body once, then parse — avoids double serialization\n let body: { model?: string };\n let rawBody: string;\n try {\n rawBody = await c.req.text();\n body = JSON.parse(rawBody);\n } catch {\n return anthropicError(\"invalid_request_error\", \"Invalid JSON body\", requestId);\n }\n\n const model = body.model;\n if (!model) {\n return anthropicError(\"invalid_request_error\", \"Missing 'model' field in request body\", requestId);\n }\n\n const ctx = resolveRequest(model, requestId, config, rawBody);\n if (ctx) {\n (ctx as RequestContext & { parsedBody?: Record<string, unknown> }).parsedBody = body as Record<string, unknown>;\n }\n if (!ctx) {\n logger.info(\"No tier match\", { requestId, model });\n const configuredModels = config.modelRouting.size > 0\n ? ` Configured model routes: ${[...config.modelRouting.keys()].join(\", \")}.`\n : \"\";\n return anthropicError(\n \"invalid_request_error\",\n `No route matches model \"${model}\". Configured tiers: ${[...config.tierPatterns.keys()].join(\", \")}.${configuredModels}`,\n requestId\n );\n }\n\n logger.info(\"Routing request\", {\n requestId,\n model,\n tier: ctx.tier,\n providers: ctx.providerChain.map((e) => e.provider),\n });\n\n // Broadcast stream start event\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"start\",\n provider: ctx.providerChain[0]?.provider ?? \"unknown\",\n timestamp: Date.now(),\n });\n\n // Forward with fallback chain\n let successfulProvider = \"unknown\";\n let response: Response;\n try {\n response = await forwardWithFallback(\n config.providers,\n ctx.providerChain,\n ctx,\n c.req.raw,\n (provider, index) => {\n logger.info(\"Attempting provider\", { requestId, provider, index, tier: ctx.tier });\n // Only capture first attempted provider; accurate winner tracking requires\n // an onSuccess callback in proxy.ts (handled separately).\n if (!successfulProvider) successfulProvider = provider;\n },\n logger\n );\n // Broadcast TTFB event — headers received from upstream (skip for error responses)\n if (response.status < 400) {\n let headerSize = 17; // approximate HTTP status line: \"HTTP/1.1 200 OK\\r\\n\"\n response.headers.forEach((v, k) => { headerSize += k.length + v.length + 4; });\n headerSize += 2; // trailing CRLF\n setImmediate(() => {\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"ttfb\",\n status: response.status,\n headerSize,\n timestamp: Date.now(),\n });\n });\n }\n\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n logger.error(\"Forward failed\", { requestId, error: errMsg });\n setImmediate(() => {\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"error\",\n status: 502,\n message: errMsg,\n timestamp: Date.now(),\n });\n });\n return c.json(\n { type: \"error\", error: { type: \"api_error\", message: \"Upstream request failed: \" + errMsg } },\n 502\n );\n }\n\n // Broadcast error event for non-2xx responses\n if (response.status >= 400) {\n setImmediate(() => {\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"error\",\n status: response.status,\n message: `HTTP ${response.status}`,\n timestamp: Date.now(),\n });\n });\n }\n\n // Extract tokens via inline TransformStream for successful responses\n let responseBody: ReadableStream<Uint8Array> | null = response.body;\n if (response.body && response.status >= 200 && response.status < 300 && metricsStore) {\n const targetProvider = ctx.providerChain.length > 0 ? ctx.providerChain[0].provider : successfulProvider;\n const transform = createMetricsTransform(ctx, successfulProvider, targetProvider, metricsStore, response.status, response.headers.get(\"content-type\") || \"\");\n responseBody = response.body.pipeThrough(transform) as typeof responseBody;\n }\n\n // Add request ID to response (responses from fetch have immutable headers, so create new)\n const newHeaders = new Headers(response.headers);\n newHeaders.set(\"x-request-id\", requestId);\n const finalResponse = new Response(responseBody, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n });\n\n const latency = Date.now() - ctx.startTime;\n logger.info(\"Request completed\", {\n requestId,\n model,\n tier: ctx.tier,\n status: finalResponse.status,\n latencyMs: latency,\n });\n\n return finalResponse;\n });\n\n // REST endpoint for metrics summary (used by GUI on connect)\n // Returns gzip-compressed JSON when client supports it\n app.get(\"/api/metrics/summary\", async (c) => {\n if (!metricsStore) return c.json({ error: \"Metrics not enabled\" }, 503);\n const data = metricsStore.getSummary();\n const json = JSON.stringify(data);\n\n const acceptEncoding = c.req.header(\"accept-encoding\") || \"\";\n if (acceptEncoding.includes(\"gzip\") && json.length >= 1024) {\n const compressed = await gzipAsync(Buffer.from(json));\n return new Response(compressed, {\n status: 200,\n headers: {\n \"content-type\": \"application/json\",\n \"content-encoding\": \"gzip\",\n \"vary\": \"accept-encoding\",\n },\n });\n }\n\n return c.json(data);\n });\n\n // Circuit breaker status endpoint\n app.get(\"/api/circuit-breaker\", (c) => {\n const status: Record<string, { state: string; failures: number; lastFailure: string | null }> = {};\n for (const [name, provider] of config.providers) {\n const breaker = provider._circuitBreaker;\n if (breaker) {\n const s = breaker.getStatus();\n status[name] = {\n state: s.state,\n failures: s.failures,\n lastFailure: s.lastFailure ? new Date(s.lastFailure).toISOString() : null,\n };\n }\n }\n return c.json(status);\n });\n\n return {\n app,\n getConfig: () => config,\n setConfig: async (newConfig: AppConfig) => {\n // Build key → agent map from old config for reuse lookup\n const oldAgents = new Map<string, import(\"undici\").Agent>();\n for (const provider of config.providers.values()) {\n if (provider._agent) {\n oldAgents.set(agentKey(provider), provider._agent);\n }\n }\n\n // For each new provider, check if we can reuse an existing agent\n const reusedKeys = new Set<string>();\n for (const provider of newConfig.providers.values()) {\n const key = agentKey(provider);\n const existingAgent = oldAgents.get(key);\n if (existingAgent) {\n // Reuse: the origin and poolSize haven't changed\n provider._agent = existingAgent;\n reusedKeys.add(key);\n }\n // else: loadConfig() already created a fresh agent for this provider\n }\n\n // Close agents that are no longer needed (removed or changed origin/poolSize)\n const closePromises: Promise<void>[] = [];\n for (const [key, agent] of oldAgents) {\n if (!reusedKeys.has(key)) {\n closePromises.push(agent.close().catch(() => {}));\n }\n }\n\n config = newConfig;\n clearRoutingCache();\n await Promise.all(closePromises);\n },\n closeAgents: async () => {\n const closePromises: Promise<void>[] = [];\n for (const provider of config.providers.values()) {\n if (provider._agent) {\n closePromises.push(provider._agent.close().catch(() => {}));\n }\n }\n await Promise.all(closePromises);\n },\n };\n}\n","// src/router.ts\nimport type { RoutingEntry, AppConfig, RequestContext } from \"./types.js\";\n\nconst ROUTING_CACHE_MAX_SIZE = 200;\n\ninterface RoutingCacheEntry {\n tier: string;\n providerChain: RoutingEntry[];\n}\n\n/**\n * LRU cache for model-to-(tier, providerChain) lookups.\n * Map insertion order serves as LRU ordering (first = oldest).\n */\nconst routingCache = new Map<string, RoutingCacheEntry>();\n\n/**\n * Invalidate the routing cache. Called on config hot-reload.\n */\nexport function clearRoutingCache(): void {\n routingCache.clear();\n}\n\n/**\n * Match a model name to a tier using case-sensitive substring matching.\n * First tier whose patterns contain any match wins (config order = priority).\n */\nexport function matchTier(\n modelName: string,\n tierPatterns: Map<string, string[]>\n): string | null {\n for (const [tier, patterns] of tierPatterns) {\n for (const pattern of patterns) {\n if (modelName.includes(pattern)) {\n return tier;\n }\n }\n }\n return null;\n}\n\n/**\n * Get the ordered routing chain for a tier.\n */\nexport function buildRoutingChain(\n tier: string,\n routing: Map<string, RoutingEntry[]>\n): RoutingEntry[] {\n return routing.get(tier) || [];\n}\n\n/**\n * Build a RequestContext from an incoming model name and raw body.\n * Priority 1: exact model name match in modelRouting.\n * Priority 2: substring match via tierPatterns.\n * Uses an LRU cache to skip repeated resolution for the same model.\n * Returns null if no route matches.\n */\nexport function resolveRequest(\n model: string,\n requestId: string,\n config: AppConfig,\n rawBody: string\n): RequestContext | null {\n // Check LRU cache first\n const cached = routingCache.get(model);\n if (cached) {\n // Move to most-recently-used position (delete + re-insert)\n routingCache.delete(model);\n routingCache.set(model, cached);\n return {\n requestId,\n model,\n tier: cached.tier,\n providerChain: cached.providerChain,\n startTime: Date.now(),\n rawBody,\n };\n }\n\n let tier: string;\n let providerChain: RoutingEntry[];\n\n // Priority 1: exact model name match in modelRouting\n const modelChain = config.modelRouting.get(model);\n if (modelChain && modelChain.length > 0) {\n tier = \"(modelRouting)\";\n providerChain = modelChain;\n } else {\n // Priority 2: substring match via tierPatterns (existing behavior)\n const matchedTier = matchTier(model, config.tierPatterns);\n if (!matchedTier) return null;\n tier = matchedTier;\n providerChain = buildRoutingChain(tier, config.routing);\n }\n\n // Cache the resolved tier + providerChain\n if (routingCache.size >= ROUTING_CACHE_MAX_SIZE) {\n // Evict the oldest entry (first key in Map)\n const oldestKey = routingCache.keys().next().value;\n if (oldestKey !== undefined) routingCache.delete(oldestKey);\n }\n routingCache.set(model, { tier, providerChain });\n\n return {\n requestId,\n model,\n tier,\n providerChain,\n startTime: Date.now(),\n rawBody,\n };\n}\n","// src/proxy.ts\nimport type { ProviderConfig, RoutingEntry, RequestContext } from \"./types.js\";\nimport { request as undiciRequest } from \"undici\";\nimport { PassThrough } from \"node:stream\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { latencyTracker, inFlightCounter, computeHedgingCount } from './hedging.js';\nimport { broadcastStreamEvent } from './ws.js';\n\n/**\n * Shallow-clone a parsed API request body just enough so that\n * cleanOrphanedToolMessages() can safely reassign body.messages\n * without affecting the original parsed object.\n *\n * cleanOrphanedToolMessages either:\n * - leaves body.messages untouched (no orphans found), or\n * - replaces body.messages with a new array (via .map())\n * It never mutates individual message objects in-place.\n * Therefore a one-level-deep clone of the messages array is sufficient.\n */\nfunction shallowCloneForMutation(parsed: Record<string, unknown>): Record<string, unknown> {\n const clone = { ...parsed };\n if (Array.isArray(clone.messages)) {\n clone.messages = [...clone.messages];\n }\n return clone;\n}\n\n/** Headers forwarded as-is to upstream */\nconst FORWARD_HEADERS = new Set([\n \"anthropic-version\",\n \"anthropic-beta\",\n \"content-type\",\n \"accept\",\n]);\n\n/** Pre-compiled regex for normalizing duplicate slashes in URL paths */\nconst MULTI_SLASH = /\\/+/g;\n\n/** Pre-compiled regex for stripping origin from URLs */\nconst STRIP_ORIGIN = /^https?:\\/\\/[^/]+/;\n\n/** Pre-compiled regexes for targeted body replacements (preserve prompt caching) */\nconst MODEL_KEY_REGEX = /\"model\"\\s*:\\s*\"([^\"]*)\"/;\nconst MAX_TOKENS_REGEX = /\"max_tokens\"\\s*:\\s*(\\d+)/;\n\n/** Module-level TextEncoder — avoids per-request allocation */\nconst textEncoder = new TextEncoder();\n\n/** Delay (ms) before starting backup providers in staggered race */\nconst SPECULATIVE_DELAY = 3000;\n\nexport function isRetriable(status: number): boolean {\n return status === 429 || status >= 500;\n}\n\nconst CONTEXT_WINDOW_PATTERNS = [\n 'context window', 'context_limit', 'token limit',\n 'prompt is too long', 'max tokens', 'input too large', 'too many tokens',\n];\n\nfunction isContextWindowError(status: number, body: string): boolean {\n if (status !== 400 && status !== 413) return false;\n const lower = body.toLowerCase();\n return CONTEXT_WINDOW_PATTERNS.some(p => lower.includes(p));\n}\n\nfunction handleContextWindowError(status: number, body: string): Response | null {\n if (!isContextWindowError(status, body)) return null;\n\n console.warn('[context-compact] Upstream context window limit detected');\n try {\n const flagDir = path.join(os.homedir(), '.claude', 'state');\n fs.mkdirSync(flagDir, { recursive: true });\n fs.writeFileSync(path.join(flagDir, 'context-compact-needed'), Date.now().toString());\n } catch {\n // Best-effort flag write\n }\n\n const enhanced = JSON.stringify({\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: \"Context window limit reached. Run /compact to reduce conversation size, then retry.\",\n },\n });\n return new Response(enhanced, {\n status: 413,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\nexport function buildOutboundUrl(baseUrl: string, incomingPath: string): string {\n let basePath = \"\";\n let origin = baseUrl;\n const slashIndex = baseUrl.indexOf('/', baseUrl.indexOf('//') + 2);\n if (slashIndex !== -1) {\n origin = baseUrl.substring(0, slashIndex);\n basePath = baseUrl.substring(slashIndex);\n }\n\n let incomingQuery = \"\";\n let incomingOnly = incomingPath;\n const qIndex = incomingPath.indexOf('?');\n if (qIndex !== -1) {\n incomingOnly = incomingPath.substring(0, qIndex);\n incomingQuery = incomingPath.substring(qIndex);\n }\n\n // Deduplicate /v1 when base URL path already ends with it and incoming path starts with it.\n // e.g. baseUrl=\"https://api.fireworks.ai/inference/v1\" + path=\"/v1/chat/completions\"\n // → \"/inference/v1/chat/completions\" (not \"/inference/v1/v1/chat/completions\")\n let resolvedPath;\n if (basePath.endsWith('/v1') && incomingOnly.startsWith('/v1')) {\n resolvedPath = basePath + incomingOnly.substring(3);\n } else {\n resolvedPath = basePath + incomingOnly;\n }\n\n // Normalize duplicate slashes\n resolvedPath = resolvedPath.replace(MULTI_SLASH, \"/\");\n\n return origin + resolvedPath + incomingQuery;\n}\n\nexport function buildOutboundHeaders(\n incomingHeaders: Headers,\n provider: ProviderConfig,\n requestId: string\n): Headers {\n const headers = new Headers();\n\n // Forward select headers as-is\n for (const name of FORWARD_HEADERS) {\n const value = incomingHeaders.get(name);\n if (value) headers.set(name, value);\n }\n\n // Rewrite auth headers based on provider authType\n if (provider.authType === \"bearer\") {\n headers.set(\"Authorization\", `Bearer ${provider.apiKey}`);\n } else {\n headers.set(\"x-api-key\", provider.apiKey);\n }\n headers.set(\"x-request-id\", requestId);\n\n // Set host to provider hostname (use cached components when available)\n const cachedHost = provider._cachedHost;\n if (cachedHost) {\n headers.set(\"host\", cachedHost);\n } else {\n try {\n const url = new URL(provider.baseUrl);\n headers.set(\"host\", url.host);\n } catch {\n // If baseUrl is not a valid URL, skip host rewrite\n }\n }\n\n return headers;\n}\n\n/**\n * Remove orphaned tool_use/tool_result pairs from the messages array.\n *\n * In Anthropic's format:\n * - tool_use blocks live inside assistant message content: { role: \"assistant\", content: [{ type: \"tool_use\", id: \"call_xxx\", ... }] }\n * - tool_result blocks live inside user message content: { role: \"user\", content: [{ type: \"tool_result\", tool_use_id: \"call_xxx\", ... }] }\n *\n * A tool_result is orphaned if its tool_use_id references a tool_use not in any assistant content block.\n * A tool_use is orphaned if its id has no matching tool_result in any user content block.\n *\n * Single-pass: collects all IDs, computes orphans, then filters once.\n */\nfunction cleanOrphanedToolMessages(body: Record<string, unknown>): void {\n const messages = body.messages;\n if (!Array.isArray(messages)) return;\n\n const allToolUseIds = new Set<string>();\n const allToolResultIds = new Set<string>();\n\n // Single collection pass — gather every tool_use and tool_result ID\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (!Array.isArray(msg.content)) continue;\n\n for (const block of msg.content) {\n if (block.type === \"tool_use\" && block.id) {\n allToolUseIds.add(String(block.id));\n } else if (block.type === \"tool_result\" && block.tool_use_id) {\n allToolResultIds.add(String(block.tool_use_id));\n }\n }\n }\n\n // Compute orphaned IDs: tool_use without a matching tool_result, and vice versa.\n // A tool_use is kept if ANY tool_result references it (even if that tool_result\n // itself is orphaned — we only strip truly dangling references).\n const orphanedToolUseIds = new Set<string>();\n const orphanedToolResultIds = new Set<string>();\n for (const id of allToolUseIds) {\n if (!allToolResultIds.has(id)) orphanedToolUseIds.add(id);\n }\n for (const id of allToolResultIds) {\n if (!allToolUseIds.has(id)) orphanedToolResultIds.add(id);\n }\n\n if (orphanedToolUseIds.size === 0 && orphanedToolResultIds.size === 0) return;\n\n // Single filter pass — remove orphaned blocks from content arrays\n let changed = false;\n const cleaned = messages.map((msg: Record<string, unknown>) => {\n if (!Array.isArray(msg.content)) return msg;\n\n const filtered = msg.content.filter((block: Record<string, unknown>) => {\n if (block.type === \"tool_use\" && orphanedToolUseIds.has(String(block.id))) return false;\n if (block.type === \"tool_result\" && orphanedToolResultIds.has(String(block.tool_use_id))) return false;\n return true;\n });\n\n if (filtered.length < msg.content.length) {\n changed = true;\n return { ...msg, content: filtered };\n }\n return msg;\n });\n\n if (changed) {\n body.messages = cleaned;\n }\n}\n\n/**\n * Apply targeted string replacements to rawBody to preserve prompt caching.\n * On the primary attempt (chainIndex === 0), we avoid full JSON.stringify which\n * breaks Anthropic's cache breakpoints (position-sensitive, whitespace/order-sensitive).\n * Falls back to full JSON parse/mutate/stringify when structural changes are needed.\n */\nfunction applyTargetedReplacements(\n rawBody: string,\n entry: RoutingEntry,\n provider: ProviderConfig,\n parsed: Record<string, unknown>,\n needsOrphanClean: boolean,\n): string {\n // If orphan cleaning is needed, we must do full JSON parse (structural changes to messages)\n if (needsOrphanClean) {\n // shallow clone: cleanOrphanedToolMessages reassigns body.messages\n const mutable = shallowCloneForMutation(parsed);\n if (entry.model) mutable.model = entry.model;\n cleanOrphanedToolMessages(mutable);\n if (provider.modelLimits) {\n const { maxOutputTokens } = provider.modelLimits;\n const requested = typeof mutable.max_tokens === \"number\" ? mutable.max_tokens : maxOutputTokens;\n if (mutable.max_tokens === undefined || requested > maxOutputTokens) {\n mutable.max_tokens = Math.min(requested, maxOutputTokens);\n }\n }\n return JSON.stringify(mutable);\n }\n\n // Targeted replacement path -- only model override and/or max_tokens clamping.\n // Single-pass: build a combined regex with alternation so the entire raw body\n // string is scanned and replaced in one call instead of per-pattern copies.\n const needsModel = !!(entry.model && (parsed.model as string | undefined) !== entry.model);\n let needsMaxTokensClamp = false;\n let needsMaxTokensAdd = false;\n let maxOutputTokens = 0;\n if (provider.modelLimits) {\n maxOutputTokens = provider.modelLimits.maxOutputTokens;\n const m = MAX_TOKENS_REGEX.exec(rawBody);\n if (m) {\n needsMaxTokensClamp = parseInt(m[1], 10) > maxOutputTokens;\n } else if (typeof parsed.max_tokens !== \"number\") {\n needsMaxTokensAdd = true;\n }\n }\n\n if (!needsModel && !needsMaxTokensClamp && !needsMaxTokensAdd) return rawBody;\n\n // max_tokens not in body at all — must add via JSON parse (structural change)\n if (needsMaxTokensAdd) {\n const mutable = { ...parsed };\n if (entry.model) mutable.model = entry.model;\n mutable.max_tokens = maxOutputTokens;\n return JSON.stringify(mutable);\n }\n\n // Build combined regex for single-pass replacement\n const patterns: string[] = [];\n if (needsModel) patterns.push(MODEL_KEY_REGEX.source);\n if (needsMaxTokensClamp) patterns.push(MAX_TOKENS_REGEX.source);\n const combinedRegex = new RegExp(patterns.join(\"|\"), \"g\");\n\n // Capture values for the replacer (avoid repeated property access)\n const modelRepl = needsModel ? `\"model\":\"${entry.model}\"` : null;\n const tokensRepl = needsMaxTokensClamp ? `\"max_tokens\":${maxOutputTokens}` : null;\n const origModel = parsed.model as string | undefined;\n let modelLogged = false;\n\n const result = rawBody.replace(combinedRegex, (match: string) => {\n if (modelRepl && MODEL_KEY_REGEX.test(match)) {\n MODEL_KEY_REGEX.lastIndex = 0;\n if (!modelLogged && origModel) {\n console.warn(`Routing override: ${origModel} -> ${entry.model} via ${provider.name}`);\n modelLogged = true;\n }\n return modelRepl;\n }\n if (tokensRepl && MAX_TOKENS_REGEX.test(match)) {\n MAX_TOKENS_REGEX.lastIndex = 0;\n return tokensRepl;\n }\n return match;\n });\n\n return result;\n}\n\n/**\n * Forward a request to a single provider.\n * Uses ctx.parsedBody when available (avoids re-parsing); falls back to ctx.rawBody.\n * incomingRequest is used for metadata only (url, headers).\n * Returns the Response object — caller decides fallback logic.\n */\nexport async function forwardRequest(\n provider: ProviderConfig,\n entry: RoutingEntry,\n ctx: RequestContext,\n incomingRequest: Request,\n externalSignal?: AbortSignal,\n chainIndex: number = 0,\n): Promise<Response> {\n const outgoingPath = incomingRequest.url.replace(STRIP_ORIGIN, \"\");\n\n // Set actualModel early so metrics always record the routed model,\n // even if body parsing or the fetch itself fails\n if (entry.model) {\n ctx.actualModel = entry.model;\n }\n\n // Build outbound URL from provider base URL and request path\n const url = buildOutboundUrl(provider.baseUrl, outgoingPath);\n\n // Prepare body — prefer raw passthrough to preserve upstream prompt caching.\n // Only parse and re-serialize when a modification is actually required,\n // because Anthropic's cache breakpoints are position-sensitive and\n // JSON.stringify changes whitespace / key order, breaking cache hits.\n let body: string;\n const contentType = incomingRequest.headers.get(\"content-type\") || \"\";\n\n if (contentType.includes(\"application/json\")) {\n try {\n const parsed = (ctx as RequestContext & { parsedBody?: Record<string, unknown> }).parsedBody\n ?? JSON.parse(ctx.rawBody);\n\n // Determine whether any body modification is needed\n let needsModification = false;\n\n // Check 1: Model override needed?\n if (entry.model && (parsed.model as string | undefined) !== entry.model) {\n needsModification = true;\n }\n\n // Check 2: Orphan cleaning needed? (only for fallback attempts, not primary)\n // On the primary attempt (index 0), conversation history is intact.\n // Only when falling back (index > 0) do cross-provider orphans appear.\n const needsOrphanClean = chainIndex > 0;\n if (needsOrphanClean) needsModification = true;\n\n // Check 3: max_tokens clamping needed?\n if (provider.modelLimits) {\n const { maxOutputTokens } = provider.modelLimits;\n const requestedMaxTokens = typeof parsed.max_tokens === \"number\" ? parsed.max_tokens : maxOutputTokens;\n if (parsed.max_tokens === undefined || requestedMaxTokens > maxOutputTokens) {\n needsModification = true;\n }\n }\n\n if (needsModification) {\n // On primary attempt (chainIndex === 0) without orphan cleaning, use targeted\n // string replacements to preserve prompt caching. Anthropic's cache breakpoints\n // are position-sensitive -- JSON.stringify changes whitespace/order, breaking hits.\n if (chainIndex === 0 && !needsOrphanClean) {\n body = applyTargetedReplacements(ctx.rawBody, entry, provider, parsed, false);\n } else {\n // Fallback attempts: full JSON parse/mutate/stringify (caching already broken)\n // shallow clone: cleanOrphanedToolMessages reassigns body.messages\n const mutable = shallowCloneForMutation(parsed);\n\n if (entry.model) {\n const originalModel = mutable.model as string | undefined;\n mutable.model = entry.model;\n if (originalModel && originalModel !== entry.model) {\n console.warn(\n `Routing override: ${originalModel} -> ${entry.model} via ${provider.name}`\n );\n }\n }\n\n if (needsOrphanClean) {\n cleanOrphanedToolMessages(mutable);\n }\n\n if (provider.modelLimits) {\n const { maxOutputTokens } = provider.modelLimits;\n const requestedMaxTokens = typeof mutable.max_tokens === \"number\" ? mutable.max_tokens : maxOutputTokens;\n if (mutable.max_tokens === undefined || requestedMaxTokens > maxOutputTokens) {\n mutable.max_tokens = Math.min(requestedMaxTokens, maxOutputTokens);\n }\n }\n\n body = JSON.stringify(mutable);\n }\n } else {\n // No modifications needed — passthrough raw body to preserve prompt caching\n body = ctx.rawBody;\n }\n } catch {\n // If body can't be parsed, send it as-is without model override\n body = ctx.rawBody;\n }\n } else {\n body = ctx.rawBody;\n }\n\n const headers = buildOutboundHeaders(incomingRequest.headers, provider, ctx.requestId);\n headers.set(\"content-length\", Buffer.byteLength(body, 'utf-8').toString());\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), provider.timeout);\n\n // TTFB timeout: fail if no response headers received within ttfbTimeout ms\n const ttfbTimeout = provider.ttfbTimeout ?? 15000;\n let ttfbTimedOut = false;\n let ttfbTimer: ReturnType<typeof setTimeout> | null = null;\n\n const ttfbPromise = new Promise<never>((_, reject) => {\n ttfbTimer = setTimeout(() => {\n ttfbTimedOut = true;\n controller.abort();\n reject(new Error(`TTFB timeout after ${ttfbTimeout}ms`));\n }, ttfbTimeout);\n });\n\n // Listen for external abort (from race cancellation) to abort this request\n let removeAbortListener: (() => void) | undefined;\n let upstreamBody: import(\"node:stream\").Readable | undefined;\n let passThrough: PassThrough | undefined;\n let stallTimerRef: ReturnType<typeof setTimeout> | undefined;\n if (externalSignal) {\n if (externalSignal.aborted) {\n // Already aborted — don't even start the request\n clearTimeout(timeout);\n if (ttfbTimer) clearTimeout(ttfbTimer);\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: `Provider \"${provider.name}\" cancelled by race winner` },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n }\n // Increase max listeners to prevent Node.js warning when multiple providers race\n // and all listen to the same sharedController.signal\n const prevMax = (externalSignal as any).getMaxListeners?.() ?? 10;\n (externalSignal as any).setMaxListeners?.(prevMax + 1);\n const onExternalAbort = () => {\n clearTimeout(timeout);\n if (ttfbTimer) clearTimeout(ttfbTimer);\n if (stallTimerRef) clearTimeout(stallTimerRef);\n // Destroy upstream body and passThrough to free the connection back to the pool.\n // Deferred to avoid throwing inside AbortSignal event dispatch.\n setImmediate(() => {\n if (upstreamBody && !upstreamBody.destroyed && !(upstreamBody as any).readableEnded) {\n try { (upstreamBody.destroy() as any).catch?.(() => {}); } catch { /* already consumed */ }\n }\n if (passThrough && !passThrough.destroyed) {\n passThrough.destroy(new Error(\"Cancelled\"));\n }\n });\n };\n externalSignal.addEventListener(\"abort\", onExternalAbort, { once: true });\n removeAbortListener = () => {\n externalSignal.removeEventListener(\"abort\", onExternalAbort);\n (externalSignal as any).setMaxListeners?.((externalSignal as any).getMaxListeners?.() - 1);\n };\n }\n\n try {\n const undiciResponse = await Promise.race([\n undiciRequest(url, {\n method: \"POST\",\n headers,\n body,\n signal: controller.signal,\n dispatcher: provider._agent,\n }),\n ttfbPromise,\n ]);\n\n // TTFB succeeded — cancel TTFB timer\n if (ttfbTimer) clearTimeout(ttfbTimer);\n\n // Track upstream body for cleanup on error paths\n upstreamBody = undiciResponse.body;\n\n // Guard against uncaught error events when the pipe is torn down\n // after passThrough.destroy() fires before upstreamBody.destroy().\n upstreamBody.on(\"error\", () => {\n clearTimeout(stallTimerRef);\n });\n\n // For error status codes (4xx/5xx), consume body immediately without stall detection.\n // Rate limits (429) and server errors (5xx) return small JSON bodies that arrive instantly.\n if (undiciResponse.statusCode >= 400) {\n clearTimeout(timeout);\n const errBody = await undiciResponse.body.text();\n return new Response(errBody, {\n status: undiciResponse.statusCode,\n statusText: undiciResponse.statusText,\n headers: undiciResponse.headers as unknown as HeadersInit,\n });\n }\n\n // Body stall detection: pipe through PassThrough to monitor for data without\n // interfering with undici's internal stream state (no flowing mode conflict).\n const stallTimeout = provider.stallTimeout ?? 30000;\n passThrough = new PassThrough();\n\n const stallMsg = `Body stalled: no data after ${stallTimeout}ms`;\n stallTimerRef = setTimeout(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: String(ctx.actualModel ?? entry.model ?? \"\"),\n tier: \"\",\n state: \"error\",\n message: stallMsg,\n timestamp: Date.now(),\n });\n try { (upstreamBody?.destroy(new Error(stallMsg)) as any).catch?.(() => {}); } catch { /* already consumed */ }\n passThrough!.destroy(new Error(stallMsg));\n }, stallTimeout);\n\n // Monitor OUR PassThrough for every data event — re-arms stall timer on each chunk\n passThrough!.on(\"data\", () => {\n clearTimeout(stallTimerRef);\n stallTimerRef = setTimeout(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: String(ctx.actualModel ?? entry.model ?? \"\"),\n tier: \"\",\n state: \"error\",\n message: stallMsg,\n timestamp: Date.now(),\n });\n try { (upstreamBody?.destroy(new Error(stallMsg)) as any).catch?.(() => {}); } catch { /* already consumed */ }\n passThrough!.destroy(new Error(stallMsg));\n }, stallTimeout);\n });\n\n passThrough.on(\"end\", () => {\n clearTimeout(stallTimerRef);\n });\n\n passThrough.on(\"error\", () => {\n clearTimeout(stallTimerRef);\n try { passThrough!.destroy(); } catch { /* already destroyed */ }\n });\n\n // Pipe undici body → PassThrough. Data flows through without mode conflicts.\n if (!undiciResponse.body || typeof undiciResponse.body.pipe !== 'function') {\n // Non-standard upstream response (e.g., plain text/buffer) — send as-is\n const fallback = undiciResponse.body\n ? new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode(String(undiciResponse.body))); controller.close(); } })\n : new ReadableStream({ start(controller) { controller.close(); } });\n passThrough.end();\n clearTimeout(stallTimerRef);\n return new Response(fallback, {\n status: undiciResponse.statusCode,\n headers: undiciResponse.headers as unknown as HeadersInit,\n });\n }\n undiciResponse.body.pipe(passThrough);\n\n // Wrap undici response as a standard Web Response for downstream compatibility\n const response = new Response(\n passThrough as unknown as BodyInit,\n {\n status: undiciResponse.statusCode,\n headers: undiciResponse.headers as unknown as HeadersInit,\n }\n );\n\n clearTimeout(timeout);\n return response;\n } catch (error) {\n clearTimeout(timeout);\n if (ttfbTimer) clearTimeout(ttfbTimer);\n if (stallTimerRef) clearTimeout(stallTimerRef);\n\n // Network errors / timeouts — return a synthetic 502\n const message = ttfbTimedOut\n ? `Provider \"${provider.name}\" timed out waiting for first byte after ${ttfbTimeout}ms`\n : error instanceof DOMException && error.name === \"AbortError\"\n ? `Provider \"${provider.name}\" timed out after ${provider.timeout}ms`\n : `Provider \"${provider.name}\" connection failed: ${(error as Error).message}`;\n\n const body = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message },\n });\n return new Response(body, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(body).byteLength.toString(),\n },\n });\n } finally {\n removeAbortListener?.();\n }\n}\n\n/**\n * Race multiple providers simultaneously. Returns the first successful response.\n * Aborts all remaining requests once a winner is found.\n */\nasync function raceProviders(\n chain: RoutingEntry[],\n providers: Map<string, ProviderConfig>,\n ctx: RequestContext,\n incomingRequest: Request,\n onAttempt?: (provider: string, index: number) => void,\n logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void },\n chainOffset: number = 0,\n): Promise<Response> {\n const sharedController = new AbortController();\n\n const races = chain.map(async (entry, index): Promise<{ response: Response; index: number }> => {\n const provider = providers.get(entry.provider);\n if (!provider) {\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Unknown provider: ${entry.provider}` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n }),\n index,\n };\n }\n\n // Check circuit breaker\n let cbProbeId: number | undefined;\n if (provider._circuitBreaker) {\n const cb = provider._circuitBreaker.canProceed();\n if (!cb.allowed) {\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${entry.provider}\" skipped by circuit breaker` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n }),\n index,\n };\n }\n cbProbeId = cb.probeId;\n }\n\n onAttempt?.(entry.provider, index);\n\n try {\n const response = await forwardRequest(provider, entry, ctx, incomingRequest, sharedController.signal, index + chainOffset);\n // Record for circuit breaker\n if (provider._circuitBreaker) {\n provider._circuitBreaker.recordResult(response.status, cbProbeId);\n }\n return { response, index };\n } catch {\n if (provider._circuitBreaker) {\n provider._circuitBreaker.recordResult(502, cbProbeId);\n }\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${entry.provider}\" failed` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n }),\n index,\n };\n }\n });\n\n // Track completed promises to avoid double-processing\n const completed = new Set<Promise<{ response: Response; index: number }>>();\n const failures: { response: Response; index: number }[] = [];\n\n try {\n while (completed.size < races.length) {\n const pending = races.filter(r => !completed.has(r));\n if (pending.length === 0) break;\n\n const winner = await Promise.race(pending);\n completed.add(races[winner.index] ?? races[0]);\n\n if (winner.response.status >= 200 && winner.response.status < 300) {\n // Drain/cancel in-flight loser response bodies BEFORE aborting shared controller\n // to prevent leaked stream chunks from mid-write cancellation.\n for (const r of pending) {\n if (r !== races[winner.index]) {\n r.then(({ response }) => {\n if (response.body) {\n try { response.body.cancel(); } catch { /* already consumed */ }\n }\n }).catch(() => { /* aborted */ });\n }\n }\n sharedController.abort();\n // Cancel bodies of already-completed losing responses to free resources\n for (const f of failures) {\n try { f.response.body?.cancel(); } catch { /* ignore */ }\n }\n return winner.response;\n }\n\n // Non-retriable error — check for context window limit before propagating\n if (!isRetriable(winner.response.status)) {\n sharedController.abort();\n if ((winner.response.status === 400 || winner.response.status === 413) && winner.response.body) {\n try {\n const errBody = await winner.response.text();\n const handled = handleContextWindowError(winner.response.status, errBody);\n if (handled) return handled;\n // Not a context error — re-create response with buffered body\n return new Response(errBody, {\n status: winner.response.status,\n statusText: winner.response.statusText,\n headers: winner.response.headers,\n });\n } catch {\n return winner.response;\n }\n }\n return winner.response;\n }\n\n // Retriable but not success — record and continue waiting\n failures.push(winner);\n }\n\n // All providers returned retriable errors — return the first failure\n sharedController.abort();\n if (failures.length > 0) {\n return failures[0].response;\n }\n\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers in race failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n });\n } catch {\n sharedController.abort();\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers in race failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n}\n\n/**\n * Forward a request with optional adaptive hedging.\n * When latency variance is high, sends multiple copies and returns the fastest.\n */\nasync function hedgedForwardRequest(\n provider: ProviderConfig,\n entry: RoutingEntry,\n ctx: RequestContext,\n incomingRequest: Request,\n chainSignal: AbortSignal | undefined,\n index: number,\n logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void }\n): Promise<Response> {\n const count = computeHedgingCount(provider);\n\n if (count <= 1) {\n // No hedging — single request\n inFlightCounter.increment(provider.name);\n const start = Date.now();\n try {\n const r = await forwardRequest(provider, entry, ctx, incomingRequest, chainSignal, index);\n latencyTracker.record(provider.name, Date.now() - start);\n return r;\n } finally {\n inFlightCounter.decrement(provider.name);\n }\n }\n\n // Hedged path: send multiple copies, race for first success\n logger?.warn(\"Hedging request\", {\n requestId: ctx.requestId,\n provider: provider.name,\n count,\n cv: Math.round(latencyTracker.getCV(provider.name) * 100) / 100,\n inFlight: inFlightCounter.get(provider.name),\n maxConcurrent: provider.concurrentLimit,\n });\n\n const start = Date.now();\n const launched: Promise<Response>[] = [];\n const hedgeController = new AbortController();\n\n for (let h = 0; h < count; h++) {\n inFlightCounter.increment(provider.name);\n const hedgeSignal = chainSignal\n ? AbortSignal.any([chainSignal, hedgeController.signal])\n : hedgeController.signal;\n launched.push(\n forwardRequest(provider, entry, ctx, incomingRequest, hedgeSignal, index)\n .finally(() => inFlightCounter.decrement(provider.name))\n );\n }\n\n // Race: first successful response wins, cancel the rest\n // Wrap each promise so we can identify which one completed by index\n const wrapped = launched.map((p, i) =>\n p.then(response => ({ response, hedgeIndex: i }))\n );\n\n const completed = new Set<number>();\n const failures: Response[] = [];\n\n try {\n while (completed.size < wrapped.length) {\n const pending = wrapped.filter((_, i) => !completed.has(i));\n if (pending.length === 0) break;\n\n const winner = await Promise.race(pending);\n completed.add(winner.hedgeIndex);\n\n // Record each hedged copy's result for circuit breaker\n if (provider._circuitBreaker) {\n provider._circuitBreaker.recordResult(winner.response.status);\n }\n\n if (winner.response.status >= 200 && winner.response.status < 300) {\n latencyTracker.record(provider.name, Date.now() - start);\n // Abort all in-flight hedge copies — triggers onExternalAbort in each\n // which properly destroys their PassThrough streams and clears stall timers\n hedgeController.abort();\n // Cancel remaining — record 502 for each cancelled copy\n for (let i = 0; i < wrapped.length; i++) {\n if (!completed.has(i)) {\n if (provider._circuitBreaker) provider._circuitBreaker.recordResult(502);\n wrapped[i].then(r => { try { r.response.body?.cancel(); } catch {} });\n }\n }\n for (const f of failures) { try { f.body?.cancel(); } catch {} }\n return winner.response;\n }\n\n failures.push(winner.response);\n }\n\n // All hedged copies returned errors — cancel bodies, return first failure\n hedgeController.abort();\n for (const f of failures) { try { f.body?.cancel(); } catch {} }\n return failures[0] ?? new Response(\n JSON.stringify({ type: \"error\", error: { type: \"api_error\", message: `Provider \"${provider.name}\" all hedged requests failed` } }),\n { status: 502, headers: { \"content-type\": \"application/json\" } }\n );\n } catch {\n hedgeController.abort();\n for (const f of failures) { try { f.body?.cancel(); } catch {} }\n return failures[0] ?? new Response(\n JSON.stringify({ type: \"error\", error: { type: \"api_error\", message: `Provider \"${provider.name}\" hedging failed` } }),\n { status: 502, headers: { \"content-type\": \"application/json\" } }\n );\n }\n}\n\n/**\n * Try forwarding through a chain of providers.\n * Returns the first successful response, or 502 if all fail.\n */\nexport async function forwardWithFallback(\n providers: Map<string, ProviderConfig>,\n chain: RoutingEntry[],\n ctx: RequestContext,\n incomingRequest: Request,\n onAttempt?: (provider: string, index: number) => void,\n logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void }\n): Promise<Response> {\n // Single provider — no racing needed\n if (chain.length <= 1) {\n const entry = chain[0];\n const provider = providers.get(entry.provider);\n\n if (!provider) {\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Unknown provider: ${entry.provider}` },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n }\n\n if (provider._circuitBreaker) {\n const cb = provider._circuitBreaker.canProceed();\n if (!cb.allowed) {\n logger?.warn(\"Provider skipped by circuit breaker\", { requestId: ctx.requestId, provider: entry.provider });\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${entry.provider}\" skipped by circuit breaker` },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n }\n }\n\n onAttempt?.(entry.provider, 0);\n\n const response = await hedgedForwardRequest(provider, entry, ctx, incomingRequest, undefined, 0, logger);\n\n return response;\n }\n\n // Multiple providers — staggered race\n const sharedController = new AbortController();\n const completed = new Set<number>();\n const failures: { response: Response; index: number }[] = [];\n\n async function attemptProvider(\n index: number,\n ): Promise<{ response: Response; index: number }> {\n const entry = chain[index];\n const provider = providers.get(entry.provider);\n\n if (!provider) {\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Unknown provider: ${entry.provider}` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n }),\n index,\n };\n }\n\n let cbProbeId: number | undefined;\n if (provider._circuitBreaker) {\n const cb = provider._circuitBreaker.canProceed();\n if (!cb.allowed) {\n logger?.warn(\"Provider skipped by circuit breaker\", {\n requestId: ctx.requestId,\n provider: entry.provider,\n });\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${entry.provider}\" skipped by circuit breaker` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n }),\n index,\n };\n }\n cbProbeId = cb.probeId;\n }\n\n onAttempt?.(entry.provider, index);\n\n try {\n const response = await hedgedForwardRequest(\n provider,\n entry,\n ctx,\n incomingRequest,\n sharedController.signal,\n index,\n logger,\n );\n return { response, index };\n } catch {\n if (provider._circuitBreaker) provider._circuitBreaker.recordResult(502, cbProbeId);\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${entry.provider}\" failed` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n }),\n index,\n };\n }\n }\n\n // Build staggered race promises:\n // Provider 0 starts immediately\n // Provider 1+ start after SPECULATIVE_DELAY (if race not already won)\n const races: Promise<{ response: Response; index: number }>[] = [];\n\n for (let i = 0; i < chain.length; i++) {\n if (i === 0) {\n races.push(attemptProvider(0));\n } else {\n races.push(\n new Promise<{ response: Response; index: number }>((resolve) => {\n setTimeout(() => {\n if (sharedController.signal.aborted) {\n // Race already won — resolve with a cancelled placeholder\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: \"Cancelled by race winner\" },\n });\n resolve({\n response: new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n }),\n index: i,\n });\n return;\n }\n attemptProvider(i).then(resolve);\n }, SPECULATIVE_DELAY);\n }),\n );\n }\n }\n\n // Pick winner — same logic as raceProviders\n try {\n while (completed.size < races.length) {\n const pending = races.filter((_, idx) => !completed.has(idx));\n if (pending.length === 0) break;\n\n const winner = await Promise.race(pending);\n completed.add(winner.index);\n\n if (winner.response.status >= 200 && winner.response.status < 300) {\n sharedController.abort();\n for (const f of failures) {\n try {\n f.response.body?.cancel();\n } catch {\n /* ignore */\n }\n }\n return winner.response;\n }\n\n if (!isRetriable(winner.response.status)) {\n sharedController.abort();\n if ((winner.response.status === 400 || winner.response.status === 413) && winner.response.body) {\n try {\n const errBody = await winner.response.text();\n const handled = handleContextWindowError(winner.response.status, errBody);\n if (handled) return handled;\n return new Response(errBody, {\n status: winner.response.status,\n statusText: winner.response.statusText,\n headers: winner.response.headers,\n });\n } catch {\n return winner.response;\n }\n }\n return winner.response;\n }\n\n failures.push(winner);\n }\n\n sharedController.abort();\n if (failures.length > 0) return failures[0].response;\n\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n } catch {\n sharedController.abort();\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n }\n}\n","/**\n * Adaptive request hedging — sends multiple copies of a request when\n * the provider shows high latency variance, returning the fastest response.\n */\n\nimport type { ProviderConfig } from './types.js';\n\ninterface LatencySample {\n ttfbMs: number;\n timestamp: number;\n}\n\nexport class LatencyTracker {\n private samples = new Map<string, LatencySample[]>();\n private readonly maxSize: number;\n private readonly MAX_PROVIDERS = 50;\n\n constructor(maxSize = 20) {\n this.maxSize = maxSize;\n }\n\n record(provider: string, ttfbMs: number): void {\n // Cap total tracked providers to prevent unbounded growth\n if (this.samples.size >= this.MAX_PROVIDERS && !this.samples.has(provider)) {\n // Remove the first (oldest) provider key\n const firstKey = this.samples.keys().next().value;\n if (firstKey !== undefined) this.samples.delete(firstKey);\n }\n\n let window = this.samples.get(provider);\n if (!window) {\n window = [];\n this.samples.set(provider, window);\n }\n window.push({ ttfbMs, timestamp: Date.now() });\n if (window.length > this.maxSize) {\n window.splice(0, window.length - this.maxSize);\n }\n }\n\n /** Coefficient of variation (stddev / mean). Returns 0 if insufficient data. */\n getCV(provider: string): number {\n const window = this.samples.get(provider);\n if (!window || window.length < 3) return 0;\n const values = window.map(s => s.ttfbMs);\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n if (mean === 0) return 0;\n const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;\n return Math.sqrt(variance) / mean;\n }\n\n getStats(provider: string): { count: number; mean: number; cv: number } {\n const window = this.samples.get(provider);\n if (!window || window.length === 0) return { count: 0, mean: 0, cv: 0 };\n const values = window.map(s => s.ttfbMs);\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n return { count: values.length, mean: Math.round(mean), cv: Math.round(this.getCV(provider) * 100) / 100 };\n }\n\n clear(provider: string): void {\n this.samples.delete(provider);\n }\n\n /** Remove entries for providers no longer in the current config. */\n prune(activeProviders: string[]): void {\n const active = new Set(activeProviders);\n for (const key of this.samples.keys()) {\n if (!active.has(key)) {\n this.samples.delete(key);\n }\n }\n }\n}\n\nexport class InFlightCounter {\n private counts = new Map<string, number>();\n\n increment(provider: string): number {\n const count = (this.counts.get(provider) ?? 0) + 1;\n this.counts.set(provider, count);\n return count;\n }\n\n decrement(provider: string): number {\n const count = Math.max(0, (this.counts.get(provider) ?? 0) - 1);\n this.counts.set(provider, count);\n return count;\n }\n\n get(provider: string): number {\n return this.counts.get(provider) ?? 0;\n }\n}\n\nexport const latencyTracker = new LatencyTracker();\nexport const inFlightCounter = new InFlightCounter();\n\n/**\n * Compute adaptive hedging count based on latency variance and available concurrency.\n *\n * CV (coefficient of variation) drives the count:\n * CV 0.0 → 1 (no hedging, provider is consistent)\n * CV 0.5 → 2\n * CV 1.0 → 3\n * CV 1.5+ → 4\n *\n * Clamped by available concurrency slots: maxConcurrent - inFlight.\n */\nexport function computeHedgingCount(provider: ProviderConfig): number {\n const cv = latencyTracker.getCV(provider.name);\n const inFlight = inFlightCounter.get(provider.name);\n const maxConcurrent = provider.concurrentLimit ?? 1;\n const available = Math.max(1, maxConcurrent - inFlight);\n\n const adaptive = Math.max(1, Math.floor(cv * 2 + 0.5));\n return Math.min(adaptive, available);\n}\n","// src/ws.ts\nimport { WebSocketServer } from \"ws\";\nimport type { Server } from \"node:http\";\nimport type { MetricsStore } from \"./metrics.js\";\nimport type { RequestMetrics, MetricsSummary, StreamEvent } from \"./types.js\";\n\ninterface WsMessage {\n type: \"request\" | \"summary\";\n data: RequestMetrics | MetricsSummary;\n}\n\nconst PING_INTERVAL_MS = 30_000; // 30 seconds\nconst MAX_MISSED_PONGS = 2;\nconst BACKPRESSURE_THRESHOLD = 64 * 1024; // 64KB\nconst SUMMARY_DEBOUNCE_MS = 500;\nconst STREAM_WS_THROTTLE_MS = 500; // caps stream event delivery to ~2 Hz per client\nconst BACKPRESSURE_LOG_INTERVAL_MS = 10_000; // throttle backpressure warnings to once per 10s\nconst clientStreamThrottle = new WeakMap<any, number>();\n\ninterface PendingDrain {\n timer: ReturnType<typeof setTimeout>;\n queue: string[];\n}\nconst pendingDrains = new WeakMap<any, PendingDrain>();\n\nlet wssInstance: InstanceType<typeof import(\"ws\").WebSocketServer> | null = null;\n\n// Module-level counters for dropped events (useful for monitoring)\nlet streamDroppedCount = 0;\nlet lastBackpressureWarnTime = 0;\n\nfunction maybeLogBackpressure(source: string): void {\n const now = Date.now();\n if (now - lastBackpressureWarnTime >= BACKPRESSURE_LOG_INTERVAL_MS) {\n console.warn(`[ws] Backpressure: dropping ${source} events (total dropped stream events: ${streamDroppedCount})`);\n lastBackpressureWarnTime = now;\n }\n}\n\nexport function attachWebSocket(server: Server, metricsStore: MetricsStore): void {\n const wss = new WebSocketServer({ server, path: \"/ws\" });\n wssInstance = wss;\n\n wss.on(\"connection\", (ws) => {\n // Send current summary as initial state\n const summary = metricsStore.getSummary();\n const initialMsg: WsMessage = { type: \"summary\", data: summary };\n ws.send(JSON.stringify(initialMsg));\n\n let pendingSummaryTimer: ReturnType<typeof setTimeout> | undefined;\n let missedPongs = 0;\n const alive = () => ws.readyState === ws.OPEN;\n\n // Subscribe to new metrics with backpressure check and debounced summary\n const unsubscribe = metricsStore.onRecord((metrics: RequestMetrics) => {\n if (!alive()) return;\n\n // Backpressure: skip send if outbound buffer is too large\n if (ws.bufferedAmount > BACKPRESSURE_THRESHOLD) {\n // Schedule a summary update instead so the client eventually catches up\n scheduleSummaryUpdate();\n maybeLogBackpressure(\"metrics\");\n return;\n }\n\n // Defer JSON.stringify + send off the critical path\n setImmediate(() => {\n if (!alive()) return;\n const msg: WsMessage = { type: \"request\", data: metrics };\n ws.send(JSON.stringify(msg));\n });\n\n scheduleSummaryUpdate();\n });\n\n function scheduleSummaryUpdate(): void {\n if (pendingSummaryTimer) return; // already scheduled\n pendingSummaryTimer = setTimeout(() => {\n pendingSummaryTimer = undefined;\n if (!alive()) return;\n const msg: WsMessage = { type: \"summary\", data: metricsStore.getSummary() };\n ws.send(JSON.stringify(msg));\n }, SUMMARY_DEBOUNCE_MS);\n }\n\n // Ping/pong heartbeat for liveness tracking\n const pingTimer = setInterval(() => {\n if (!alive()) {\n clearInterval(pingTimer);\n return;\n }\n // Terminate if client missed too many pongs\n if (missedPongs >= MAX_MISSED_PONGS) {\n cleanup(); // ensure timers and subscriber are cleaned up\n ws.terminate();\n return;\n }\n ws.ping();\n missedPongs++;\n }, PING_INTERVAL_MS);\n\n ws.on(\"pong\", () => {\n missedPongs = 0; // reset on successful pong\n });\n\n let cleanedUp = false;\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n clearInterval(pingTimer);\n if (pendingSummaryTimer) clearTimeout(pendingSummaryTimer);\n const pending = pendingDrains.get(ws);\n if (pending) {\n clearTimeout(pending.timer);\n pendingDrains.delete(ws);\n }\n unsubscribe();\n };\n\n ws.on(\"close\", cleanup);\n ws.on(\"error\", cleanup);\n });\n}\n\nexport function broadcastStreamEvent(data: StreamEvent): void {\n if (!wssInstance) return;\n const isStreaming = data.state === \"streaming\";\n const isCritical = data.state === \"complete\" || data.state === \"error\";\n const now = Date.now();\n\n let serializedMsg: string | undefined;\n for (const client of wssInstance.clients) {\n if (client.readyState !== client.OPEN) continue;\n\n // Throttle streaming events per client (non-streaming events always pass)\n if (isStreaming) {\n const lastEmit = clientStreamThrottle.get(client) ?? 0;\n if (now - lastEmit < STREAM_WS_THROTTLE_MS) continue;\n clientStreamThrottle.set(client, now);\n }\n\n // Lazy serialization — only when we know at least one client needs it\n if (!serializedMsg) serializedMsg = JSON.stringify({ type: \"stream\", data });\n const msg = serializedMsg;\n\n // Backpressure handling\n if (client.bufferedAmount > BACKPRESSURE_THRESHOLD) {\n if (isCritical) {\n let pending = pendingDrains.get(client);\n if (pending) {\n pending.queue.push(msg);\n } else {\n const queue = [msg];\n const sendOnDrain = () => {\n pendingDrains.delete(client);\n if (client.readyState === client.OPEN) {\n for (const queuedMsg of queue) client.send(queuedMsg);\n }\n };\n const timer = setTimeout(() => {\n pendingDrains.delete(client);\n client.removeListener('drain', sendOnDrain);\n if (client.readyState === client.OPEN) {\n for (const queuedMsg of queue) client.send(queuedMsg);\n }\n }, 5_000).unref();\n pendingDrains.set(client, { timer, queue });\n client.once('drain', sendOnDrain);\n }\n continue;\n }\n // Non-critical streaming event: drop and count\n streamDroppedCount++;\n maybeLogBackpressure(\"stream\");\n continue;\n }\n\n setImmediate(() => {\n if (client.readyState === client.OPEN) {\n client.send(msg);\n }\n });\n }\n}\n","// src/metrics.ts\nimport type { RequestMetrics, MetricsSummary } from \"./types.js\";\n\ntype Subscriber = (metrics: RequestMetrics) => void;\n\nconst WS_RECENT_REQUESTS_CAP = 50;\n\ninterface ModelEntry {\n actualModel?: string;\n count: number;\n lastSeen: number;\n}\n\nexport class MetricsStore {\n private static readonly MAX_MAP_SIZE = 200;\n\n private buffer: (RequestMetrics | null)[];\n private maxSize: number;\n private head = 0;\n private count = 0;\n private _lifetimeRequests = 0;\n private subscribers: Set<Subscriber>;\n private createdAt: number;\n\n // Running counters — updated incrementally in recordRequest()\n private _totalInputTokens = 0;\n private _totalOutputTokens = 0;\n private _totalTokensPerSec = 0;\n private _totalCacheReadTokens = 0;\n private _totalCacheCreationTokens = 0;\n private _modelMap = new Map<string, ModelEntry>();\n private _providerMap = new Map<string, number>();\n\n constructor(maxSize: number = 1000) {\n this.buffer = new Array(maxSize).fill(null);\n this.maxSize = maxSize;\n this.subscribers = new Set();\n this.createdAt = Date.now();\n }\n\n /** Evict the entry with the lowest count when the map exceeds MAX_MAP_SIZE. */\n private pruneMap<V>(map: Map<string, V>, getCount: (v: V) => number): void {\n if (map.size <= MetricsStore.MAX_MAP_SIZE) return;\n let minKey = '';\n let minVal = Infinity;\n for (const [k, v] of map) {\n const val = getCount(v);\n if (val < minVal) {\n minVal = val;\n minKey = k;\n }\n }\n if (minKey) map.delete(minKey);\n }\n\n recordRequest(metrics: RequestMetrics): void {\n const index = this.head % this.maxSize;\n const evicted = this.count >= this.maxSize ? this.buffer[index] : null;\n\n // Decrement counters for evicted entry\n if (evicted !== null) {\n this._totalInputTokens -= evicted.inputTokens ?? 0;\n this._totalOutputTokens -= evicted.outputTokens ?? 0;\n this._totalTokensPerSec -= evicted.tokensPerSec ?? 0;\n this._totalCacheReadTokens -= evicted.cacheReadTokens ?? 0;\n this._totalCacheCreationTokens -= evicted.cacheCreationTokens ?? 0;\n\n const mKey = evicted.model;\n const mEntry = this._modelMap.get(mKey);\n if (mEntry) {\n mEntry.count--;\n if (mEntry.count <= 0) this._modelMap.delete(mKey);\n }\n\n const pKey = evicted.targetProvider ?? evicted.provider;\n const pCount = this._providerMap.get(pKey) ?? 0;\n if (pCount <= 1) this._providerMap.delete(pKey);\n else this._providerMap.set(pKey, pCount - 1);\n }\n\n // Increment counters for new entry\n this._totalInputTokens += metrics.inputTokens ?? 0;\n this._totalOutputTokens += metrics.outputTokens ?? 0;\n this._totalTokensPerSec += metrics.tokensPerSec ?? 0;\n this._totalCacheReadTokens += metrics.cacheReadTokens ?? 0;\n this._totalCacheCreationTokens += metrics.cacheCreationTokens ?? 0;\n\n const mKey = metrics.model;\n const existing = this._modelMap.get(mKey);\n if (existing) {\n existing.count++;\n if (metrics.timestamp > existing.lastSeen) existing.lastSeen = metrics.timestamp;\n // Update actualModel to latest seen for the grouped model\n existing.actualModel = metrics.actualModel;\n } else {\n this._modelMap.set(mKey, { actualModel: metrics.actualModel, count: 1, lastSeen: metrics.timestamp });\n }\n\n const pKey = metrics.targetProvider ?? metrics.provider;\n this._providerMap.set(pKey, (this._providerMap.get(pKey) ?? 0) + 1);\n\n // Enforce size caps on maps\n this.pruneMap(this._modelMap, (e) => e.count);\n this.pruneMap(this._providerMap, (v) => v);\n\n // Ring buffer: overwrite oldest entry when full\n this.buffer[index] = metrics;\n this.head++;\n if (this.count < this.maxSize) this.count++;\n this._lifetimeRequests++;\n\n // Notify subscribers (catch errors to prevent breaking recording)\n for (const cb of this.subscribers) {\n try {\n cb(metrics);\n } catch {\n // Swallow subscriber errors — recording must not break\n }\n }\n }\n\n getSummary(): MetricsSummary {\n const requests = this.getRecentRequests();\n\n const activeModels = [...this._modelMap.entries()]\n .map(([model, { actualModel, count, lastSeen }]) => ({ model, actualModel, count, lastSeen }))\n .sort((a, b) => b.count - a.count);\n\n const providerDistribution = [...this._providerMap.entries()]\n .map(([provider, count]) => ({ provider, count }))\n .sort((a, b) => b.count - a.count);\n\n // Compute average cache hit rate across all requests with cache data\n let cacheHitRateSum = 0;\n let cacheHitRateCount = 0;\n for (const r of requests) {\n const totalInput = (r.inputTokens ?? 0) + (r.cacheReadTokens ?? 0) + (r.cacheCreationTokens ?? 0);\n if (totalInput > 0 && (r.cacheReadTokens ?? 0) > 0) {\n cacheHitRateSum += (r.cacheReadTokens! / totalInput) * 100;\n cacheHitRateCount++;\n }\n }\n\n // getRecentRequests() already caps at WS_RECENT_REQUESTS_CAP\n return {\n totalRequests: this._lifetimeRequests,\n totalInputTokens: this._totalInputTokens,\n totalOutputTokens: this._totalOutputTokens,\n avgTokensPerSec: this.count > 0 ? Math.round((this._totalTokensPerSec / this.count) * 10) / 10 : 0,\n totalCacheReadTokens: this._totalCacheReadTokens,\n totalCacheCreationTokens: this._totalCacheCreationTokens,\n avgCacheHitRate: cacheHitRateCount > 0 ? Math.round((cacheHitRateSum / cacheHitRateCount) * 10) / 10 : 0,\n activeModels,\n providerDistribution,\n recentRequests: requests,\n uptimeSeconds: Math.floor((Date.now() - this.createdAt) / 1000),\n };\n }\n\n onRecord(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private getRecentRequests(): RequestMetrics[] {\n if (this.count === 0) return [];\n\n // Collect only the last WS_RECENT_REQUESTS_CAP entries in reverse (newest first)\n const cap = Math.min(this.count, WS_RECENT_REQUESTS_CAP);\n const result: RequestMetrics[] = [];\n // Start from the most recently written slot and walk backward\n for (let i = 0; i < cap; i++) {\n const index = ((this.head - 1 - i) % this.maxSize + this.maxSize) % this.maxSize;\n const entry = this.buffer[index];\n if (entry !== null) {\n result.push(entry);\n }\n }\n // Reverse to get chronological order (oldest first, newest last)\n result.reverse();\n return result;\n }\n}\n","// src/monitor.ts — Monitor mode: spawns daemon child, auto-restarts on crash\nimport { spawn } from \"node:child_process\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { dirname, join as pathJoin } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { writePidFile, removePidFile, removeWorkerPidFile, getPidPath } from \"./daemon.js\";\n\nexport async function startMonitor(args: {\n config?: string;\n port?: number;\n verbose: boolean;\n}): Promise<void> {\n // Monitor writes its own PID to modelweaver.pid\n // Clean up any stale PID file left by a previous run\n const pidPath = getPidPath();\n if (existsSync(pidPath)) {\n unlinkSync(pidPath);\n }\n await writePidFile(process.pid);\n\n const entryScript =\n process.argv[1] || pathJoin(dirname(fileURLToPath(import.meta.url)), \"index.js\");\n\n // Prevent monitor from crashing on unexpected errors\n process.on(\"uncaughtException\", (err) => {\n console.error(`[monitor] Uncaught exception: ${err.message}`);\n });\n process.on(\"unhandledRejection\", (reason) => {\n console.error(`[monitor] Unhandled rejection: ${reason}`);\n });\n\n const MAX_RESTART_ATTEMPTS = 10;\n const INITIAL_BACKOFF_MS = 1000;\n const MAX_BACKOFF_MS = 30000;\n const STABLE_RUN_MS = 60000;\n let restartCount = 0;\n let stableTimer: ReturnType<typeof setTimeout> | null = null;\n let restartTimer: ReturnType<typeof setTimeout> | null = null;\n let shuttingDown = false;\n let reloading = false;\n let child: ReturnType<typeof spawn> | null = null;\n\n async function spawnDaemon(): Promise<void> {\n const net = await import(\"node:net\");\n const { execFileSync } = await import(\"child_process\");\n const port = args.port ?? 3456;\n\n const checkPort = (): Promise<boolean> =>\n new Promise<boolean>((resolve) => {\n const server = net.createServer();\n server.once(\"error\", () => resolve(true));\n server.once(\"listening\", () => { server.close(() => resolve(false)); });\n server.listen(port);\n });\n\n if (await checkPort()) {\n // Port in use — find and kill the orphaned process\n console.warn(`[monitor] Port ${port} in use by orphaned process — cleaning up`);\n try {\n const lsofOutput = execFileSync(\"lsof\", [\"-ti\", `:${port}`], { encoding: \"utf8\" }).trim();\n if (lsofOutput) {\n const pids = lsofOutput.split(\"\\n\").map(Number).filter(Boolean);\n for (const pid of pids) {\n try {\n process.kill(pid, \"SIGTERM\");\n console.warn(`[monitor] Sent SIGTERM to orphaned process ${pid}`);\n } catch {\n // Process may have already exited\n }\n }\n // Wait for port to free up (max 5 seconds)\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!(await checkPort())) {\n console.warn(`[monitor] Port ${port} freed — spawning new daemon`);\n break;\n }\n }\n // If port still in use after timeout, force kill\n if (await checkPort()) {\n for (const pid of pids) {\n try { process.kill(pid, \"SIGKILL\"); } catch { /* already dead */ }\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n }\n } catch {\n // lsof not available or other error — try pkill as fallback\n try {\n execFileSync(\"pkill\", [\"-f\", \"modelweaver.*dist/index.*--daemon\"], { stdio: \"ignore\" });\n await new Promise((r) => setTimeout(r, 1000));\n } catch { /* pkill also failed */ }\n }\n\n // Final check — if port is still in use, give up\n if (await checkPort()) {\n console.error(`[monitor] Port ${port} still in use after cleanup — skipping worker spawn`);\n return;\n }\n }\n\n const childArgs: string[] = [entryScript, \"--daemon\"];\n if (args.config) childArgs.push(\"--config\", args.config);\n if (args.port) childArgs.push(\"--port\", String(args.port));\n if (args.verbose) childArgs.push(\"--verbose\");\n\n child = spawn(process.execPath, childArgs, {\n detached: true,\n stdio: \"ignore\",\n env: { ...process.env },\n });\n // NOTE: do NOT child.unref() here — the monitor must stay alive to watch the child\n\n // Start stability timer — if worker lives this long, reset restart counter\n if (stableTimer) clearTimeout(stableTimer);\n stableTimer = setTimeout(() => {\n if (restartCount > 0) {\n console.error(\n `[monitor] Worker stable for ${STABLE_RUN_MS}ms, resetting restart counter`,\n );\n }\n restartCount = 0;\n stableTimer = null;\n }, STABLE_RUN_MS);\n\n child.on(\"exit\", async (code) => {\n child = null;\n\n // Clear stability timer — worker died before becoming stable\n if (stableTimer) {\n clearTimeout(stableTimer);\n stableTimer = null;\n }\n\n await removeWorkerPidFile();\n if (code === 0 && !reloading) {\n // Clean shutdown — monitor exits too\n await removePidFile();\n process.exit(0);\n }\n reloading = false;\n\n // Don't restart if we're shutting down\n if (shuttingDown) {\n console.error(\"[monitor] Worker exited during shutdown, monitor exiting\");\n await removePidFile();\n process.exit(0);\n }\n\n // Crash — apply exponential backoff restart\n const attempt = restartCount;\n if (attempt >= MAX_RESTART_ATTEMPTS) {\n console.error(\n `[monitor] Max restart attempts exhausted (${MAX_RESTART_ATTEMPTS}), monitor exiting`,\n );\n await removePidFile();\n process.exit(1);\n }\n\n const backoff = Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS);\n restartCount++;\n console.error(\n `[monitor] Worker died (code ${code}), restarting in ${backoff}ms (attempt ${restartCount}/${MAX_RESTART_ATTEMPTS})`,\n );\n\n restartTimer = setTimeout(spawnDaemon, backoff);\n });\n }\n\n // SIGTERM from `stop` → kill child, then exit cleanly\n // Does NOT register a second `exit` listener on the child. Instead, relies on\n // the existing child exit handler (registered in spawnDaemon) which already\n // checks `shuttingDown` and performs cleanup + process.exit(0).\n process.on(\"SIGTERM\", () => {\n shuttingDown = true;\n if (restartTimer) {\n clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (stableTimer) {\n clearTimeout(stableTimer);\n stableTimer = null;\n }\n if (child) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n // Child exit handler will clean up pid files and call process.exit(0).\n // Safety: if child doesn't exit within 5 s, force exit.\n setTimeout(() => {\n console.error(\"[monitor] Child did not exit within 5 s, forcing exit\");\n process.exit(0);\n }, 5000);\n } else {\n // Child already dead — clean up and exit.\n removePidFile().then(() => process.exit(0));\n }\n });\n\n // SIGINT (Ctrl-C) — same pattern as SIGTERM.\n process.on(\"SIGINT\", () => {\n shuttingDown = true;\n if (restartTimer) {\n clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (stableTimer) {\n clearTimeout(stableTimer);\n stableTimer = null;\n }\n if (child) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n // Child exit handler will clean up pid files and call process.exit(0).\n // Safety: if child doesn't exit within 5 s, force exit.\n setTimeout(() => {\n console.error(\"[monitor] Child did not exit within 5 s, forcing exit\");\n process.exit(0);\n }, 5000);\n } else {\n // Child already dead — clean up and exit.\n removePidFile().then(() => process.exit(0));\n }\n });\n\n // SIGHUP from `reload` → gracefully kill current worker so monitor restarts it\n // Note: SIGHUP is POSIX-only; this handler is a no-op on Windows.\n process.on(\"SIGHUP\", () => {\n console.log(\"[monitor] Received reload signal, restarting worker...\");\n reloading = true;\n if (restartTimer) {\n clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (child) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n }\n // Reset restart count — this is an intentional restart, not a crash\n restartCount = 0;\n });\n\n spawnDaemon();\n}\n"],"mappings":"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","names":["createAdaptorServer","readFileSync","Hono","routingCache","clearRoutingCache","matchTier","modelName","tierPatterns","tier","patterns","pattern","buildRoutingChain","routing","resolveRequest","model","requestId","config","rawBody","cached","providerChain","modelChain","matchedTier","oldestKey","undiciRequest","PassThrough","fs","path","os","LatencyTracker","maxSize","provider","ttfbMs","firstKey","window","values","s","mean","b","variance","sum","v","a","activeProviders","active","key","InFlightCounter","count","latencyTracker","inFlightCounter","computeHedgingCount","cv","inFlight","maxConcurrent","available","adaptive","WebSocketServer","PING_INTERVAL_MS","MAX_MISSED_PONGS","BACKPRESSURE_THRESHOLD","SUMMARY_DEBOUNCE_MS","STREAM_WS_THROTTLE_MS","BACKPRESSURE_LOG_INTERVAL_MS","clientStreamThrottle","pendingDrains","wssInstance","streamDroppedCount","lastBackpressureWarnTime","maybeLogBackpressure","source","now","attachWebSocket","server","metricsStore","wss","ws","initialMsg","pendingSummaryTimer","missedPongs","alive","unsubscribe","metrics","scheduleSummaryUpdate","msg","pingTimer","cleanup","cleanedUp","pending","broadcastStreamEvent","data","isStreaming","isCritical","serializedMsg","client","lastEmit","queue","sendOnDrain","queuedMsg","timer","shallowCloneForMutation","parsed","clone","FORWARD_HEADERS","MULTI_SLASH","STRIP_ORIGIN","MODEL_KEY_REGEX","MAX_TOKENS_REGEX","textEncoder","SPECULATIVE_DELAY","isRetriable","status","CONTEXT_WINDOW_PATTERNS","isContextWindowError","body","lower","p","handleContextWindowError","flagDir","path","os","fs","enhanced","buildOutboundUrl","baseUrl","incomingPath","basePath","origin","slashIndex","incomingQuery","incomingOnly","qIndex","resolvedPath","buildOutboundHeaders","incomingHeaders","provider","requestId","headers","name","value","cachedHost","url","cleanOrphanedToolMessages","messages","allToolUseIds","allToolResultIds","i","msg","block","orphanedToolUseIds","orphanedToolResultIds","id","changed","cleaned","filtered","applyTargetedReplacements","rawBody","entry","needsOrphanClean","mutable","maxOutputTokens","requested","needsModel","needsMaxTokensClamp","needsMaxTokensAdd","m","patterns","combinedRegex","modelRepl","tokensRepl","origModel","modelLogged","match","forwardRequest","ctx","incomingRequest","externalSignal","chainIndex","outgoingPath","needsModification","requestedMaxTokens","originalModel","controller","timeout","ttfbTimeout","ttfbTimedOut","ttfbTimer","ttfbPromise","_","reject","removeAbortListener","upstreamBody","passThrough","stallTimerRef","errBody","prevMax","onExternalAbort","undiciResponse","undiciRequest","stallTimeout","PassThrough","stallMsg","broadcastStreamEvent","fallback","response","error","message","hedgedForwardRequest","provider","entry","ctx","incomingRequest","chainSignal","index","logger","count","computeHedgingCount","inFlightCounter","start","r","forwardRequest","latencyTracker","launched","hedgeController","h","hedgeSignal","wrapped","p","i","response","completed","failures","pending","_","winner","f","forwardWithFallback","providers","chain","onAttempt","errBody","textEncoder","sharedController","attemptProvider","cbProbeId","cb","races","resolve","SPECULATIVE_DELAY","idx","isRetriable","handled","handleContextWindowError","randomUUID","gzip","promisify","gzipAsync","promisify","gzip","MODEL_CONTEXT_WINDOWS","getContextWindow","model","key","size","computeCacheHitRate","cacheRead","cacheCreation","input","totalInput","computeContextPercent","output","contextWindow","total","anthropicError","type","message","requestId","parseUsageFromData","data","usage","inp","out","createMetricsTransform","ctx","provider","targetProvider","metricsStore","status","contentType","td","tokens","lineBuf","eventBuf","WINDOW_SIZE","inputTokens","cacheReadTokens","cacheCreationTokens","outputTokens","windowBuf","isSSE","STREAM_THROTTLE_MS","lastStreamEmit","firstChunk","responsePreview","PREVIEW_MAX","drainEvents","eventText","event","dataLine","l","delta","choices","choiceDelta","scanWindow","text","anthContent","lastText","inputMatches","cacheReadMatches","cacheCreationMatches","outputMatches","val","recordMetrics","latencyMs","latencySec","tps","broadcastStreamEvent","processChunk","decoded","isFinal","lines","line","now","nowJson","chunk","controller","agentKey","origin","createApp","initConfig","logLevel","config","logger","createLogger","app","Hono","err","c","next","randomUUID","body","rawBody","resolveRequest","configuredModels","e","successfulProvider","response","forwardWithFallback","index","headerSize","k","errMsg","responseBody","transform","newHeaders","finalResponse","latency","json","compressed","name","breaker","s","newConfig","oldAgents","reusedKeys","existingAgent","closePromises","agent","clearRoutingCache","MetricsStore","_MetricsStore","maxSize","map","getCount","minKey","minVal","k","v","val","metrics","index","evicted","mKey","mEntry","pKey","pCount","existing","e","cb","requests","activeModels","model","actualModel","count","lastSeen","b","providerDistribution","provider","cacheHitRateSum","cacheHitRateCount","r","totalInput","callback","cap","result","i","entry","spawn","existsSync","unlinkSync","dirname","pathJoin","fileURLToPath","startMonitor","args","pidPath","getPidPath","existsSync","unlinkSync","writePidFile","entryScript","pathJoin","dirname","fileURLToPath","err","reason","MAX_RESTART_ATTEMPTS","INITIAL_BACKOFF_MS","MAX_BACKOFF_MS","STABLE_RUN_MS","restartCount","stableTimer","restartTimer","shuttingDown","reloading","child","spawnDaemon","net","execFileSync","port","checkPort","resolve","server","lsofOutput","pids","pid","i","r","childArgs","spawn","code","removeWorkerPidFile","removePidFile","attempt","backoff","VERSION","readFileSync","parseArgs","argv","args","i","portStr","configPath","printHelp","main","dotenv","existsSync","join","home","paths","p","quick","runInit","startDaemon","result","stopDaemon","statusDaemon","getService","installed","err","removeDaemon","launchGui","reloadDaemon","config","loadConfig","error","port","host","logLevel","metricsStore","MetricsStore","startMonitor","removeWorkerPidFile","writeWorkerPidFile","createDebouncedReload","getLogPath","reloadConfig","createWriteStream","watch","createLogger","logger","reason","logStream","handle","createApp","configWatcher","debounced","newConfig","latencyTracker","server","createAdaptorServer","attachWebSocket","shutdown","tier","entries","providerList","e","model"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/router.ts","../src/proxy.ts","../src/hedging.ts","../src/ws.ts","../src/metrics.ts","../src/monitor.ts"],"sourcesContent":["// src/index.ts\nimport { createAdaptorServer } from \"@hono/node-server\";\nimport { readFileSync } from \"node:fs\";\nimport { createApp } from \"./server.js\";\nimport { loadConfig } from \"./config.js\";\nimport type { LogLevel } from \"./logger.js\";\nimport { MetricsStore } from \"./metrics.js\";\nimport { latencyTracker } from \"./hedging.js\";\nimport { attachWebSocket } from \"./ws.js\";\nimport { startMonitor } from \"./monitor.js\";\n\n// Read version from package.json at startup\nconst VERSION: string = JSON.parse(readFileSync(new URL(\"../package.json\", import.meta.url), \"utf-8\")).version;\n\nfunction parseArgs(argv: string[]): { port?: number; config?: string; verbose: boolean; help: boolean; daemon: boolean; monitor: boolean; gui: boolean } {\n const args: { port?: number; config?: string; verbose: boolean; help: boolean; daemon: boolean; monitor: boolean; gui: boolean } = { verbose: false, help: false, daemon: false, monitor: false, gui: false };\n for (let i = 2; i < argv.length; i++) {\n switch (argv[i]) {\n case \"-p\":\n case \"--port\":\n const portStr = argv[++i];\n if (!portStr || isNaN(parseInt(portStr, 10))) {\n console.error(\"Error: -p/--port requires a number\");\n process.exit(1);\n }\n args.port = parseInt(portStr, 10);\n break;\n case \"-c\":\n case \"--config\":\n const configPath = argv[++i];\n if (!configPath) {\n console.error(\"Error: -c/--config requires a path\");\n process.exit(1);\n }\n args.config = configPath;\n break;\n case \"-v\":\n case \"--verbose\":\n args.verbose = true;\n break;\n case \"-h\":\n case \"--help\":\n args.help = true;\n break;\n case \"--daemon\":\n args.daemon = true;\n break;\n case \"--monitor\":\n args.monitor = true;\n break;\n }\n }\n return args;\n}\n\nfunction printHelp() {\n console.log(`\nModelWeaver — Multi-provider model orchestration proxy for Claude Code\n\nUsage: modelweaver [command] [options]\n\nCommands:\n init [--quick] Run interactive setup wizard (--quick for express mode)\n start Start as background daemon\n stop Stop background daemon\n status Show daemon status\n remove Stop daemon and remove PID + log files\n reload Reload daemon worker (load fresh code after build)\n install Install launchd service (auto-start at login)\n uninstall Uninstall launchd service\n gui Launch the GUI (downloads if needed)\n\nOptions:\n -p, --port <number> Server port (default: from config)\n -c, --config <path> Config file path (auto-detected)\n -v, --verbose Enable debug logging (default: off)\n -h, --help Show this help\n\nConfig locations (first found wins):\n ./modelweaver.yaml\n ~/.modelweaver/config.yaml\n`);\n}\n\nasync function main() {\n const args = parseArgs(process.argv);\n\n // Load .env file if present (created by modelweaver init)\n try {\n const dotenv = await import('dotenv');\n const { existsSync } = await import('node:fs');\n const { join } = await import('node:path');\n const home = process.env.HOME || process.env.USERPROFILE || '';\n // Try cwd/.env first, then ~/.modelweaver/.env, then ~/.env\n const paths = [\n join(process.cwd(), '.env'),\n join(home, '.modelweaver', '.env'),\n join(home, '.env'),\n ];\n for (const p of paths) {\n if (existsSync(p)) {\n dotenv.config({ path: p, quiet: true });\n break;\n }\n }\n } catch {\n // dotenv not installed or .env not present — continue without it\n }\n\n // Handle 'init' subcommand — dynamic import to avoid loading prompts for normal startup\n if (process.argv[2] === 'init') {\n const quick = process.argv.includes('--quick') || process.argv.includes('-q');\n const { runInit } = await import('./init.js');\n await runInit({ quick });\n process.exit(0);\n }\n\n // Handle 'start' subcommand\n if (process.argv[2] === 'start') {\n const { startDaemon } = await import('./daemon.js');\n const result = await startDaemon(args.config, args.port, args.verbose);\n console.log(` ${result.message}`);\n console.log(` Log file: ${result.logPath}`);\n process.exit(result.success ? 0 : 1);\n }\n\n // Handle 'stop' subcommand\n if (process.argv[2] === 'stop') {\n const { stopDaemon } = await import('./daemon.js');\n const result = await stopDaemon();\n console.log(` ${result.message}`);\n process.exit(result.success ? 0 : 1);\n }\n\n // Handle 'status' subcommand\n if (process.argv[2] === 'status') {\n const { statusDaemon } = await import('./daemon.js');\n const result = await statusDaemon();\n console.log(` ${result.message}`);\n try {\n const { getService } = await import('./service.js');\n const svc = await getService();\n const installed = svc.isInstalled();\n if (installed) {\n console.log(` Service: installed`);\n } else {\n console.log(` Service: not installed (run \"modelweaver install\" to enable auto-start)`);\n }\n } catch (err) {\n console.log(` Service: ${err instanceof Error ? err.message : String(err)}`);\n }\n process.exit(0);\n }\n\n // Handle 'remove' subcommand — stop + clean up PID and log files\n if (process.argv[2] === 'remove') {\n const { removeDaemon } = await import('./daemon.js');\n const result = await removeDaemon();\n console.log(` ${result.message}`);\n process.exit(result.success ? 0 : 1);\n }\n\n // Handle 'install' subcommand — install platform service\n if (process.argv[2] === 'install') {\n try {\n const { getService } = await import('./service.js');\n const svc = await getService();\n await svc.install();\n } catch (err) {\n console.error(` Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n process.exit(0);\n }\n\n // Handle 'uninstall' subcommand — uninstall platform service\n if (process.argv[2] === 'uninstall') {\n try {\n const { getService } = await import('./service.js');\n const svc = await getService();\n svc.uninstall();\n } catch (err) {\n console.error(` Error: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n process.exit(0);\n }\n\n // Handle 'gui' subcommand\n if (process.argv[2] === 'gui') {\n const { launchGui } = await import('./gui-launcher.js');\n await launchGui();\n process.exit(0);\n }\n\n // Handle 'reload' subcommand\n if (process.argv[2] === 'reload') {\n const { reloadDaemon } = await import('./daemon.js');\n await reloadDaemon(args.port);\n process.exit(0);\n }\n\n if (args.help) {\n printHelp();\n process.exit(0);\n }\n\n // Load config\n let config;\n let configPath;\n try {\n const result = await loadConfig(args.config);\n config = result.config;\n configPath = result.configPath;\n } catch (error) {\n console.error(`Config error: ${(error as Error).message}`);\n process.exit(1);\n }\n\n // CLI port override\n const port = args.port || config.server.port;\n const host = config.server.host;\n const logLevel: LogLevel = args.verbose ? \"debug\" : \"info\";\n\n // Initialize metrics store\n const metricsStore = new MetricsStore();\n\n // --- Monitor mode (spawns daemon child, auto-restarts on crash) ---\n if (args.monitor) {\n await startMonitor(args);\n return;\n }\n\n // --- Daemon mode ---\n if (args.daemon) {\n const { removeWorkerPidFile, writeWorkerPidFile, createDebouncedReload, getLogPath } = await import('./daemon.js');\n const { reloadConfig } = await import('./config.js');\n const { createWriteStream, watch } = await import('node:fs');\n const { createLogger } = await import('./logger.js');\n const logger = createLogger(logLevel);\n\n // Prevent silent crashes from killing the daemon worker\n process.on('uncaughtException', (err) => {\n logger.error('Uncaught exception (daemon survived)', { error: err.message, stack: err.stack });\n });\n process.on('unhandledRejection', (reason) => {\n logger.error('Unhandled rejection (daemon survived)', { reason: String(reason) });\n });\n\n // Write worker PID file (monitor owns modelweaver.pid)\n await writeWorkerPidFile(process.pid);\n\n // Redirect stdout/stderr to log file\n const logStream = createWriteStream(getLogPath(), { flags: 'a' });\n logStream.on('error', () => { /* ignore write errors to log file */ });\n process.stdout.write = logStream.write.bind(logStream) as typeof process.stdout.write;\n process.stderr.write = logStream.write.bind(logStream) as typeof process.stderr.write;\n\n // Create app with mutable config\n const handle = createApp(config, logLevel, metricsStore);\n\n // Hot-reload: watch config file for changes\n let configWatcher: ReturnType<typeof watch> | null = null;\n if (configPath) {\n const debounced = createDebouncedReload(async () => {\n try {\n const newConfig = await reloadConfig(configPath);\n await handle.setConfig(newConfig);\n latencyTracker.prune([...newConfig.providers.keys()]);\n logger.info(\"Config reloaded\", { path: configPath });\n } catch (err) {\n logger.error(\"Config reload failed — keeping old config\", { error: (err as Error).message });\n }\n }, 300);\n\n try {\n configWatcher = watch(configPath, () => {\n debounced.reload();\n });\n configWatcher.on('error', () => {\n // fs.watch failed — silently disable hot-reload\n if (configWatcher) {\n configWatcher.close();\n configWatcher = null;\n }\n });\n } catch {\n // fs.watch not available — hot-reload disabled\n }\n }\n\n // SIGUSR1 triggers config hot-reload\n // Note: SIGUSR1 is POSIX-only; this handler is a no-op on Windows.\n process.on('SIGUSR1', async () => {\n try {\n const newConfig = await reloadConfig(configPath!);\n await handle.setConfig(newConfig);\n latencyTracker.prune([...newConfig.providers.keys()]);\n logger.info(\"Config reloaded (SIGUSR1)\", { path: configPath });\n } catch (err) {\n logger.error(\"Config reload failed (SIGUSR1)\", { error: (err as Error).message });\n }\n });\n\n // Start server — register error handler BEFORE listen() so EADDRINUSE is caught\n const server = createAdaptorServer({\n fetch: handle.app.fetch,\n hostname: host,\n port,\n serverOptions: {\n requestTimeout: 300_000, // 5 min max total request time (covers long SSE streams)\n headersTimeout: 10_000, // 10s to receive headers\n keepAliveTimeout: 30_000, // 30s keep-alive (matches undici agent)\n },\n });\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n logger.error(`Port ${port} already in use, exiting for monitor restart`, { port });\n process.exit(1);\n }\n });\n server.listen(port, host);\n attachWebSocket(server as any, metricsStore);\n\n // Graceful shutdown\n const shutdown = async () => {\n if (configWatcher) {\n configWatcher.close();\n configWatcher = null;\n }\n await handle.closeAgents();\n await removeWorkerPidFile();\n logStream.end();\n process.exit(0);\n };\n process.on('SIGTERM', shutdown);\n process.on('SIGINT', shutdown);\n\n return; // Don't fall through to foreground mode\n }\n\n // --- Foreground mode ---\n const handle = createApp(config, logLevel, metricsStore);\n\n // Print startup info\n console.log(`\\n ModelWeaver v${VERSION}`);\n console.log(` Listening: http://${host}:${port}`);\n console.log(` Config: ${configPath}\\n`);\n\n console.log(\" Routes:\");\n for (const [tier, entries] of config.routing) {\n const providerList = entries\n .map((e, i) => `${e.provider}${i === 0 ? \" (primary)\" : \" (fallback)\"}`)\n .join(\", \");\n console.log(` ${tier.padEnd(8)} → ${providerList}`);\n }\n console.log();\n\n if (config.modelRouting.size > 0) {\n console.log(\" Model Routes:\");\n for (const [model, entries] of config.modelRouting) {\n const providerList = entries\n .map((e, i) => `${e.provider}${i === 0 ? \" (primary)\" : \" (fallback)\"}`)\n .join(\", \");\n console.log(` ${model.padEnd(20)} → ${providerList}`);\n }\n console.log();\n }\n\n // Start server — register error handler BEFORE listen() so EADDRINUSE is caught\n const server = createAdaptorServer({\n fetch: handle.app.fetch,\n hostname: host,\n port,\n serverOptions: {\n requestTimeout: 300_000, // 5 min max total request time (covers long SSE streams)\n headersTimeout: 10_000, // 10s to receive headers\n keepAliveTimeout: 30_000, // 30s keep-alive (matches undici agent)\n },\n });\n server.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n console.error(`Port ${port} already in use, exiting for monitor restart`);\n process.exit(1);\n }\n });\n server.listen(port, host);\n attachWebSocket(server as any, metricsStore);\n\n // Graceful shutdown\n const shutdown = async () => {\n console.log(\"\\n Shutting down...\");\n await handle.closeAgents();\n process.exit(0);\n };\n process.on(\"SIGTERM\", shutdown);\n process.on(\"SIGINT\", shutdown);\n}\n\nmain();\n","// src/server.ts\nimport { Hono } from \"hono\";\nimport { resolveRequest, clearRoutingCache } from \"./router.js\";\nimport { forwardWithFallback } from \"./proxy.js\";\nimport { createLogger, type LogLevel } from \"./logger.js\";\nimport type { AppConfig, ProviderConfig, RequestContext } from \"./types.js\";\nimport { randomUUID } from \"node:crypto\";\nimport { gzip } from \"node:zlib\";\nimport { promisify } from \"node:util\";\n\nconst gzipAsync = promisify(gzip);\nimport type { MetricsStore } from \"./metrics.js\";\nimport { broadcastStreamEvent } from \"./ws.js\";\nimport type { StreamEvent } from \"./types.js\";\n\nconst MODEL_CONTEXT_WINDOWS: Record<string, number> = {\n 'claude-opus-4-6': 200000,\n 'claude-sonnet-4-6': 200000,\n 'claude-haiku-4-5-20251001': 200000,\n 'claude-3-5-sonnet': 200000,\n 'claude-3-5-haiku': 200000,\n 'glm-4.7': 128000,\n 'glm-5-turbo': 128000,\n};\n\nfunction getContextWindow(model: string): number {\n // Exact match first, then prefix match\n if (MODEL_CONTEXT_WINDOWS[model]) return MODEL_CONTEXT_WINDOWS[model];\n for (const [key, size] of Object.entries(MODEL_CONTEXT_WINDOWS)) {\n if (model.startsWith(key)) return size;\n }\n return 0;\n}\n\nfunction computeCacheHitRate(cacheRead: number, cacheCreation: number, input: number): number {\n const totalInput = input + cacheRead + cacheCreation;\n if (totalInput <= 0) return 0;\n return Math.round((cacheRead / totalInput) * 1000) / 10;\n}\n\nfunction computeContextPercent(input: number, cacheRead: number, cacheCreation: number, output: number, contextWindow: number): number {\n if (contextWindow <= 0) return 0;\n const total = input + cacheRead + cacheCreation + output;\n return Math.round((total / contextWindow) * 1000) / 10;\n}\n\nfunction anthropicError(type: string, message: string, requestId: string): Response {\n return new Response(\n JSON.stringify({ type: \"error\", error: { type, message } }),\n {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"x-request-id\": requestId,\n },\n }\n );\n}\n\n/**\n * Parse token counts from an SSE data line's JSON payload.\n * Supports both Anthropic (input_tokens/output_tokens) and OpenAI (prompt_tokens/completion_tokens) formats.\n */\nfunction parseUsageFromData(data: Record<string, unknown>): { inputTokens: number; outputTokens: number; cacheReadTokens: number; cacheCreationTokens: number } {\n const usage = (data.message as Record<string, unknown> | undefined)?.usage as Record<string, unknown> | undefined\n ?? data.usage as Record<string, unknown> | undefined;\n if (!usage) return { inputTokens: 0, outputTokens: 0, cacheReadTokens: 0, cacheCreationTokens: 0 };\n\n const inp = (usage.input_tokens as number | undefined) ?? (usage.prompt_tokens as number | undefined) ?? 0;\n const out = (usage.output_tokens as number | undefined) ?? (usage.completion_tokens as number | undefined) ?? 0;\n const cacheRead = (usage.cache_read_input_tokens as number | undefined) ?? 0;\n const cacheCreation = (usage.cache_creation_input_tokens as number | undefined) ?? 0;\n\n return { inputTokens: inp, outputTokens: out, cacheReadTokens: cacheRead, cacheCreationTokens: cacheCreation };\n}\n\n/**\n * Creates a TransformStream that forwards chunks unchanged while extracting\n * token counts for metrics inline (no tee() or separate reader needed).\n * For SSE responses, extracts token counts from usage events incrementally.\n * For non-streaming JSON responses, uses a bounded sliding-window regex scan.\n */\nfunction createMetricsTransform(\n ctx: { requestId: string; model: string; actualModel?: string; tier: string; startTime: number; fallbackMode?: \"sequential\" | \"race\" },\n provider: string,\n targetProvider: string,\n metricsStore: MetricsStore,\n status: number,\n contentType: string,\n): TransformStream<Uint8Array, Uint8Array> {\n const td = new TextDecoder();\n\n // --- SSE state ---\n const tokens = { input: 0, output: 0, cacheRead: 0, cacheCreation: 0 };\n let lineBuf = \"\";\n let eventBuf = \"\";\n\n // --- JSON state ---\n const WINDOW_SIZE = 4096;\n let inputTokens = 0;\n let cacheReadTokens = 0;\n let cacheCreationTokens = 0;\n let outputTokens = 0;\n let windowBuf = \"\";\n\n // Detection: resolved after the first chunk arrives\n let isSSE: boolean | null = null;\n\n // Stream event throttling (~4 Hz)\n const STREAM_THROTTLE_MS = 250;\n let lastStreamEmit = 0;\n let firstChunk = true;\n\n // Response text preview (last 100 chars for progress bar tooltip)\n let responsePreview = \"\";\n const PREVIEW_MAX = 100;\n\n const drainEvents = (eventText: string) => {\n for (const event of eventText.split(\"\\n\\n\")) {\n if (!event) continue;\n const dataLine = event.split(\"\\n\").find(l => l.startsWith(\"data:\"));\n if (!dataLine) continue;\n\n // Fast path: skip JSON.parse entirely if the event has nothing we need.\n // Only ~5-10 of ~100-500 SSE events contain usage/preview data.\n const hasUsage = dataLine.indexOf('\"usage\"') !== -1;\n const hasDelta = dataLine.indexOf('\"delta\"') !== -1;\n const hasChoices = dataLine.indexOf('\"choices\"') !== -1;\n if (!hasUsage && !hasDelta && !hasChoices) continue;\n\n try {\n const data = JSON.parse(dataLine.slice(5)) as Record<string, unknown>;\n\n // Extract usage (token counts)\n if (hasUsage) {\n const usage = parseUsageFromData(data);\n if (usage.inputTokens > tokens.input) tokens.input = usage.inputTokens;\n if (usage.outputTokens > tokens.output) tokens.output = usage.outputTokens;\n if (usage.cacheReadTokens > tokens.cacheRead) tokens.cacheRead = usage.cacheReadTokens;\n if (usage.cacheCreationTokens > tokens.creationTokens) tokens.cacheCreation = usage.cacheCreationTokens;\n }\n\n // Extract text content for preview\n // Anthropic format: content_block_delta with delta.text\n if (hasDelta) {\n const delta = data.delta as Record<string, unknown> | undefined;\n if (delta && typeof delta.text === \"string\") {\n responsePreview += delta.text;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n }\n // OpenAI format: choices[0].delta.content\n if (hasChoices) {\n const choices = data.choices as Array<Record<string, unknown>> | undefined;\n if (choices?.[0]) {\n const choiceDelta = choices[0].delta as Record<string, unknown> | undefined;\n if (choiceDelta && typeof choiceDelta.content === \"string\") {\n responsePreview += choiceDelta.content;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n }\n }\n } catch { /* skip malformed */ }\n }\n };\n\n const scanWindow = (text: string) => {\n // Fast bailout: most chunks don't contain usage data\n const hasUsage = text.includes('\"usage\"');\n\n // Single combined regex pass for all token fields\n const TOKEN_RE = /\"(input_tokens|prompt_tokens|cache_read_input_tokens|cache_creation_input_tokens|output_tokens|completion_tokens)\"\\s*:\\s*(\\d+)/g;\n if (hasUsage) {\n let m: RegExpExecArray | null;\n while ((m = TOKEN_RE.exec(text)) !== null) {\n const val = parseInt(m[2], 10);\n const field = m[1];\n if (field === \"input_tokens\" || field === \"prompt_tokens\") {\n if (val > inputTokens) inputTokens = val;\n } else if (field === \"cache_read_input_tokens\") {\n if (val > cacheReadTokens) cacheReadTokens = val;\n } else if (field === \"cache_creation_input_tokens\") {\n if (val > cacheCreationTokens) cacheCreationTokens = val;\n } else if (field === \"output_tokens\" || field === \"completion_tokens\") {\n if (val > outputTokens) outputTokens = val;\n }\n }\n }\n\n // Extract text content for preview from JSON responses\n // Anthropic format: \"content\":[{\"type\":\"text\",\"text\":\"...\"}]\n const anthContent = [...text.matchAll(/\"text\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"/g)];\n if (anthContent.length > 0) {\n const lastText = anthContent[anthContent.length - 1][1].replace(/\\\\n/g, \"\\n\").replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n responsePreview += lastText;\n if (responsePreview.length > PREVIEW_MAX) {\n responsePreview = responsePreview.slice(-PREVIEW_MAX);\n }\n }\n };\n\n const recordMetrics = (inp: number, out: number, cacheRead: number = 0, cacheCreation: number = 0) => {\n try {\n const latencyMs = Date.now() - ctx.startTime;\n const latencySec = latencyMs / 1000;\n const tps = latencySec > 0 ? out / latencySec : 0;\n\n metricsStore.recordRequest({\n requestId: ctx.requestId,\n model: ctx.model,\n actualModel: ctx.actualModel || ctx.model,\n tier: ctx.tier,\n provider,\n targetProvider,\n status,\n inputTokens: inp,\n outputTokens: out,\n latencyMs,\n tokensPerSec: Math.round(tps * 10) / 10,\n timestamp: Date.now(),\n fallbackMode: ctx.fallbackMode,\n cacheReadTokens: cacheRead,\n cacheCreationTokens: cacheCreation,\n });\n\n // Broadcast completion event\n const contextWindow = getContextWindow(ctx.actualModel || ctx.model);\n setImmediate(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: ctx.model,\n tier: ctx.tier,\n state: \"complete\",\n status,\n latencyMs: Date.now() - ctx.startTime,\n inputTokens: inp,\n outputTokens: out,\n tokensPerSec: Math.round(tps * 10) / 10,\n timestamp: Date.now(),\n cacheReadTokens: cacheRead,\n cacheCreationTokens: cacheCreation,\n cacheHitRate: computeCacheHitRate(cacheRead, cacheCreation, inp),\n contextPercent: computeContextPercent(inp, cacheRead, cacheCreation, out, contextWindow),\n contextWindowSize: contextWindow || undefined,\n });\n });\n } catch {\n // Metrics recording errors must not affect the response stream\n }\n };\n\n const processChunk = (decoded: string, isFinal: boolean) => {\n if (isSSE === null) {\n // First chunk — detect format\n isSSE = contentType.includes(\"text/event-stream\") || decoded.startsWith(\"event:\");\n }\n\n if (isSSE) {\n lineBuf += decoded;\n const lines = lineBuf.split(\"\\n\");\n lineBuf = lines.pop()!;\n\n for (const line of lines) {\n if (line === \"\") {\n if (eventBuf) {\n drainEvents(eventBuf);\n eventBuf = \"\";\n }\n } else {\n eventBuf += (eventBuf ? \"\\n\" : \"\") + line;\n }\n }\n\n if (isFinal && eventBuf.trim()) drainEvents(eventBuf);\n\n // Emit streaming progress (throttled ~4 Hz)\n const now = Date.now();\n if (firstChunk || now - lastStreamEmit >= STREAM_THROTTLE_MS) {\n lastStreamEmit = now;\n firstChunk = false;\n const contextWindow = getContextWindow(ctx.actualModel || ctx.model);\n setImmediate(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: ctx.model,\n tier: ctx.tier,\n state: \"streaming\",\n outputTokens: tokens.output,\n timestamp: now,\n preview: responsePreview,\n cacheHitRate: computeCacheHitRate(tokens.cacheRead, tokens.cacheCreation, tokens.input),\n contextPercent: computeContextPercent(tokens.input, tokens.cacheRead, tokens.cacheCreation, tokens.output, contextWindow),\n contextWindowSize: contextWindow || undefined,\n });\n });\n }\n\n if (isFinal) {\n recordMetrics(tokens.input, tokens.output, tokens.cacheRead, tokens.cacheCreation);\n }\n } else {\n windowBuf += decoded;\n if (windowBuf.length > WINDOW_SIZE) {\n windowBuf = windowBuf.slice(-WINDOW_SIZE);\n }\n scanWindow(windowBuf);\n\n // Emit streaming progress (throttled ~4 Hz)\n const nowJson = Date.now();\n if (firstChunk || nowJson - lastStreamEmit >= STREAM_THROTTLE_MS) {\n lastStreamEmit = nowJson;\n firstChunk = false;\n const contextWindow = getContextWindow(ctx.actualModel || ctx.model);\n setImmediate(() => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: ctx.model,\n tier: ctx.tier,\n state: \"streaming\",\n outputTokens,\n timestamp: nowJson,\n preview: responsePreview,\n cacheHitRate: computeCacheHitRate(cacheReadTokens, cacheCreationTokens, inputTokens),\n contextPercent: computeContextPercent(inputTokens, cacheReadTokens, cacheCreationTokens, outputTokens, contextWindow),\n contextWindowSize: contextWindow || undefined,\n });\n });\n }\n\n if (isFinal) {\n recordMetrics(inputTokens, outputTokens, cacheReadTokens, cacheCreationTokens);\n }\n }\n };\n\n return new TransformStream({\n transform(chunk, controller) {\n controller.enqueue(chunk);\n processChunk(td.decode(chunk, { stream: true }), false);\n },\n flush() {\n processChunk(\"\", true);\n },\n });\n}\n\nexport interface AppHandle {\n app: Hono;\n getConfig: () => AppConfig;\n setConfig: (config: AppConfig) => Promise<void>;\n closeAgents: () => Promise<void>;\n}\n\nfunction agentKey(provider: ProviderConfig): string {\n const origin = provider._cachedOrigin;\n const size = provider.poolSize ?? 10;\n return `${origin ?? \"unknown\"}:${size}`;\n}\n\nexport function createApp(initConfig: AppConfig, logLevel: LogLevel, metricsStore?: MetricsStore): AppHandle {\n let config: AppConfig = initConfig;\n const logger = createLogger(logLevel);\n const app = new Hono();\n\n // Global error handler — returns Anthropic-compatible JSON error responses\n app.onError((err, c) => {\n console.error(`[server] Unhandled error: ${err.message}`);\n return c.json(\n { type: \"error\", error: { type: \"api_error\", message: \"Internal proxy error\" } },\n { status: 500, headers: { \"content-type\": \"application/json\" } }\n );\n });\n\n // CORS for GUI (Tauri WebView has origin tauri://localhost)\n app.use(\"/api/*\", async (c, next) => {\n c.header(\"Access-Control-Allow-Origin\", \"*\");\n await next();\n });\n // Handle CORS preflight for API routes only (GUI needs CORS; proxy endpoint does not)\n app.options(\"/api/*\", (c) => {\n c.header(\"Access-Control-Allow-Origin\", \"*\");\n c.header(\"Access-Control-Allow-Methods\", \"GET, POST, OPTIONS\");\n c.header(\"Access-Control-Allow-Headers\", \"Content-Type, Authorization, anthropic-version, x-api-key\");\n return c.body(\"\", 200);\n });\n\n app.post(\"/v1/messages\", async (c) => {\n const requestId = randomUUID();\n\n // Read raw body once, then parse — avoids double serialization\n let body: { model?: string };\n let rawBody: string;\n try {\n rawBody = await c.req.text();\n body = JSON.parse(rawBody);\n } catch {\n return anthropicError(\"invalid_request_error\", \"Invalid JSON body\", requestId);\n }\n\n const model = body.model;\n if (!model) {\n return anthropicError(\"invalid_request_error\", \"Missing 'model' field in request body\", requestId);\n }\n\n const ctx = resolveRequest(model, requestId, config, rawBody);\n if (ctx) {\n (ctx as RequestContext & { parsedBody?: Record<string, unknown> }).parsedBody = body as Record<string, unknown>;\n }\n if (!ctx) {\n logger.info(\"No tier match\", { requestId, model });\n const configuredModels = config.modelRouting.size > 0\n ? ` Configured model routes: ${[...config.modelRouting.keys()].join(\", \")}.`\n : \"\";\n return anthropicError(\n \"invalid_request_error\",\n `No route matches model \"${model}\". Configured tiers: ${[...config.tierPatterns.keys()].join(\", \")}.${configuredModels}`,\n requestId\n );\n }\n\n logger.info(\"Routing request\", {\n requestId,\n model,\n tier: ctx.tier,\n providers: ctx.providerChain.map((e) => e.provider),\n });\n\n // Broadcast stream start event\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"start\",\n provider: ctx.providerChain[0]?.provider ?? \"unknown\",\n timestamp: Date.now(),\n });\n\n // Forward with fallback chain\n let successfulProvider = \"unknown\";\n let response: Response;\n try {\n response = await forwardWithFallback(\n config.providers,\n ctx.providerChain,\n ctx,\n c.req.raw,\n (provider, index) => {\n logger.info(\"Attempting provider\", { requestId, provider, index, tier: ctx.tier });\n // Only capture first attempted provider; accurate winner tracking requires\n // an onSuccess callback in proxy.ts (handled separately).\n if (!successfulProvider) successfulProvider = provider;\n },\n logger\n );\n // Broadcast TTFB event — headers received from upstream (skip for error responses)\n if (response.status < 400) {\n let headerSize = 17; // approximate HTTP status line: \"HTTP/1.1 200 OK\\r\\n\"\n response.headers.forEach((v, k) => { headerSize += k.length + v.length + 4; });\n headerSize += 2; // trailing CRLF\n setImmediate(() => {\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"ttfb\",\n status: response.status,\n headerSize,\n timestamp: Date.now(),\n });\n });\n }\n\n } catch (err) {\n const errMsg = err instanceof Error ? err.message : String(err);\n logger.error(\"Forward failed\", { requestId, error: errMsg });\n setImmediate(() => {\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"error\",\n status: 502,\n message: errMsg,\n timestamp: Date.now(),\n });\n });\n return c.json(\n { type: \"error\", error: { type: \"api_error\", message: \"Upstream request failed: \" + errMsg } },\n 502\n );\n }\n\n // Broadcast error event for non-2xx responses\n if (response.status >= 400) {\n setImmediate(() => {\n broadcastStreamEvent({\n requestId,\n model,\n tier: ctx.tier,\n state: \"error\",\n status: response.status,\n message: `HTTP ${response.status}`,\n timestamp: Date.now(),\n });\n });\n }\n\n // Extract tokens via inline TransformStream for successful responses\n let responseBody: ReadableStream<Uint8Array> | null = response.body;\n if (response.body && response.status >= 200 && response.status < 300 && metricsStore) {\n const targetProvider = ctx.providerChain.length > 0 ? ctx.providerChain[0].provider : successfulProvider;\n const transform = createMetricsTransform(ctx, successfulProvider, targetProvider, metricsStore, response.status, response.headers.get(\"content-type\") || \"\");\n responseBody = response.body.pipeThrough(transform) as typeof responseBody;\n }\n\n // Add request ID to response (responses from fetch have immutable headers, so create new)\n const newHeaders = new Headers(response.headers);\n newHeaders.set(\"x-request-id\", requestId);\n const finalResponse = new Response(responseBody, {\n status: response.status,\n statusText: response.statusText,\n headers: newHeaders,\n });\n\n const latency = Date.now() - ctx.startTime;\n logger.info(\"Request completed\", {\n requestId,\n model,\n tier: ctx.tier,\n status: finalResponse.status,\n latencyMs: latency,\n });\n\n return finalResponse;\n });\n\n // REST endpoint for metrics summary (used by GUI on connect)\n // Returns gzip-compressed JSON when client supports it\n app.get(\"/api/metrics/summary\", async (c) => {\n if (!metricsStore) return c.json({ error: \"Metrics not enabled\" }, 503);\n const data = metricsStore.getSummary();\n const json = JSON.stringify(data);\n\n const acceptEncoding = c.req.header(\"accept-encoding\") || \"\";\n if (acceptEncoding.includes(\"gzip\") && json.length >= 1024) {\n const compressed = await gzipAsync(Buffer.from(json));\n return new Response(compressed, {\n status: 200,\n headers: {\n \"content-type\": \"application/json\",\n \"content-encoding\": \"gzip\",\n \"vary\": \"accept-encoding\",\n },\n });\n }\n\n return c.json(data);\n });\n\n // Circuit breaker status endpoint\n app.get(\"/api/circuit-breaker\", (c) => {\n const status: Record<string, { state: string; failures: number; lastFailure: string | null }> = {};\n for (const [name, provider] of config.providers) {\n const breaker = provider._circuitBreaker;\n if (breaker) {\n const s = breaker.getStatus();\n status[name] = {\n state: s.state,\n failures: s.failures,\n lastFailure: s.lastFailure ? new Date(s.lastFailure).toISOString() : null,\n };\n }\n }\n return c.json(status);\n });\n\n return {\n app,\n getConfig: () => config,\n setConfig: async (newConfig: AppConfig) => {\n // Build key → agent map from old config for reuse lookup\n const oldAgents = new Map<string, import(\"undici\").Agent>();\n for (const provider of config.providers.values()) {\n if (provider._agent) {\n oldAgents.set(agentKey(provider), provider._agent);\n }\n }\n\n // For each new provider, check if we can reuse an existing agent\n const reusedKeys = new Set<string>();\n for (const provider of newConfig.providers.values()) {\n const key = agentKey(provider);\n const existingAgent = oldAgents.get(key);\n if (existingAgent) {\n // Reuse: the origin and poolSize haven't changed\n provider._agent = existingAgent;\n reusedKeys.add(key);\n }\n // else: loadConfig() already created a fresh agent for this provider\n }\n\n // Close agents that are no longer needed (removed or changed origin/poolSize)\n const closePromises: Promise<void>[] = [];\n for (const [key, agent] of oldAgents) {\n if (!reusedKeys.has(key)) {\n closePromises.push(agent.close().catch(() => {}));\n }\n }\n\n config = newConfig;\n clearRoutingCache();\n await Promise.all(closePromises);\n },\n closeAgents: async () => {\n const closePromises: Promise<void>[] = [];\n for (const provider of config.providers.values()) {\n if (provider._agent) {\n closePromises.push(provider._agent.close().catch(() => {}));\n }\n }\n await Promise.all(closePromises);\n },\n };\n}\n","// src/router.ts\nimport type { RoutingEntry, AppConfig, RequestContext } from \"./types.js\";\n\nconst ROUTING_CACHE_MAX_SIZE = 200;\n\ninterface RoutingCacheEntry {\n tier: string;\n providerChain: RoutingEntry[];\n}\n\n/**\n * LRU cache for model-to-(tier, providerChain) lookups.\n * Map insertion order serves as LRU ordering (first = oldest).\n */\nconst routingCache = new Map<string, RoutingCacheEntry>();\n\n/**\n * Invalidate the routing cache. Called on config hot-reload.\n */\nexport function clearRoutingCache(): void {\n routingCache.clear();\n}\n\n/**\n * Match a model name to a tier using case-sensitive substring matching.\n * First tier whose patterns contain any match wins (config order = priority).\n */\nexport function matchTier(\n modelName: string,\n tierPatterns: Map<string, string[]>\n): string | null {\n for (const [tier, patterns] of tierPatterns) {\n for (const pattern of patterns) {\n if (modelName.includes(pattern)) {\n return tier;\n }\n }\n }\n return null;\n}\n\n/**\n * Get the ordered routing chain for a tier.\n */\nexport function buildRoutingChain(\n tier: string,\n routing: Map<string, RoutingEntry[]>\n): RoutingEntry[] {\n return routing.get(tier) || [];\n}\n\n/**\n * Build a RequestContext from an incoming model name and raw body.\n * Priority 1: exact model name match in modelRouting.\n * Priority 2: substring match via tierPatterns.\n * Uses an LRU cache to skip repeated resolution for the same model.\n * Returns null if no route matches.\n */\nexport function resolveRequest(\n model: string,\n requestId: string,\n config: AppConfig,\n rawBody: string\n): RequestContext | null {\n // Check LRU cache first\n const cached = routingCache.get(model);\n if (cached) {\n // Move to most-recently-used position (delete + re-insert)\n routingCache.delete(model);\n routingCache.set(model, cached);\n return {\n requestId,\n model,\n tier: cached.tier,\n providerChain: cached.providerChain,\n startTime: Date.now(),\n rawBody,\n };\n }\n\n let tier: string;\n let providerChain: RoutingEntry[];\n\n // Priority 1: exact model name match in modelRouting\n const modelChain = config.modelRouting.get(model);\n if (modelChain && modelChain.length > 0) {\n tier = \"(modelRouting)\";\n providerChain = modelChain;\n } else {\n // Priority 2: substring match via tierPatterns (existing behavior)\n const matchedTier = matchTier(model, config.tierPatterns);\n if (!matchedTier) return null;\n tier = matchedTier;\n providerChain = buildRoutingChain(tier, config.routing);\n }\n\n // Cache the resolved tier + providerChain\n if (routingCache.size >= ROUTING_CACHE_MAX_SIZE) {\n // Evict the oldest entry (first key in Map)\n const oldestKey = routingCache.keys().next().value;\n if (oldestKey !== undefined) routingCache.delete(oldestKey);\n }\n routingCache.set(model, { tier, providerChain });\n\n return {\n requestId,\n model,\n tier,\n providerChain,\n startTime: Date.now(),\n rawBody,\n };\n}\n","// src/proxy.ts\nimport type { ProviderConfig, RoutingEntry, RequestContext } from \"./types.js\";\nimport { request as undiciRequest } from \"undici\";\nimport { PassThrough } from \"node:stream\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { latencyTracker, inFlightCounter, computeHedgingCount } from './hedging.js';\nimport { broadcastStreamEvent } from './ws.js';\n\n/**\n * Shallow-clone a parsed API request body just enough so that\n * cleanOrphanedToolMessages() can safely reassign body.messages\n * without affecting the original parsed object.\n *\n * cleanOrphanedToolMessages either:\n * - leaves body.messages untouched (no orphans found), or\n * - replaces body.messages with a new array (via .map())\n * It never mutates individual message objects in-place.\n * Therefore a one-level-deep clone of the messages array is sufficient.\n */\nfunction shallowCloneForMutation(parsed: Record<string, unknown>): Record<string, unknown> {\n const clone = { ...parsed };\n if (Array.isArray(clone.messages)) {\n clone.messages = [...clone.messages];\n }\n return clone;\n}\n\n/** Headers forwarded as-is to upstream */\nconst FORWARD_HEADERS = new Set([\n \"anthropic-version\",\n \"anthropic-beta\",\n \"content-type\",\n \"accept\",\n]);\n\n/** Pre-compiled regex for normalizing duplicate slashes in URL paths */\nconst MULTI_SLASH = /\\/+/g;\n\n/** Pre-compiled regex for stripping origin from URLs */\nconst STRIP_ORIGIN = /^https?:\\/\\/[^/]+/;\n\n/** Pre-compiled regexes for targeted body replacements (preserve prompt caching) */\nconst MODEL_KEY_REGEX = /\"model\"\\s*:\\s*\"([^\"]*)\"/;\nconst MAX_TOKENS_REGEX = /\"max_tokens\"\\s*:\\s*(\\d+)/;\n\n/** Module-level TextEncoder — avoids per-request allocation */\nconst textEncoder = new TextEncoder();\n\n// --- Pre-built error response helpers ---\n\nconst ERR_HEADERS = Object.freeze({ \"content-type\": \"application/json\" });\n\nfunction unknownProviderErr(providerName: string): Response {\n const body = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Unknown provider: ${providerName}` },\n });\n return new Response(body, {\n status: 502,\n headers: { ...ERR_HEADERS, \"content-length\": String(textEncoder.encode(body).byteLength) },\n });\n}\n\nfunction circuitBreakerErr(providerName: string): Response {\n const body = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${providerName}\" skipped by circuit breaker` },\n });\n return new Response(body, {\n status: 502,\n headers: { ...ERR_HEADERS, \"content-length\": String(textEncoder.encode(body).byteLength) },\n });\n}\n\nfunction providerFailedErr(providerName: string): Response {\n const body = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${providerName}\" failed` },\n });\n return new Response(body, { status: 502, headers: ERR_HEADERS });\n}\n\n/** Delay (ms) before starting backup providers in staggered race */\nconst SPECULATIVE_DELAY = 3000;\n\nexport function isRetriable(status: number): boolean {\n return status === 429 || status >= 500;\n}\n\nconst CONTEXT_WINDOW_PATTERNS = [\n 'context window', 'context_limit', 'token limit',\n 'prompt is too long', 'max tokens', 'input too large', 'too many tokens',\n];\n\nfunction isContextWindowError(status: number, body: string): boolean {\n if (status !== 400 && status !== 413) return false;\n const lower = body.toLowerCase();\n return CONTEXT_WINDOW_PATTERNS.some(p => lower.includes(p));\n}\n\nfunction handleContextWindowError(status: number, body: string): Response | null {\n if (!isContextWindowError(status, body)) return null;\n\n console.warn('[context-compact] Upstream context window limit detected');\n try {\n const flagDir = path.join(os.homedir(), '.claude', 'state');\n fs.mkdirSync(flagDir, { recursive: true });\n fs.writeFileSync(path.join(flagDir, 'context-compact-needed'), Date.now().toString());\n } catch {\n // Best-effort flag write\n }\n\n const enhanced = JSON.stringify({\n type: \"error\",\n error: {\n type: \"invalid_request_error\",\n message: \"Context window limit reached. Run /compact to reduce conversation size, then retry.\",\n },\n });\n return new Response(enhanced, {\n status: 413,\n headers: { \"content-type\": \"application/json\" },\n });\n}\n\nexport function buildOutboundUrl(\n baseUrlOrProvider: string | { baseUrl: string; _cachedOrigin?: string; _cachedPathname?: string },\n incomingPath: string,\n): string {\n // Extract baseUrl and cached components\n let baseUrl: string;\n let cachedOrigin: string | undefined;\n let cachedPathname: string | undefined;\n\n if (typeof baseUrlOrProvider === 'object' && baseUrlOrProvider !== null) {\n baseUrl = baseUrlOrProvider.baseUrl;\n cachedOrigin = baseUrlOrProvider._cachedOrigin;\n cachedPathname = baseUrlOrProvider._cachedPathname;\n } else {\n baseUrl = baseUrlOrProvider;\n }\n\n let basePath = \"\";\n let origin = baseUrl;\n\n // Use cached values when available (avoids re-parsing baseUrl on every request)\n if (cachedOrigin && cachedPathname !== undefined) {\n origin = cachedOrigin;\n basePath = cachedPathname;\n } else {\n const slashIndex = baseUrl.indexOf('/', baseUrl.indexOf('//') + 2);\n if (slashIndex !== -1) {\n origin = baseUrl.substring(0, slashIndex);\n basePath = baseUrl.substring(slashIndex);\n }\n }\n\n let incomingQuery = \"\";\n let incomingOnly = incomingPath;\n const qIndex = incomingPath.indexOf('?');\n if (qIndex !== -1) {\n incomingOnly = incomingPath.substring(0, qIndex);\n incomingQuery = incomingPath.substring(qIndex);\n }\n\n // Deduplicate /v1 when base URL path already ends with it and incoming path starts with it.\n // e.g. baseUrl=\"https://api.fireworks.ai/inference/v1\" + path=\"/v1/chat/completions\"\n // → \"/inference/v1/chat/completions\" (not \"/inference/v1/v1/chat/completions\")\n let resolvedPath;\n if (basePath.endsWith('/v1') && incomingOnly.startsWith('/v1')) {\n resolvedPath = basePath + incomingOnly.substring(3);\n } else {\n resolvedPath = basePath + incomingOnly;\n }\n\n // Normalize duplicate slashes\n resolvedPath = resolvedPath.replace(MULTI_SLASH, \"/\");\n\n return origin + resolvedPath + incomingQuery;\n}\n\nexport function buildOutboundHeaders(\n incomingHeaders: Headers,\n provider: ProviderConfig,\n requestId: string\n): Headers {\n const headers = new Headers();\n\n // Forward select headers as-is\n for (const name of FORWARD_HEADERS) {\n const value = incomingHeaders.get(name);\n if (value) headers.set(name, value);\n }\n\n // Rewrite auth headers based on provider authType\n if (provider.authType === \"bearer\") {\n headers.set(\"Authorization\", `Bearer ${provider.apiKey}`);\n } else {\n headers.set(\"x-api-key\", provider.apiKey);\n }\n headers.set(\"x-request-id\", requestId);\n\n // Set host to provider hostname (use cached components when available)\n const cachedHost = provider._cachedHost;\n if (cachedHost) {\n headers.set(\"host\", cachedHost);\n } else {\n try {\n const url = new URL(provider.baseUrl);\n headers.set(\"host\", url.host);\n } catch {\n // If baseUrl is not a valid URL, skip host rewrite\n }\n }\n\n return headers;\n}\n\n/**\n * Remove orphaned tool_use/tool_result pairs from the messages array.\n *\n * In Anthropic's format:\n * - tool_use blocks live inside assistant message content: { role: \"assistant\", content: [{ type: \"tool_use\", id: \"call_xxx\", ... }] }\n * - tool_result blocks live inside user message content: { role: \"user\", content: [{ type: \"tool_result\", tool_use_id: \"call_xxx\", ... }] }\n *\n * A tool_result is orphaned if its tool_use_id references a tool_use not in any assistant content block.\n * A tool_use is orphaned if its id has no matching tool_result in any user content block.\n *\n * Single-pass: collects all IDs, computes orphans, then filters once.\n */\nfunction cleanOrphanedToolMessages(body: Record<string, unknown>): void {\n const messages = body.messages;\n if (!Array.isArray(messages)) return;\n\n // Fast exit: count tool blocks — if none exist, skip entirely\n let hasToolBlocks = false;\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (!Array.isArray(msg.content)) continue;\n for (const block of msg.content) {\n if (block.type === \"tool_use\" || block.type === \"tool_result\") {\n hasToolBlocks = true;\n break;\n }\n }\n if (hasToolBlocks) break;\n }\n if (!hasToolBlocks) return;\n\n // Single collection pass — gather every tool_use and tool_result ID\n const allToolUseIds = new Set<string>();\n const allToolResultIds = new Set<string>();\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (!Array.isArray(msg.content)) continue;\n\n for (const block of msg.content) {\n if (block.type === \"tool_use\" && block.id) {\n allToolUseIds.add(String(block.id));\n } else if (block.type === \"tool_result\" && block.tool_use_id) {\n allToolResultIds.add(String(block.tool_use_id));\n }\n }\n }\n\n // Check if any orphans exist before doing filter work\n let hasOrphans = false;\n for (const id of allToolUseIds) {\n if (!allToolResultIds.has(id)) { hasOrphans = true; break; }\n }\n if (!hasOrphans) {\n for (const id of allToolResultIds) {\n if (!allToolUseIds.has(id)) { hasOrphans = true; break; }\n }\n }\n if (!hasOrphans) return;\n\n // Filter pass — mutate messages in-place instead of creating new array\n for (let i = 0; i < messages.length; i++) {\n const msg = messages[i];\n if (!Array.isArray(msg.content)) continue;\n\n let filteredCount = 0;\n for (const block of msg.content) {\n if (block.type === \"tool_use\" && !allToolResultIds.has(String(block.id))) continue;\n if (block.type === \"tool_result\" && !allToolUseIds.has(String(block.tool_use_id))) continue;\n msg.content[filteredCount] = block;\n filteredCount++;\n }\n\n if (filteredCount < msg.content.length) {\n msg.content.length = filteredCount;\n }\n }\n}\n\n/**\n * Apply targeted string replacements to rawBody to preserve prompt caching.\n * On the primary attempt (chainIndex === 0), we avoid full JSON.stringify which\n * breaks Anthropic's cache breakpoints (position-sensitive, whitespace/order-sensitive).\n * Falls back to full JSON parse/mutate/stringify when structural changes are needed.\n */\nfunction applyTargetedReplacements(\n rawBody: string,\n entry: RoutingEntry,\n provider: ProviderConfig,\n parsed: Record<string, unknown>,\n needsOrphanClean: boolean,\n): string {\n // If orphan cleaning is needed, we must do full JSON parse (structural changes to messages)\n if (needsOrphanClean) {\n // shallow clone: cleanOrphanedToolMessages reassigns body.messages\n const mutable = shallowCloneForMutation(parsed);\n if (entry.model) mutable.model = entry.model;\n cleanOrphanedToolMessages(mutable);\n if (provider.modelLimits) {\n const { maxOutputTokens } = provider.modelLimits;\n const requested = typeof mutable.max_tokens === \"number\" ? mutable.max_tokens : maxOutputTokens;\n if (mutable.max_tokens === undefined || requested > maxOutputTokens) {\n mutable.max_tokens = Math.min(requested, maxOutputTokens);\n }\n }\n return JSON.stringify(mutable);\n }\n\n // Targeted replacement path -- only model override and/or max_tokens clamping.\n // Single-pass: build a combined regex with alternation so the entire raw body\n // string is scanned and replaced in one call instead of per-pattern copies.\n const needsModel = !!(entry.model && (parsed.model as string | undefined) !== entry.model);\n let needsMaxTokensClamp = false;\n let needsMaxTokensAdd = false;\n let maxOutputTokens = 0;\n if (provider.modelLimits) {\n maxOutputTokens = provider.modelLimits.maxOutputTokens;\n const m = MAX_TOKENS_REGEX.exec(rawBody);\n if (m) {\n needsMaxTokensClamp = parseInt(m[1], 10) > maxOutputTokens;\n } else if (typeof parsed.max_tokens !== \"number\") {\n needsMaxTokensAdd = true;\n }\n }\n\n if (!needsModel && !needsMaxTokensClamp && !needsMaxTokensAdd) return rawBody;\n\n // max_tokens not in body at all — must add via JSON parse (structural change)\n if (needsMaxTokensAdd) {\n const mutable = { ...parsed };\n if (entry.model) mutable.model = entry.model;\n mutable.max_tokens = maxOutputTokens;\n return JSON.stringify(mutable);\n }\n\n // Build combined regex for single-pass replacement\n const patterns: string[] = [];\n if (needsModel) patterns.push(MODEL_KEY_REGEX.source);\n if (needsMaxTokensClamp) patterns.push(MAX_TOKENS_REGEX.source);\n const combinedRegex = new RegExp(patterns.join(\"|\"), \"g\");\n\n // Capture values for the replacer (avoid repeated property access)\n const modelRepl = needsModel ? `\"model\":\"${entry.model}\"` : null;\n const tokensRepl = needsMaxTokensClamp ? `\"max_tokens\":${maxOutputTokens}` : null;\n const origModel = parsed.model as string | undefined;\n let modelLogged = false;\n\n const result = rawBody.replace(combinedRegex, (match: string) => {\n if (modelRepl && MODEL_KEY_REGEX.test(match)) {\n MODEL_KEY_REGEX.lastIndex = 0;\n if (!modelLogged && origModel) {\n console.warn(`Routing override: ${origModel} -> ${entry.model} via ${provider.name}`);\n modelLogged = true;\n }\n return modelRepl;\n }\n if (tokensRepl && MAX_TOKENS_REGEX.test(match)) {\n MAX_TOKENS_REGEX.lastIndex = 0;\n return tokensRepl;\n }\n return match;\n });\n\n return result;\n}\n\n/**\n * Forward a request to a single provider.\n * Uses ctx.parsedBody when available (avoids re-parsing); falls back to ctx.rawBody.\n * incomingRequest is used for metadata only (url, headers).\n * Returns the Response object — caller decides fallback logic.\n */\nexport async function forwardRequest(\n provider: ProviderConfig,\n entry: RoutingEntry,\n ctx: RequestContext,\n incomingRequest: Request,\n externalSignal?: AbortSignal,\n chainIndex: number = 0,\n): Promise<Response> {\n const outgoingPath = incomingRequest.url.replace(STRIP_ORIGIN, \"\");\n\n // Set actualModel early so metrics always record the routed model,\n // even if body parsing or the fetch itself fails\n if (entry.model) {\n ctx.actualModel = entry.model;\n }\n\n // Build outbound URL from provider base URL and request path\n const url = buildOutboundUrl(provider, outgoingPath);\n\n // Prepare body — prefer raw passthrough to preserve upstream prompt caching.\n // Only parse and re-serialize when a modification is actually required,\n // because Anthropic's cache breakpoints are position-sensitive and\n // JSON.stringify changes whitespace / key order, breaking cache hits.\n let body: string;\n const contentType = incomingRequest.headers.get(\"content-type\") || \"\";\n\n if (contentType.includes(\"application/json\")) {\n try {\n const parsed = (ctx as RequestContext & { parsedBody?: Record<string, unknown> }).parsedBody\n ?? JSON.parse(ctx.rawBody);\n\n // Determine whether any body modification is needed\n let needsModification = false;\n\n // Check 1: Model override needed?\n if (entry.model && (parsed.model as string | undefined) !== entry.model) {\n needsModification = true;\n }\n\n // Check 2: Orphan cleaning needed? (only for fallback attempts, not primary)\n // On the primary attempt (index 0), conversation history is intact.\n // Only when falling back (index > 0) do cross-provider orphans appear.\n const needsOrphanClean = chainIndex > 0;\n if (needsOrphanClean) needsModification = true;\n\n // Check 3: max_tokens clamping needed?\n if (provider.modelLimits) {\n const { maxOutputTokens } = provider.modelLimits;\n const requestedMaxTokens = typeof parsed.max_tokens === \"number\" ? parsed.max_tokens : maxOutputTokens;\n if (parsed.max_tokens === undefined || requestedMaxTokens > maxOutputTokens) {\n needsModification = true;\n }\n }\n\n if (needsModification) {\n // On primary attempt (chainIndex === 0) without orphan cleaning, use targeted\n // string replacements to preserve prompt caching. Anthropic's cache breakpoints\n // are position-sensitive -- JSON.stringify changes whitespace/order, breaking hits.\n if (chainIndex === 0 && !needsOrphanClean) {\n body = applyTargetedReplacements(ctx.rawBody, entry, provider, parsed, false);\n } else {\n // Fallback attempts: full JSON parse/mutate/stringify (caching already broken)\n // shallow clone: cleanOrphanedToolMessages reassigns body.messages\n const mutable = shallowCloneForMutation(parsed);\n\n if (entry.model) {\n const originalModel = mutable.model as string | undefined;\n mutable.model = entry.model;\n if (originalModel && originalModel !== entry.model) {\n console.warn(\n `Routing override: ${originalModel} -> ${entry.model} via ${provider.name}`\n );\n }\n }\n\n if (needsOrphanClean) {\n cleanOrphanedToolMessages(mutable);\n }\n\n if (provider.modelLimits) {\n const { maxOutputTokens } = provider.modelLimits;\n const requestedMaxTokens = typeof mutable.max_tokens === \"number\" ? mutable.max_tokens : maxOutputTokens;\n if (mutable.max_tokens === undefined || requestedMaxTokens > maxOutputTokens) {\n mutable.max_tokens = Math.min(requestedMaxTokens, maxOutputTokens);\n }\n }\n\n body = JSON.stringify(mutable);\n }\n } else {\n // No modifications needed — passthrough raw body to preserve prompt caching\n body = ctx.rawBody;\n }\n } catch {\n // If body can't be parsed, send it as-is without model override\n body = ctx.rawBody;\n }\n } else {\n body = ctx.rawBody;\n }\n\n const headers = buildOutboundHeaders(incomingRequest.headers, provider, ctx.requestId);\n headers.set(\"content-length\", Buffer.byteLength(body, 'utf-8').toString());\n\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), provider.timeout);\n\n // TTFB timeout: fail if no response headers received within ttfbTimeout ms\n const ttfbTimeout = provider.ttfbTimeout ?? 15000;\n let ttfbTimedOut = false;\n let ttfbTimer: ReturnType<typeof setTimeout> | null = null;\n\n const ttfbPromise = new Promise<never>((_, reject) => {\n ttfbTimer = setTimeout(() => {\n ttfbTimedOut = true;\n controller.abort();\n reject(new Error(`TTFB timeout after ${ttfbTimeout}ms`));\n }, ttfbTimeout);\n });\n\n // Listen for external abort (from race cancellation) to abort this request\n let removeAbortListener: (() => void) | undefined;\n let upstreamBody: import(\"node:stream\").Readable | undefined;\n let passThrough: PassThrough | undefined;\n let stallTimerRef: ReturnType<typeof setInterval> | undefined;\n if (externalSignal) {\n if (externalSignal.aborted) {\n // Already aborted — don't even start the request\n clearTimeout(timeout);\n if (ttfbTimer) clearTimeout(ttfbTimer);\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: `Provider \"${provider.name}\" cancelled by race winner` },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n }\n // Increase max listeners to prevent Node.js warning when multiple providers race\n // and all listen to the same sharedController.signal\n const prevMax = (externalSignal as any).getMaxListeners?.() ?? 10;\n (externalSignal as any).setMaxListeners?.(prevMax + 1);\n const onExternalAbort = () => {\n clearTimeout(timeout);\n if (ttfbTimer) clearTimeout(ttfbTimer);\n if (stallTimerRef) clearInterval(stallTimerRef);\n // Destroy upstream body and passThrough to free the connection back to the pool.\n // Deferred to avoid throwing inside AbortSignal event dispatch.\n setImmediate(() => {\n if (upstreamBody && !upstreamBody.destroyed && !(upstreamBody as any).readableEnded) {\n try { (upstreamBody.destroy() as any).catch?.(() => {}); } catch { /* already consumed */ }\n }\n if (passThrough && !passThrough.destroyed) {\n passThrough.destroy(new Error(\"Cancelled\"));\n }\n });\n };\n externalSignal.addEventListener(\"abort\", onExternalAbort, { once: true });\n removeAbortListener = () => {\n externalSignal.removeEventListener(\"abort\", onExternalAbort);\n (externalSignal as any).setMaxListeners?.((externalSignal as any).getMaxListeners?.() - 1);\n };\n }\n\n try {\n const undiciResponse = await Promise.race([\n undiciRequest(url, {\n method: \"POST\",\n headers,\n body,\n signal: controller.signal,\n dispatcher: provider._agent,\n }),\n ttfbPromise,\n ]);\n\n // TTFB succeeded — cancel TTFB timer\n if (ttfbTimer) clearTimeout(ttfbTimer);\n\n // Track upstream body for cleanup on error paths\n upstreamBody = undiciResponse.body;\n\n // Guard against uncaught error events when the pipe is torn down\n // after passThrough.destroy() fires before upstreamBody.destroy().\n upstreamBody.on(\"error\", () => {\n if (stallTimerRef) clearInterval(stallTimerRef);\n });\n\n // For error status codes (4xx/5xx), consume body immediately without stall detection.\n // Rate limits (429) and server errors (5xx) return small JSON bodies that arrive instantly.\n if (undiciResponse.statusCode >= 400) {\n clearTimeout(timeout);\n const errBody = await undiciResponse.body.text();\n return new Response(errBody, {\n status: undiciResponse.statusCode,\n statusText: undiciResponse.statusText,\n headers: undiciResponse.headers as unknown as HeadersInit,\n });\n }\n\n // Body stall detection: pipe through PassThrough to monitor for data without\n // interfering with undici's internal stream state (no flowing mode conflict).\n // Uses a single interval that checks a timestamp instead of per-chunk setTimeout/clearTimeout,\n // reducing syscall-level overhead on every data event.\n const stallTimeout = provider.stallTimeout ?? 30000;\n passThrough = new PassThrough();\n\n const stallMsg = `Body stalled: no data after ${stallTimeout}ms`;\n let lastDataTime = Date.now();\n\n const handleStall = () => {\n broadcastStreamEvent({\n requestId: ctx.requestId,\n model: String(ctx.actualModel ?? entry.model ?? \"\"),\n tier: \"\",\n state: \"error\",\n message: stallMsg,\n timestamp: Date.now(),\n });\n try { (upstreamBody?.destroy(new Error(stallMsg)) as any).catch?.(() => {}); } catch { /* already consumed */ }\n passThrough!.destroy(new Error(stallMsg));\n };\n\n stallTimerRef = setInterval(() => {\n if (Date.now() - lastDataTime >= stallTimeout) {\n clearInterval(stallTimerRef);\n stallTimerRef = undefined;\n handleStall();\n }\n }, Math.min(stallTimeout / 4, 5000));\n\n // Monitor PassThrough for data events — just update timestamp (no timer churn)\n passThrough!.on(\"data\", () => {\n lastDataTime = Date.now();\n });\n\n passThrough.on(\"end\", () => {\n if (stallTimerRef) { clearInterval(stallTimerRef); stallTimerRef = undefined; }\n });\n\n passThrough.on(\"error\", () => {\n if (stallTimerRef) { clearInterval(stallTimerRef); stallTimerRef = undefined; }\n try { passThrough!.destroy(); } catch { /* already destroyed */ }\n });\n\n // Pipe undici body → PassThrough. Data flows through without mode conflicts.\n if (!undiciResponse.body || typeof undiciResponse.body.pipe !== 'function') {\n // Non-standard upstream response (e.g., plain text/buffer) — send as-is\n const fallback = undiciResponse.body\n ? new ReadableStream({ start(controller) { controller.enqueue(new TextEncoder().encode(String(undiciResponse.body))); controller.close(); } })\n : new ReadableStream({ start(controller) { controller.close(); } });\n passThrough.end();\n if (stallTimerRef) clearInterval(stallTimerRef);\n return new Response(fallback, {\n status: undiciResponse.statusCode,\n headers: undiciResponse.headers as unknown as HeadersInit,\n });\n }\n undiciResponse.body.pipe(passThrough);\n\n // Wrap undici response as a standard Web Response for downstream compatibility\n const response = new Response(\n passThrough as unknown as BodyInit,\n {\n status: undiciResponse.statusCode,\n headers: undiciResponse.headers as unknown as HeadersInit,\n }\n );\n\n clearTimeout(timeout);\n return response;\n } catch (error) {\n clearTimeout(timeout);\n if (ttfbTimer) clearTimeout(ttfbTimer);\n if (stallTimerRef) clearInterval(stallTimerRef);\n\n // Network errors / timeouts — return a synthetic 502\n const message = ttfbTimedOut\n ? `Provider \"${provider.name}\" timed out waiting for first byte after ${ttfbTimeout}ms`\n : error instanceof DOMException && error.name === \"AbortError\"\n ? `Provider \"${provider.name}\" timed out after ${provider.timeout}ms`\n : `Provider \"${provider.name}\" connection failed: ${(error as Error).message}`;\n\n const body = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message },\n });\n return new Response(body, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(body).byteLength.toString(),\n },\n });\n } finally {\n removeAbortListener?.();\n }\n}\n\n/**\n * Race multiple providers simultaneously. Returns the first successful response.\n * Aborts all remaining requests once a winner is found.\n */\nasync function raceProviders(\n chain: RoutingEntry[],\n providers: Map<string, ProviderConfig>,\n ctx: RequestContext,\n incomingRequest: Request,\n onAttempt?: (provider: string, index: number) => void,\n logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void },\n chainOffset: number = 0,\n): Promise<Response> {\n const sharedController = new AbortController();\n\n const races = chain.map(async (entry, index): Promise<{ response: Response; index: number }> => {\n const provider = providers.get(entry.provider);\n if (!provider) {\n return { response: unknownProviderErr(entry.provider), index };\n }\n\n // Check circuit breaker\n let cbProbeId: number | undefined;\n if (provider._circuitBreaker) {\n const cb = provider._circuitBreaker.canProceed();\n if (!cb.allowed) {\n return { response: circuitBreakerErr(entry.provider), index };\n }\n cbProbeId = cb.probeId;\n }\n\n onAttempt?.(entry.provider, index);\n\n try {\n const response = await forwardRequest(provider, entry, ctx, incomingRequest, sharedController.signal, index + chainOffset);\n // Record for circuit breaker\n if (provider._circuitBreaker) {\n provider._circuitBreaker.recordResult(response.status, cbProbeId);\n }\n return { response, index };\n } catch {\n if (provider._circuitBreaker) {\n provider._circuitBreaker.recordResult(502, cbProbeId);\n }\n return { response: providerFailedErr(entry.provider), index };\n }\n });\n\n // Track completed promises to avoid double-processing\n const completed = new Set<Promise<{ response: Response; index: number }>>();\n const failures: { response: Response; index: number }[] = [];\n\n try {\n while (completed.size < races.length) {\n const pending = races.filter(r => !completed.has(r));\n if (pending.length === 0) break;\n\n const winner = await Promise.race(pending);\n completed.add(races[winner.index] ?? races[0]);\n\n if (winner.response.status >= 200 && winner.response.status < 300) {\n // Drain/cancel in-flight loser response bodies BEFORE aborting shared controller\n // to prevent leaked stream chunks from mid-write cancellation.\n for (const r of pending) {\n if (r !== races[winner.index]) {\n r.then(({ response }) => {\n if (response.body) {\n try { response.body.cancel(); } catch { /* already consumed */ }\n }\n }).catch(() => { /* aborted */ });\n }\n }\n sharedController.abort();\n // Cancel bodies of already-completed losing responses to free resources\n for (const f of failures) {\n try { f.response.body?.cancel(); } catch { /* ignore */ }\n }\n return winner.response;\n }\n\n // Non-retriable error — check for context window limit before propagating\n if (!isRetriable(winner.response.status)) {\n sharedController.abort();\n if ((winner.response.status === 400 || winner.response.status === 413) && winner.response.body) {\n try {\n const errBody = await winner.response.text();\n const handled = handleContextWindowError(winner.response.status, errBody);\n if (handled) return handled;\n // Not a context error — re-create response with buffered body\n return new Response(errBody, {\n status: winner.response.status,\n statusText: winner.response.statusText,\n headers: winner.response.headers,\n });\n } catch {\n return winner.response;\n }\n }\n return winner.response;\n }\n\n // Retriable but not success — record and continue waiting\n failures.push(winner);\n }\n\n // All providers returned retriable errors — return the first failure\n sharedController.abort();\n if (failures.length > 0) {\n return failures[0].response;\n }\n\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers in race failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n });\n } catch {\n sharedController.abort();\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers in race failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n });\n }\n}\n\n/**\n * Forward a request with optional adaptive hedging.\n * When latency variance is high, sends multiple copies and returns the fastest.\n */\nasync function hedgedForwardRequest(\n provider: ProviderConfig,\n entry: RoutingEntry,\n ctx: RequestContext,\n incomingRequest: Request,\n chainSignal: AbortSignal | undefined,\n index: number,\n logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void }\n): Promise<Response> {\n const count = computeHedgingCount(provider);\n\n if (count <= 1) {\n // No hedging — single request\n inFlightCounter.increment(provider.name);\n const start = Date.now();\n try {\n const r = await forwardRequest(provider, entry, ctx, incomingRequest, chainSignal, index);\n latencyTracker.record(provider.name, Date.now() - start);\n return r;\n } finally {\n inFlightCounter.decrement(provider.name);\n }\n }\n\n // Hedged path: send multiple copies, race for first success\n logger?.warn(\"Hedging request\", {\n requestId: ctx.requestId,\n provider: provider.name,\n count,\n cv: Math.round(latencyTracker.getCV(provider.name) * 100) / 100,\n inFlight: inFlightCounter.get(provider.name),\n maxConcurrent: provider.concurrentLimit,\n });\n\n const start = Date.now();\n const launched: Promise<Response>[] = [];\n const hedgeController = new AbortController();\n\n for (let h = 0; h < count; h++) {\n inFlightCounter.increment(provider.name);\n const hedgeSignal = chainSignal\n ? AbortSignal.any([chainSignal, hedgeController.signal])\n : hedgeController.signal;\n launched.push(\n forwardRequest(provider, entry, ctx, incomingRequest, hedgeSignal, index)\n .finally(() => inFlightCounter.decrement(provider.name))\n );\n }\n\n // Race: first successful response wins, cancel the rest\n // Wrap each promise so we can identify which one completed by index\n const wrapped = launched.map((p, i) =>\n p.then(response => ({ response, hedgeIndex: i }))\n );\n\n const completed = new Set<number>();\n const failures: Response[] = [];\n\n try {\n while (completed.size < wrapped.length) {\n const pending = wrapped.filter((_, i) => !completed.has(i));\n if (pending.length === 0) break;\n\n const winner = await Promise.race(pending);\n completed.add(winner.hedgeIndex);\n\n // Record each hedged copy's result for circuit breaker\n if (provider._circuitBreaker) {\n provider._circuitBreaker.recordResult(winner.response.status);\n }\n\n if (winner.response.status >= 200 && winner.response.status < 300) {\n latencyTracker.record(provider.name, Date.now() - start);\n // Abort all in-flight hedge copies — triggers onExternalAbort in each\n // which properly destroys their PassThrough streams and clears stall timers\n hedgeController.abort();\n // Cancel remaining — record 502 for each cancelled copy\n for (let i = 0; i < wrapped.length; i++) {\n if (!completed.has(i)) {\n if (provider._circuitBreaker) provider._circuitBreaker.recordResult(502);\n wrapped[i].then(r => { try { r.response.body?.cancel(); } catch {} });\n }\n }\n for (const f of failures) { try { f.body?.cancel(); } catch {} }\n return winner.response;\n }\n\n failures.push(winner.response);\n }\n\n // All hedged copies returned errors — cancel bodies, return first failure\n hedgeController.abort();\n for (const f of failures) { try { f.body?.cancel(); } catch {} }\n return failures[0] ?? new Response(\n JSON.stringify({ type: \"error\", error: { type: \"api_error\", message: `Provider \"${provider.name}\" all hedged requests failed` } }),\n { status: 502, headers: { \"content-type\": \"application/json\" } }\n );\n } catch {\n hedgeController.abort();\n for (const f of failures) { try { f.body?.cancel(); } catch {} }\n return failures[0] ?? new Response(\n JSON.stringify({ type: \"error\", error: { type: \"api_error\", message: `Provider \"${provider.name}\" hedging failed` } }),\n { status: 502, headers: { \"content-type\": \"application/json\" } }\n );\n }\n}\n\n/**\n * Try forwarding through a chain of providers.\n * Returns the first successful response, or 502 if all fail.\n */\nexport async function forwardWithFallback(\n providers: Map<string, ProviderConfig>,\n chain: RoutingEntry[],\n ctx: RequestContext,\n incomingRequest: Request,\n onAttempt?: (provider: string, index: number) => void,\n logger?: { warn: (msg: string, meta?: Record<string, unknown>) => void }\n): Promise<Response> {\n // Single provider — no racing needed\n if (chain.length <= 1) {\n const entry = chain[0];\n const provider = providers.get(entry.provider);\n\n if (!provider) {\n return unknownProviderErr(entry.provider);\n }\n\n if (provider._circuitBreaker) {\n const cb = provider._circuitBreaker.canProceed();\n if (!cb.allowed) {\n logger?.warn(\"Provider skipped by circuit breaker\", { requestId: ctx.requestId, provider: entry.provider });\n return circuitBreakerErr(entry.provider);\n }\n }\n\n onAttempt?.(entry.provider, 0);\n\n const response = await hedgedForwardRequest(provider, entry, ctx, incomingRequest, undefined, 0, logger);\n\n return response;\n }\n\n // Multiple providers — staggered race\n const sharedController = new AbortController();\n const completed = new Set<number>();\n const failures: { response: Response; index: number }[] = [];\n\n async function attemptProvider(\n index: number,\n ): Promise<{ response: Response; index: number }> {\n const entry = chain[index];\n const provider = providers.get(entry.provider);\n\n if (!provider) {\n return { response: unknownProviderErr(entry.provider), index };\n }\n\n let cbProbeId: number | undefined;\n if (provider._circuitBreaker) {\n const cb = provider._circuitBreaker.canProceed();\n if (!cb.allowed) {\n logger?.warn(\"Provider skipped by circuit breaker\", {\n requestId: ctx.requestId,\n provider: entry.provider,\n });\n return { response: circuitBreakerErr(entry.provider), index };\n }\n cbProbeId = cb.probeId;\n }\n\n onAttempt?.(entry.provider, index);\n\n try {\n const response = await hedgedForwardRequest(\n provider,\n entry,\n ctx,\n incomingRequest,\n sharedController.signal,\n index,\n logger,\n );\n return { response, index };\n } catch {\n if (provider._circuitBreaker) provider._circuitBreaker.recordResult(502, cbProbeId);\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: `Provider \"${entry.provider}\" failed` },\n });\n return {\n response: new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n }),\n index,\n };\n }\n }\n\n // Build staggered race promises:\n // Provider 0 starts immediately\n // Provider 1+ start after SPECULATIVE_DELAY (if race not already won)\n const races: Promise<{ response: Response; index: number }>[] = [];\n\n for (let i = 0; i < chain.length; i++) {\n if (i === 0) {\n races.push(attemptProvider(0));\n } else {\n races.push(\n new Promise<{ response: Response; index: number }>((resolve) => {\n setTimeout(() => {\n if (sharedController.signal.aborted) {\n // Race already won — resolve with a cancelled placeholder\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"api_error\", message: \"Cancelled by race winner\" },\n });\n resolve({\n response: new Response(errBody, {\n status: 502,\n headers: { \"content-type\": \"application/json\" },\n }),\n index: i,\n });\n return;\n }\n attemptProvider(i).then(resolve);\n }, SPECULATIVE_DELAY);\n }),\n );\n }\n }\n\n // Pick winner — same logic as raceProviders\n try {\n while (completed.size < races.length) {\n const pending = races.filter((_, idx) => !completed.has(idx));\n if (pending.length === 0) break;\n\n const winner = await Promise.race(pending);\n completed.add(winner.index);\n\n if (winner.response.status >= 200 && winner.response.status < 300) {\n sharedController.abort();\n for (const f of failures) {\n try {\n f.response.body?.cancel();\n } catch {\n /* ignore */\n }\n }\n return winner.response;\n }\n\n if (!isRetriable(winner.response.status)) {\n sharedController.abort();\n if ((winner.response.status === 400 || winner.response.status === 413) && winner.response.body) {\n try {\n const errBody = await winner.response.text();\n const handled = handleContextWindowError(winner.response.status, errBody);\n if (handled) return handled;\n return new Response(errBody, {\n status: winner.response.status,\n statusText: winner.response.statusText,\n headers: winner.response.headers,\n });\n } catch {\n return winner.response;\n }\n }\n return winner.response;\n }\n\n failures.push(winner);\n }\n\n sharedController.abort();\n if (failures.length > 0) return failures[0].response;\n\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n } catch {\n sharedController.abort();\n const errBody = JSON.stringify({\n type: \"error\",\n error: { type: \"overloaded_error\", message: \"All providers failed\" },\n });\n return new Response(errBody, {\n status: 502,\n headers: {\n \"content-type\": \"application/json\",\n \"content-length\": textEncoder.encode(errBody).byteLength.toString(),\n },\n });\n }\n}\n","/**\n * Adaptive request hedging — sends multiple copies of a request when\n * the provider shows high latency variance, returning the fastest response.\n */\n\nimport type { ProviderConfig } from './types.js';\n\ninterface LatencySample {\n ttfbMs: number;\n timestamp: number;\n}\n\nexport class LatencyTracker {\n private samples = new Map<string, LatencySample[]>();\n private readonly maxSize: number;\n private readonly MAX_PROVIDERS = 50;\n\n constructor(maxSize = 20) {\n this.maxSize = maxSize;\n }\n\n record(provider: string, ttfbMs: number): void {\n // Cap total tracked providers to prevent unbounded growth\n if (this.samples.size >= this.MAX_PROVIDERS && !this.samples.has(provider)) {\n // Remove the first (oldest) provider key\n const firstKey = this.samples.keys().next().value;\n if (firstKey !== undefined) this.samples.delete(firstKey);\n }\n\n let window = this.samples.get(provider);\n if (!window) {\n window = [];\n this.samples.set(provider, window);\n }\n window.push({ ttfbMs, timestamp: Date.now() });\n if (window.length > this.maxSize) {\n window.splice(0, window.length - this.maxSize);\n }\n }\n\n /** Coefficient of variation (stddev / mean). Returns 0 if insufficient data. */\n getCV(provider: string): number {\n const window = this.samples.get(provider);\n if (!window || window.length < 3) return 0;\n const values = window.map(s => s.ttfbMs);\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n if (mean === 0) return 0;\n const variance = values.reduce((sum, v) => sum + (v - mean) ** 2, 0) / values.length;\n return Math.sqrt(variance) / mean;\n }\n\n getStats(provider: string): { count: number; mean: number; cv: number } {\n const window = this.samples.get(provider);\n if (!window || window.length === 0) return { count: 0, mean: 0, cv: 0 };\n const values = window.map(s => s.ttfbMs);\n const mean = values.reduce((a, b) => a + b, 0) / values.length;\n return { count: values.length, mean: Math.round(mean), cv: Math.round(this.getCV(provider) * 100) / 100 };\n }\n\n clear(provider: string): void {\n this.samples.delete(provider);\n }\n\n /** Remove entries for providers no longer in the current config. */\n prune(activeProviders: string[]): void {\n const active = new Set(activeProviders);\n for (const key of this.samples.keys()) {\n if (!active.has(key)) {\n this.samples.delete(key);\n }\n }\n }\n}\n\nexport class InFlightCounter {\n private counts = new Map<string, number>();\n\n increment(provider: string): number {\n const count = (this.counts.get(provider) ?? 0) + 1;\n this.counts.set(provider, count);\n return count;\n }\n\n decrement(provider: string): number {\n const count = Math.max(0, (this.counts.get(provider) ?? 0) - 1);\n this.counts.set(provider, count);\n return count;\n }\n\n get(provider: string): number {\n return this.counts.get(provider) ?? 0;\n }\n}\n\nexport const latencyTracker = new LatencyTracker();\nexport const inFlightCounter = new InFlightCounter();\n\n/**\n * Compute adaptive hedging count based on latency variance and available concurrency.\n *\n * CV (coefficient of variation) drives the count:\n * CV 0.0 → 1 (no hedging, provider is consistent)\n * CV 0.5 → 2\n * CV 1.0 → 3\n * CV 1.5+ → 4\n *\n * Clamped by available concurrency slots: maxConcurrent - inFlight.\n */\nexport function computeHedgingCount(provider: ProviderConfig): number {\n const cv = latencyTracker.getCV(provider.name);\n const inFlight = inFlightCounter.get(provider.name);\n const maxConcurrent = provider.concurrentLimit ?? 1;\n const available = Math.max(1, maxConcurrent - inFlight);\n\n const adaptive = Math.max(1, Math.floor(cv * 2 + 0.5));\n return Math.min(adaptive, available);\n}\n","// src/ws.ts\nimport { WebSocketServer } from \"ws\";\nimport type { Server } from \"node:http\";\nimport type { MetricsStore } from \"./metrics.js\";\nimport type { RequestMetrics, MetricsSummary, StreamEvent } from \"./types.js\";\n\ninterface WsMessage {\n type: \"request\" | \"summary\";\n data: RequestMetrics | MetricsSummary;\n}\n\nconst PING_INTERVAL_MS = 30_000; // 30 seconds\nconst MAX_MISSED_PONGS = 2;\nconst BACKPRESSURE_THRESHOLD = 64 * 1024; // 64KB\nconst SUMMARY_DEBOUNCE_MS = 500;\nconst STREAM_WS_THROTTLE_MS = 500; // caps stream event delivery to ~2 Hz per client\nconst BACKPRESSURE_LOG_INTERVAL_MS = 10_000; // throttle backpressure warnings to once per 10s\nconst clientStreamThrottle = new WeakMap<any, number>();\n\ninterface PendingDrain {\n timer: ReturnType<typeof setTimeout>;\n queue: string[];\n}\nconst pendingDrains = new WeakMap<any, PendingDrain>();\n\nlet wssInstance: InstanceType<typeof import(\"ws\").WebSocketServer> | null = null;\n\n// Module-level counters for dropped events (useful for monitoring)\nlet streamDroppedCount = 0;\nlet lastBackpressureWarnTime = 0;\n\nfunction maybeLogBackpressure(source: string): void {\n const now = Date.now();\n if (now - lastBackpressureWarnTime >= BACKPRESSURE_LOG_INTERVAL_MS) {\n console.warn(`[ws] Backpressure: dropping ${source} events (total dropped stream events: ${streamDroppedCount})`);\n lastBackpressureWarnTime = now;\n }\n}\n\nexport function attachWebSocket(server: Server, metricsStore: MetricsStore): void {\n const wss = new WebSocketServer({ server, path: \"/ws\" });\n wssInstance = wss;\n\n wss.on(\"connection\", (ws) => {\n // Send current summary as initial state\n const summary = metricsStore.getSummary();\n const initialMsg: WsMessage = { type: \"summary\", data: summary };\n ws.send(JSON.stringify(initialMsg));\n\n let pendingSummaryTimer: ReturnType<typeof setTimeout> | undefined;\n let missedPongs = 0;\n const alive = () => ws.readyState === ws.OPEN;\n\n // Subscribe to new metrics with backpressure check and debounced summary\n const unsubscribe = metricsStore.onRecord((metrics: RequestMetrics) => {\n if (!alive()) return;\n\n // Backpressure: skip send if outbound buffer is too large\n if (ws.bufferedAmount > BACKPRESSURE_THRESHOLD) {\n // Schedule a summary update instead so the client eventually catches up\n scheduleSummaryUpdate();\n maybeLogBackpressure(\"metrics\");\n return;\n }\n\n // Defer JSON.stringify + send off the critical path\n setImmediate(() => {\n if (!alive()) return;\n const msg: WsMessage = { type: \"request\", data: metrics };\n ws.send(JSON.stringify(msg));\n });\n\n scheduleSummaryUpdate();\n });\n\n function scheduleSummaryUpdate(): void {\n if (pendingSummaryTimer) return; // already scheduled\n pendingSummaryTimer = setTimeout(() => {\n pendingSummaryTimer = undefined;\n if (!alive()) return;\n const msg: WsMessage = { type: \"summary\", data: metricsStore.getSummary() };\n ws.send(JSON.stringify(msg));\n }, SUMMARY_DEBOUNCE_MS);\n }\n\n // Ping/pong heartbeat for liveness tracking\n const pingTimer = setInterval(() => {\n if (!alive()) {\n clearInterval(pingTimer);\n return;\n }\n // Terminate if client missed too many pongs\n if (missedPongs >= MAX_MISSED_PONGS) {\n cleanup(); // ensure timers and subscriber are cleaned up\n ws.terminate();\n return;\n }\n ws.ping();\n missedPongs++;\n }, PING_INTERVAL_MS);\n\n ws.on(\"pong\", () => {\n missedPongs = 0; // reset on successful pong\n });\n\n let cleanedUp = false;\n const cleanup = () => {\n if (cleanedUp) return;\n cleanedUp = true;\n clearInterval(pingTimer);\n if (pendingSummaryTimer) clearTimeout(pendingSummaryTimer);\n const pending = pendingDrains.get(ws);\n if (pending) {\n clearTimeout(pending.timer);\n pendingDrains.delete(ws);\n }\n unsubscribe();\n };\n\n ws.on(\"close\", cleanup);\n ws.on(\"error\", cleanup);\n });\n}\n\nexport function broadcastStreamEvent(data: StreamEvent): void {\n if (!wssInstance) return;\n const isStreaming = data.state === \"streaming\";\n const isCritical = data.state === \"complete\" || data.state === \"error\";\n const now = Date.now();\n\n let serializedMsg: string | undefined;\n for (const client of wssInstance.clients) {\n if (client.readyState !== client.OPEN) continue;\n\n // Throttle streaming events per client (non-streaming events always pass)\n if (isStreaming) {\n const lastEmit = clientStreamThrottle.get(client) ?? 0;\n if (now - lastEmit < STREAM_WS_THROTTLE_MS) continue;\n clientStreamThrottle.set(client, now);\n }\n\n // Lazy serialization — only when we know at least one client needs it\n if (!serializedMsg) serializedMsg = JSON.stringify({ type: \"stream\", data });\n const msg = serializedMsg;\n\n // Backpressure handling\n if (client.bufferedAmount > BACKPRESSURE_THRESHOLD) {\n if (isCritical) {\n let pending = pendingDrains.get(client);\n if (pending) {\n pending.queue.push(msg);\n } else {\n const queue = [msg];\n const sendOnDrain = () => {\n pendingDrains.delete(client);\n if (client.readyState === client.OPEN) {\n for (const queuedMsg of queue) client.send(queuedMsg);\n }\n };\n const timer = setTimeout(() => {\n pendingDrains.delete(client);\n client.removeListener('drain', sendOnDrain);\n if (client.readyState === client.OPEN) {\n for (const queuedMsg of queue) client.send(queuedMsg);\n }\n }, 5_000).unref();\n pendingDrains.set(client, { timer, queue });\n client.once('drain', sendOnDrain);\n }\n continue;\n }\n // Non-critical streaming event: drop and count\n streamDroppedCount++;\n maybeLogBackpressure(\"stream\");\n continue;\n }\n\n setImmediate(() => {\n if (client.readyState === client.OPEN) {\n client.send(msg);\n }\n });\n }\n}\n","// src/metrics.ts\nimport type { RequestMetrics, MetricsSummary } from \"./types.js\";\n\ntype Subscriber = (metrics: RequestMetrics) => void;\n\nconst WS_RECENT_REQUESTS_CAP = 50;\n\ninterface ModelEntry {\n actualModel?: string;\n count: number;\n lastSeen: number;\n}\n\nexport class MetricsStore {\n private static readonly MAX_MAP_SIZE = 200;\n\n private buffer: (RequestMetrics | null)[];\n private maxSize: number;\n private head = 0;\n private count = 0;\n private _lifetimeRequests = 0;\n private subscribers: Set<Subscriber>;\n private createdAt: number;\n\n // Running counters — updated incrementally in recordRequest()\n private _totalInputTokens = 0;\n private _totalOutputTokens = 0;\n private _totalTokensPerSec = 0;\n private _totalCacheReadTokens = 0;\n private _totalCacheCreationTokens = 0;\n private _modelMap = new Map<string, ModelEntry>();\n private _providerMap = new Map<string, number>();\n\n constructor(maxSize: number = 1000) {\n this.buffer = new Array(maxSize).fill(null);\n this.maxSize = maxSize;\n this.subscribers = new Set();\n this.createdAt = Date.now();\n }\n\n /** Evict the entry with the lowest count when the map exceeds MAX_MAP_SIZE. */\n private pruneMap<V>(map: Map<string, V>, getCount: (v: V) => number): void {\n if (map.size <= MetricsStore.MAX_MAP_SIZE) return;\n let minKey = '';\n let minVal = Infinity;\n for (const [k, v] of map) {\n const val = getCount(v);\n if (val < minVal) {\n minVal = val;\n minKey = k;\n }\n }\n if (minKey) map.delete(minKey);\n }\n\n recordRequest(metrics: RequestMetrics): void {\n const index = this.head % this.maxSize;\n const evicted = this.count >= this.maxSize ? this.buffer[index] : null;\n\n // Decrement counters for evicted entry\n if (evicted !== null) {\n this._totalInputTokens -= evicted.inputTokens ?? 0;\n this._totalOutputTokens -= evicted.outputTokens ?? 0;\n this._totalTokensPerSec -= evicted.tokensPerSec ?? 0;\n this._totalCacheReadTokens -= evicted.cacheReadTokens ?? 0;\n this._totalCacheCreationTokens -= evicted.cacheCreationTokens ?? 0;\n\n const mKey = evicted.model;\n const mEntry = this._modelMap.get(mKey);\n if (mEntry) {\n mEntry.count--;\n if (mEntry.count <= 0) this._modelMap.delete(mKey);\n }\n\n const pKey = evicted.targetProvider ?? evicted.provider;\n const pCount = this._providerMap.get(pKey) ?? 0;\n if (pCount <= 1) this._providerMap.delete(pKey);\n else this._providerMap.set(pKey, pCount - 1);\n }\n\n // Increment counters for new entry\n this._totalInputTokens += metrics.inputTokens ?? 0;\n this._totalOutputTokens += metrics.outputTokens ?? 0;\n this._totalTokensPerSec += metrics.tokensPerSec ?? 0;\n this._totalCacheReadTokens += metrics.cacheReadTokens ?? 0;\n this._totalCacheCreationTokens += metrics.cacheCreationTokens ?? 0;\n\n const mKey = metrics.model;\n const existing = this._modelMap.get(mKey);\n if (existing) {\n existing.count++;\n if (metrics.timestamp > existing.lastSeen) existing.lastSeen = metrics.timestamp;\n // Update actualModel to latest seen for the grouped model\n existing.actualModel = metrics.actualModel;\n } else {\n this._modelMap.set(mKey, { actualModel: metrics.actualModel, count: 1, lastSeen: metrics.timestamp });\n }\n\n const pKey = metrics.targetProvider ?? metrics.provider;\n this._providerMap.set(pKey, (this._providerMap.get(pKey) ?? 0) + 1);\n\n // Enforce size caps on maps\n this.pruneMap(this._modelMap, (e) => e.count);\n this.pruneMap(this._providerMap, (v) => v);\n\n // Ring buffer: overwrite oldest entry when full\n this.buffer[index] = metrics;\n this.head++;\n if (this.count < this.maxSize) this.count++;\n this._lifetimeRequests++;\n\n // Notify subscribers (catch errors to prevent breaking recording)\n for (const cb of this.subscribers) {\n try {\n cb(metrics);\n } catch {\n // Swallow subscriber errors — recording must not break\n }\n }\n }\n\n getSummary(): MetricsSummary {\n const requests = this.getRecentRequests();\n\n const activeModels = [...this._modelMap.entries()]\n .map(([model, { actualModel, count, lastSeen }]) => ({ model, actualModel, count, lastSeen }))\n .sort((a, b) => b.count - a.count);\n\n const providerDistribution = [...this._providerMap.entries()]\n .map(([provider, count]) => ({ provider, count }))\n .sort((a, b) => b.count - a.count);\n\n // Compute average cache hit rate across all requests with cache data\n let cacheHitRateSum = 0;\n let cacheHitRateCount = 0;\n for (const r of requests) {\n const totalInput = (r.inputTokens ?? 0) + (r.cacheReadTokens ?? 0) + (r.cacheCreationTokens ?? 0);\n if (totalInput > 0 && (r.cacheReadTokens ?? 0) > 0) {\n cacheHitRateSum += (r.cacheReadTokens! / totalInput) * 100;\n cacheHitRateCount++;\n }\n }\n\n // getRecentRequests() already caps at WS_RECENT_REQUESTS_CAP\n return {\n totalRequests: this._lifetimeRequests,\n totalInputTokens: this._totalInputTokens,\n totalOutputTokens: this._totalOutputTokens,\n avgTokensPerSec: this.count > 0 ? Math.round((this._totalTokensPerSec / this.count) * 10) / 10 : 0,\n totalCacheReadTokens: this._totalCacheReadTokens,\n totalCacheCreationTokens: this._totalCacheCreationTokens,\n avgCacheHitRate: cacheHitRateCount > 0 ? Math.round((cacheHitRateSum / cacheHitRateCount) * 10) / 10 : 0,\n activeModels,\n providerDistribution,\n recentRequests: requests,\n uptimeSeconds: Math.floor((Date.now() - this.createdAt) / 1000),\n };\n }\n\n onRecord(callback: Subscriber): () => void {\n this.subscribers.add(callback);\n return () => {\n this.subscribers.delete(callback);\n };\n }\n\n private getRecentRequests(): RequestMetrics[] {\n if (this.count === 0) return [];\n\n // Collect only the last WS_RECENT_REQUESTS_CAP entries in reverse (newest first)\n const cap = Math.min(this.count, WS_RECENT_REQUESTS_CAP);\n const result: RequestMetrics[] = [];\n // Start from the most recently written slot and walk backward\n for (let i = 0; i < cap; i++) {\n const index = ((this.head - 1 - i) % this.maxSize + this.maxSize) % this.maxSize;\n const entry = this.buffer[index];\n if (entry !== null) {\n result.push(entry);\n }\n }\n // Reverse to get chronological order (oldest first, newest last)\n result.reverse();\n return result;\n }\n}\n","// src/monitor.ts — Monitor mode: spawns daemon child, auto-restarts on crash\nimport { spawn } from \"node:child_process\";\nimport { existsSync, unlinkSync } from \"node:fs\";\nimport { dirname, join as pathJoin } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { writePidFile, removePidFile, removeWorkerPidFile, getPidPath } from \"./daemon.js\";\n\nexport async function startMonitor(args: {\n config?: string;\n port?: number;\n verbose: boolean;\n}): Promise<void> {\n // Monitor writes its own PID to modelweaver.pid\n // Clean up any stale PID file left by a previous run\n const pidPath = getPidPath();\n if (existsSync(pidPath)) {\n unlinkSync(pidPath);\n }\n await writePidFile(process.pid);\n\n const entryScript =\n process.argv[1] || pathJoin(dirname(fileURLToPath(import.meta.url)), \"index.js\");\n\n // Prevent monitor from crashing on unexpected errors\n process.on(\"uncaughtException\", (err) => {\n console.error(`[monitor] Uncaught exception: ${err.message}`);\n });\n process.on(\"unhandledRejection\", (reason) => {\n console.error(`[monitor] Unhandled rejection: ${reason}`);\n });\n\n const MAX_RESTART_ATTEMPTS = 10;\n const INITIAL_BACKOFF_MS = 1000;\n const MAX_BACKOFF_MS = 30000;\n const STABLE_RUN_MS = 60000;\n let restartCount = 0;\n let stableTimer: ReturnType<typeof setTimeout> | null = null;\n let restartTimer: ReturnType<typeof setTimeout> | null = null;\n let shuttingDown = false;\n let reloading = false;\n let child: ReturnType<typeof spawn> | null = null;\n\n async function spawnDaemon(): Promise<void> {\n const net = await import(\"node:net\");\n const { execFileSync } = await import(\"child_process\");\n const port = args.port ?? 3456;\n\n const checkPort = (): Promise<boolean> =>\n new Promise<boolean>((resolve) => {\n const server = net.createServer();\n server.once(\"error\", () => resolve(true));\n server.once(\"listening\", () => { server.close(() => resolve(false)); });\n server.listen(port);\n });\n\n if (await checkPort()) {\n // Port in use — find and kill the orphaned process\n console.warn(`[monitor] Port ${port} in use by orphaned process — cleaning up`);\n try {\n const lsofOutput = execFileSync(\"lsof\", [\"-ti\", `:${port}`], { encoding: \"utf8\" }).trim();\n if (lsofOutput) {\n const pids = lsofOutput.split(\"\\n\").map(Number).filter(Boolean);\n for (const pid of pids) {\n try {\n process.kill(pid, \"SIGTERM\");\n console.warn(`[monitor] Sent SIGTERM to orphaned process ${pid}`);\n } catch {\n // Process may have already exited\n }\n }\n // Wait for port to free up (max 5 seconds)\n for (let i = 0; i < 10; i++) {\n await new Promise((r) => setTimeout(r, 500));\n if (!(await checkPort())) {\n console.warn(`[monitor] Port ${port} freed — spawning new daemon`);\n break;\n }\n }\n // If port still in use after timeout, force kill\n if (await checkPort()) {\n for (const pid of pids) {\n try { process.kill(pid, \"SIGKILL\"); } catch { /* already dead */ }\n }\n await new Promise((r) => setTimeout(r, 500));\n }\n }\n } catch {\n // lsof not available or other error — try pkill as fallback\n try {\n execFileSync(\"pkill\", [\"-f\", \"modelweaver.*dist/index.*--daemon\"], { stdio: \"ignore\" });\n await new Promise((r) => setTimeout(r, 1000));\n } catch { /* pkill also failed */ }\n }\n\n // Final check — if port is still in use, give up\n if (await checkPort()) {\n console.error(`[monitor] Port ${port} still in use after cleanup — skipping worker spawn`);\n return;\n }\n }\n\n const childArgs: string[] = [entryScript, \"--daemon\"];\n if (args.config) childArgs.push(\"--config\", args.config);\n if (args.port) childArgs.push(\"--port\", String(args.port));\n if (args.verbose) childArgs.push(\"--verbose\");\n\n child = spawn(process.execPath, childArgs, {\n detached: true,\n stdio: \"ignore\",\n env: { ...process.env },\n });\n // NOTE: do NOT child.unref() here — the monitor must stay alive to watch the child\n\n // Start stability timer — if worker lives this long, reset restart counter\n if (stableTimer) clearTimeout(stableTimer);\n stableTimer = setTimeout(() => {\n if (restartCount > 0) {\n console.error(\n `[monitor] Worker stable for ${STABLE_RUN_MS}ms, resetting restart counter`,\n );\n }\n restartCount = 0;\n stableTimer = null;\n }, STABLE_RUN_MS);\n\n child.on(\"exit\", async (code) => {\n child = null;\n\n // Clear stability timer — worker died before becoming stable\n if (stableTimer) {\n clearTimeout(stableTimer);\n stableTimer = null;\n }\n\n await removeWorkerPidFile();\n if (code === 0 && !reloading) {\n // Clean shutdown — monitor exits too\n await removePidFile();\n process.exit(0);\n }\n reloading = false;\n\n // Don't restart if we're shutting down\n if (shuttingDown) {\n console.error(\"[monitor] Worker exited during shutdown, monitor exiting\");\n await removePidFile();\n process.exit(0);\n }\n\n // Crash — apply exponential backoff restart\n const attempt = restartCount;\n if (attempt >= MAX_RESTART_ATTEMPTS) {\n console.error(\n `[monitor] Max restart attempts exhausted (${MAX_RESTART_ATTEMPTS}), monitor exiting`,\n );\n await removePidFile();\n process.exit(1);\n }\n\n const backoff = Math.min(INITIAL_BACKOFF_MS * 2 ** attempt, MAX_BACKOFF_MS);\n restartCount++;\n console.error(\n `[monitor] Worker died (code ${code}), restarting in ${backoff}ms (attempt ${restartCount}/${MAX_RESTART_ATTEMPTS})`,\n );\n\n restartTimer = setTimeout(spawnDaemon, backoff);\n });\n }\n\n // SIGTERM from `stop` → kill child, then exit cleanly\n // Does NOT register a second `exit` listener on the child. Instead, relies on\n // the existing child exit handler (registered in spawnDaemon) which already\n // checks `shuttingDown` and performs cleanup + process.exit(0).\n process.on(\"SIGTERM\", () => {\n shuttingDown = true;\n if (restartTimer) {\n clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (stableTimer) {\n clearTimeout(stableTimer);\n stableTimer = null;\n }\n if (child) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n // Child exit handler will clean up pid files and call process.exit(0).\n // Safety: if child doesn't exit within 5 s, force exit.\n setTimeout(() => {\n console.error(\"[monitor] Child did not exit within 5 s, forcing exit\");\n process.exit(0);\n }, 5000);\n } else {\n // Child already dead — clean up and exit.\n removePidFile().then(() => process.exit(0));\n }\n });\n\n // SIGINT (Ctrl-C) — same pattern as SIGTERM.\n process.on(\"SIGINT\", () => {\n shuttingDown = true;\n if (restartTimer) {\n clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (stableTimer) {\n clearTimeout(stableTimer);\n stableTimer = null;\n }\n if (child) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n // Child exit handler will clean up pid files and call process.exit(0).\n // Safety: if child doesn't exit within 5 s, force exit.\n setTimeout(() => {\n console.error(\"[monitor] Child did not exit within 5 s, forcing exit\");\n process.exit(0);\n }, 5000);\n } else {\n // Child already dead — clean up and exit.\n removePidFile().then(() => process.exit(0));\n }\n });\n\n // SIGHUP from `reload` → gracefully kill current worker so monitor restarts it\n // Note: SIGHUP is POSIX-only; this handler is a no-op on Windows.\n process.on(\"SIGHUP\", () => {\n console.log(\"[monitor] Received reload signal, restarting worker...\");\n reloading = true;\n if (restartTimer) {\n clearTimeout(restartTimer);\n restartTimer = null;\n }\n if (child) {\n try {\n child.kill(\"SIGTERM\");\n } catch {\n /* already dead */\n }\n }\n // Reset restart count — this is an intentional restart, not a crash\n restartCount = 0;\n });\n\n spawnDaemon();\n}\n"],"mappings":"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","names":["createAdaptorServer","readFileSync","Hono","routingCache","clearRoutingCache","matchTier","modelName","tierPatterns","tier","patterns","pattern","buildRoutingChain","routing","resolveRequest","model","requestId","config","rawBody","cached","providerChain","modelChain","matchedTier","oldestKey","undiciRequest","PassThrough","fs","path","os","LatencyTracker","maxSize","provider","ttfbMs","firstKey","window","values","mean","a","b","variance","sum","v","s","activeProviders","active","key","InFlightCounter","count","latencyTracker","inFlightCounter","computeHedgingCount","cv","inFlight","maxConcurrent","available","adaptive","WebSocketServer","PING_INTERVAL_MS","MAX_MISSED_PONGS","BACKPRESSURE_THRESHOLD","SUMMARY_DEBOUNCE_MS","STREAM_WS_THROTTLE_MS","BACKPRESSURE_LOG_INTERVAL_MS","clientStreamThrottle","pendingDrains","wssInstance","streamDroppedCount","lastBackpressureWarnTime","maybeLogBackpressure","source","now","attachWebSocket","server","metricsStore","wss","ws","initialMsg","pendingSummaryTimer","missedPongs","alive","unsubscribe","metrics","scheduleSummaryUpdate","msg","pingTimer","cleanup","cleanedUp","pending","broadcastStreamEvent","data","isStreaming","isCritical","serializedMsg","client","lastEmit","queue","sendOnDrain","queuedMsg","timer","shallowCloneForMutation","parsed","clone","FORWARD_HEADERS","MULTI_SLASH","STRIP_ORIGIN","MODEL_KEY_REGEX","MAX_TOKENS_REGEX","textEncoder","ERR_HEADERS","unknownProviderErr","providerName","body","circuitBreakerErr","SPECULATIVE_DELAY","isRetriable","status","CONTEXT_WINDOW_PATTERNS","isContextWindowError","body","lower","p","handleContextWindowError","flagDir","path","os","fs","enhanced","buildOutboundUrl","baseUrlOrProvider","incomingPath","baseUrl","cachedOrigin","cachedPathname","basePath","origin","slashIndex","incomingQuery","incomingOnly","qIndex","resolvedPath","MULTI_SLASH","buildOutboundHeaders","incomingHeaders","provider","requestId","headers","name","FORWARD_HEADERS","value","cachedHost","url","cleanOrphanedToolMessages","messages","hasToolBlocks","i","msg","block","allToolUseIds","allToolResultIds","hasOrphans","id","filteredCount","applyTargetedReplacements","rawBody","entry","parsed","needsOrphanClean","mutable","shallowCloneForMutation","maxOutputTokens","requested","needsModel","needsMaxTokensClamp","needsMaxTokensAdd","m","MAX_TOKENS_REGEX","patterns","MODEL_KEY_REGEX","combinedRegex","modelRepl","tokensRepl","origModel","modelLogged","match","forwardRequest","ctx","incomingRequest","externalSignal","chainIndex","outgoingPath","STRIP_ORIGIN","needsModification","requestedMaxTokens","originalModel","controller","timeout","ttfbTimeout","ttfbTimedOut","ttfbTimer","ttfbPromise","_","reject","removeAbortListener","upstreamBody","passThrough","stallTimerRef","errBody","textEncoder","prevMax","onExternalAbort","undiciResponse","undiciRequest","stallTimeout","PassThrough","stallMsg","lastDataTime","handleStall","broadcastStreamEvent","fallback","response","error","message","hedgedForwardRequest","provider","entry","ctx","incomingRequest","chainSignal","index","logger","count","computeHedgingCount","inFlightCounter","start","r","forwardRequest","latencyTracker","launched","hedgeController","h","hedgeSignal","wrapped","p","i","response","completed","failures","pending","_","winner","f","forwardWithFallback","providers","chain","onAttempt","circuitBreakerErr","unknownProviderErr","sharedController","attemptProvider","cbProbeId","cb","errBody","textEncoder","races","resolve","SPECULATIVE_DELAY","idx","isRetriable","handled","handleContextWindowError","randomUUID","gzip","promisify","gzipAsync","promisify","gzip","MODEL_CONTEXT_WINDOWS","getContextWindow","model","key","size","computeCacheHitRate","cacheRead","cacheCreation","input","totalInput","computeContextPercent","output","contextWindow","total","anthropicError","type","message","requestId","parseUsageFromData","data","usage","inp","out","createMetricsTransform","ctx","provider","targetProvider","metricsStore","status","contentType","td","tokens","lineBuf","eventBuf","WINDOW_SIZE","inputTokens","cacheReadTokens","cacheCreationTokens","outputTokens","windowBuf","isSSE","STREAM_THROTTLE_MS","lastStreamEmit","firstChunk","responsePreview","PREVIEW_MAX","drainEvents","eventText","event","dataLine","l","hasUsage","hasDelta","hasChoices","delta","choices","choiceDelta","scanWindow","text","TOKEN_RE","m","val","field","anthContent","lastText","recordMetrics","latencyMs","latencySec","tps","broadcastStreamEvent","processChunk","decoded","isFinal","lines","line","now","nowJson","chunk","controller","agentKey","origin","createApp","initConfig","logLevel","config","logger","createLogger","app","Hono","err","c","next","randomUUID","body","rawBody","resolveRequest","configuredModels","e","successfulProvider","response","forwardWithFallback","index","headerSize","k","errMsg","responseBody","transform","newHeaders","finalResponse","latency","json","compressed","name","breaker","s","newConfig","oldAgents","reusedKeys","existingAgent","closePromises","agent","clearRoutingCache","MetricsStore","_MetricsStore","maxSize","map","getCount","minKey","minVal","k","v","val","metrics","index","evicted","mKey","mEntry","pKey","pCount","existing","e","cb","requests","activeModels","model","actualModel","count","lastSeen","a","b","providerDistribution","provider","cacheHitRateSum","cacheHitRateCount","r","totalInput","callback","cap","result","i","entry","spawn","existsSync","unlinkSync","dirname","pathJoin","fileURLToPath","startMonitor","args","pidPath","getPidPath","existsSync","unlinkSync","writePidFile","entryScript","pathJoin","dirname","fileURLToPath","err","reason","MAX_RESTART_ATTEMPTS","INITIAL_BACKOFF_MS","MAX_BACKOFF_MS","STABLE_RUN_MS","restartCount","stableTimer","restartTimer","shuttingDown","reloading","child","spawnDaemon","net","execFileSync","port","checkPort","resolve","server","lsofOutput","pids","pid","i","r","childArgs","spawn","code","removeWorkerPidFile","removePidFile","attempt","backoff","VERSION","readFileSync","parseArgs","argv","args","i","portStr","configPath","printHelp","main","dotenv","existsSync","join","home","paths","p","quick","runInit","startDaemon","result","stopDaemon","statusDaemon","getService","installed","err","removeDaemon","launchGui","reloadDaemon","config","loadConfig","error","port","host","logLevel","metricsStore","MetricsStore","startMonitor","removeWorkerPidFile","writeWorkerPidFile","createDebouncedReload","getLogPath","reloadConfig","createWriteStream","watch","createLogger","logger","reason","logStream","handle","createApp","configWatcher","debounced","newConfig","latencyTracker","server","createAdaptorServer","attachWebSocket","shutdown","tier","entries","providerList","e","model"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kianwoon/modelweaver",
3
- "version": "0.3.19",
3
+ "version": "0.3.20",
4
4
  "description": "Multi-provider model orchestration proxy for Claude Code",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",