@bivelio/savings-layer 0.4.21 → 0.4.22

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.
@@ -1,36 +1,37 @@
1
1
  #!/usr/bin/env node
2
- import {spawn}from'child_process';import {existsSync,readFileSync,mkdirSync,writeFileSync}from'fs';import {homedir}from'os';import {join,dirname,delimiter}from'path';import {fileURLToPath}from'url';import {randomUUID,createHash}from'crypto';import {createServer}from'http';import*as z from'zlib';import {connect}from'net';var ve="context-management-2025-06-27";function Be(){return {edits:[{type:"clear_tool_uses_20250919",trigger:{type:"input_tokens",value:5e4},keep:{type:"tool_uses",value:8},clear_at_least:{type:"input_tokens",value:5e3}}]}}function Et(t,e){if(e!==void 0&&e!==""&&e!=="identity")return null;try{let n=JSON.parse(t.toString("utf8"));if(n===null||typeof n!="object"||Array.isArray(n)||typeof n.model!="string"||!Array.isArray(
3
- n.messages))return null;let o=$n(n.context_management),r=jn(n.context_management);return n.context_management=Nn(n.context_management),{cuerpo:Buffer.from(JSON.stringify(n),"utf8"),nuestros:o,triggerDelCliente:r}}catch{
2
+ import {spawn}from'child_process';import {existsSync,readFileSync,mkdirSync,writeFileSync}from'fs';import {homedir}from'os';import {join,dirname,delimiter}from'path';import {fileURLToPath}from'url';import {randomUUID,createHash}from'crypto';import {createServer}from'http';import*as B from'zlib';import {connect}from'net';var ve="context-management-2025-06-27";function ze(){return {edits:[{type:"clear_tool_uses_20250919",trigger:{type:"input_tokens",value:5e4},keep:{type:"tool_uses",value:8},clear_at_least:{type:"input_tokens",value:5e3}}]}}function St(t,e){if(e!==void 0&&e!==""&&e!=="identity")return null;try{let n=JSON.parse(t.toString("utf8"));if(n===null||typeof n!="object"||Array.isArray(n)||typeof n.model!="string"||!Array.isArray(
3
+ n.messages))return null;let o=$n(n.context_management),r=Nn(n.context_management);return n.context_management=jn(n.context_management),{cuerpo:Buffer.from(JSON.stringify(n),"utf8"),nuestros:o,triggerDelCliente:r}}catch{
4
4
  return null}}function $n(t){let e=t!==null&&typeof t=="object"&&!Array.isArray(t)?t.edits:void 0,n=(Array.isArray(e)?e:[]).filter(o=>o!==null&&typeof o=="object"&&!Array.isArray(o)&&o.type===Ue);return xt(
5
- n)!==xt([Be().edits[0]])}function jn(t){let e=t!==null&&typeof t=="object"&&!Array.isArray(t)?t.edits:void 0,n=(Array.isArray(e)?e:[]).filter(r=>r!==null&&typeof r=="object"&&!Array.isArray(r)&&r.type===
6
- Ue);if(n.length===0)return null;let o=Number.POSITIVE_INFINITY;for(let r of n){let s=r.trigger,a=s!==null&&typeof s=="object"&&s.type==="input_tokens"&&typeof s.value=="number"&&Number.isFinite(s.value)&&
7
- s.value>0?s.value:1e5;a<o&&(o=a);}return o}function xt(t){return JSON.stringify(t,(e,n)=>n!==null&&typeof n=="object"&&!Array.isArray(n)?Object.fromEntries(Object.keys(n).sort().map(o=>[o,n[o]])):n)}var Ue="\
8
- clear_tool_uses_20250919",St="clear_thinking_20251015";function Nn(t){let e=Be().edits[0],n=t!==null&&typeof t=="object"&&!Array.isArray(t)?{...t}:{},o=(Array.isArray(n.edits)?n.edits:[]).filter(a=>a!==
9
- null&&typeof a=="object"&&!Array.isArray(a)&&a.type!==Ue),r=o.filter(a=>a.type===St),s=o.filter(a=>a.type!==St);return {...n,edits:[...r,...s,e]}}function wt(t,e,n=false){let o=" (rebaja de TTL activa si \
10
- la licencia trae cache_ttl_tuning; fail-closed)",r=n?" \xB7 DIALES DE ELISI\xD3N ARMADOS A MANO (BVSALA_GATEWAY_DIALES_ELISION=1): miden p\xE9rdida, R=4,05 vs R*=0,0127":" \xB7 diales de elisi\xF3n APAGADOS";
11
- return t==="0"?{linea:"canal S4a: CERRADO (BVSALA_GATEWAY_ELISION=0) \u2014 sin rebaja de TTL y sin diales",aviso:false}:e?{linea:(t==="1"?"canal S4a: ABIERTO (BVSALA_GATEWAY_ELISION=1)":"canal S4a: ABIE\
12
- RTO por defecto (=0 lo cierra)")+o+r,aviso:n}:{linea:"canal S4a: NO SE ABRE \u2014 falta BIVELIO_SERVICE_KEY (sin clave no hay org que consultar); ni rebaja de TTL ni diales"+(t==="1"?". Abre un termi\
13
- nal nuevo o exporta la clave y relanza":""),aviso:t==="1"}}function At(t){return t===void 0||t.trim()===""?ve:t.split(",").map(n=>n.trim()).filter(n=>n.length>0).includes(ve)?t:`${t},${ve}`}function N(t){let e=t.toLowerCase();return e.startsWith("gpt-")||e.startsWith("o1")||e.startsWith("o3")||e.startsWith("o4")?/^gpt-5\.6/.test(e)?1.25:1:e.startsWith("gemini")?1:1.25}function Ve(t){let e=t.
14
- toLowerCase();return e.startsWith("claude")||e.startsWith("anthropic/")?2:N(t)}var Bn=.3,Un=4096,Vn=(t,e)=>t==="cache_control"?void 0:e;function Fe(t){return t===void 0?"":JSON.stringify(t,Vn)??""}function Fn(t){return Array.isArray(t)?t.filter(e=>!(e!==null&&typeof e=="object"&&
15
- e.defer_loading===true)):t}function He(t){return createHash("sha1").update(t).digest("base64url").slice(0,16)}function J(t,e){if(e!==void 0&&e!==""&&e!=="identity")return null;try{let n=JSON.parse(t.toString("u\
16
- tf8"));if(n===null||typeof n!="object"||Array.isArray(n))return null;let o=n;return typeof o.model!="string"||!Array.isArray(o.messages)?null:o}catch{return null}}function Tt(t){let e=Fe(t.system),n=Fe(
17
- Fn(t.tools)),o=Array.isArray(t.messages)?t.messages.slice(0,Un):[],r=[],s=[];for(let a of o){let i=Fe(a);r.push(He(i)),s.push(Buffer.byteLength(i,"utf8"));}return {system:He(e),tools:He(n),mensajes:r,bytes:s,
5
+ n)!==xt([ze().edits[0]])}function Nn(t){let e=t!==null&&typeof t=="object"&&!Array.isArray(t)?t.edits:void 0,n=(Array.isArray(e)?e:[]).filter(r=>r!==null&&typeof r=="object"&&!Array.isArray(r)&&r.type===
6
+ Ue);if(n.length===0)return null;let o=Number.POSITIVE_INFINITY;for(let r of n){let s=r.trigger,i=s!==null&&typeof s=="object"&&s.type==="input_tokens"&&typeof s.value=="number"&&Number.isFinite(s.value)&&
7
+ s.value>0?s.value:1e5;i<o&&(o=i);}return o}function xt(t){return JSON.stringify(t,(e,n)=>n!==null&&typeof n=="object"&&!Array.isArray(n)?Object.fromEntries(Object.keys(n).sort().map(o=>[o,n[o]])):n)}var Ue="\
8
+ clear_tool_uses_20250919",wt="clear_thinking_20251015";function jn(t){let e=ze().edits[0],n=t!==null&&typeof t=="object"&&!Array.isArray(t)?{...t}:{},o=(Array.isArray(n.edits)?n.edits:[]).filter(i=>i!==
9
+ null&&typeof i=="object"&&!Array.isArray(i)&&i.type!==Ue),r=o.filter(i=>i.type===wt),s=o.filter(i=>i.type!==wt);return {...n,edits:[...r,...s,e]}}function Et(t,e,n=false){let o=" (TTL tuning ON if your li\
10
+ cence includes cache_ttl_tuning; fail-closed)",r=n?" \xB7 context elision dials FORCED ON BY HAND (BVSALA_GATEWAY_DIALES_ELISION=1): they measure as a net loss, R=4.05 vs R*=0.0127":" \xB7 context elisio\
11
+ n dials OFF";return t==="0"?{linea:"licence channel: CLOSED (BVSALA_GATEWAY_ELISION=0) \u2014 no TTL tuning and no context elision dials",aviso:false}:e?{linea:(t==="1"?"licence channel: OPEN (BVSALA_GAT\
12
+ EWAY_ELISION=1)":"licence channel: OPEN by default (=0 closes it)")+o+r,aviso:n}:{linea:"licence channel: NOT OPENED \u2014 BIVELIO_SERVICE_KEY is missing (without it there is no org whose licence to check\
13
+ ); no TTL tuning and no context elision dials"+(t==="1"?". Open a new terminal or export the key and relaunch":""),aviso:t==="1"}}function Tt(t){return t===void 0||t.trim()===""?ve:t.split(",").map(n=>n.
14
+ trim()).filter(n=>n.length>0).includes(ve)?t:`${t},${ve}`}function j(t){let e=t.toLowerCase();return e.startsWith("gpt-")||e.startsWith("o1")||e.startsWith("o3")||e.startsWith("o4")?/^gpt-5\.6/.test(e)?1.25:1:e.startsWith("gemini")?1:1.25}function Ve(t){let e=t.
15
+ toLowerCase();return e.startsWith("claude")||e.startsWith("anthropic/")?2:j(t)}var zn=.3,Un=4096,Vn=(t,e)=>t==="cache_control"?void 0:e;function Fe(t){return t===void 0?"":JSON.stringify(t,Vn)??""}function Fn(t){return Array.isArray(t)?t.filter(e=>!(e!==null&&typeof e=="object"&&
16
+ e.defer_loading===true)):t}function He(t){return createHash("sha1").update(t).digest("base64url").slice(0,16)}function W(t,e){if(e!==void 0&&e!==""&&e!=="identity")return null;try{let n=JSON.parse(t.toString("u\
17
+ tf8"));if(n===null||typeof n!="object"||Array.isArray(n))return null;let o=n;return typeof o.model!="string"||!Array.isArray(o.messages)?null:o}catch{return null}}function At(t){let e=Fe(t.system),n=Fe(
18
+ Fn(t.tools)),o=Array.isArray(t.messages)?t.messages.slice(0,Un):[],r=[],s=[];for(let i of o){let a=Fe(i);r.push(He(a)),s.push(Buffer.byteLength(a,"utf8"));}return {system:He(e),tools:He(n),mensajes:r,bytes:s,
18
19
  bytesFijos:Buffer.byteLength(e,"utf8")+Buffer.byteLength(n,"utf8")}}function _t(t,e){if(t===void 0||t.system!==e.system||t.tools!==e.tools)return 1;let n=e.bytesFijos;for(let s of e.bytes)n+=s;if(n<=0)
19
20
  return 1;let o=0;for(;o<t.mensajes.length&&o<e.mensajes.length&&t.mensajes[o]===e.mensajes[o];)o++;let r=0;for(let s=o;s<e.bytes.length;s++)r+=e.bytes[s]??0;return r/n}function Ct(t){let{fraccionNueva:e,
20
- umbral:n=Bn}=t;return e>=n}function qe(t,e){if(t===null||typeof t!="object")return;if(Array.isArray(t)){for(let r of t)qe(r,e);return}let n=t,o=n.cache_control;if(o!==null&&typeof o=="object"&&!Array.
21
+ umbral:n=zn}=t;return e>=n}function qe(t,e){if(t===null||typeof t!="object")return;if(Array.isArray(t)){for(let r of t)qe(r,e);return}let n=t,o=n.cache_control;if(o!==null&&typeof o=="object"&&!Array.
21
22
  isArray(o)){let r=o;r.ttl==="1h"?(r.ttl="5m",e.rebajadas++):(r.ttl==="5m"||r.ttl===void 0)&&e.yaEran5m++;}for(let[r,s]of Object.entries(n))r!=="cache_control"&&s!==null&&typeof s=="object"&&qe(s,e);}function Rt(t,e){
22
- let n=Buffer.isBuffer(t)?J(t,e):t;if(n===null)return null;try{let o={rebajadas:0,yaEran5m:0};for(let r of ["system","messages","tools"])qe(n[r],o);return o.rebajadas===0?null:{cuerpo:Buffer.from(JSON.stringify(
23
+ let n=Buffer.isBuffer(t)?W(t,e):t;if(n===null)return null;try{let o={rebajadas:0,yaEran5m:0};for(let r of ["system","messages","tools"])qe(n[r],o);return o.rebajadas===0?null:{cuerpo:Buffer.from(JSON.stringify(
23
24
  n),"utf8"),rebajadas:o.rebajadas,yaEran5m:o.yaEran5m}}catch{return null}}var ye=class{constructor(e=512,n=3600*1e3){this.maxEntradas=e;this.maxEdadMs=n;}maxEntradas;maxEdadMs;visto=new Map;ultima(e,n){let o=this.visto.get(e);if(o!==void 0){if(n-o.vistoMs>this.maxEdadMs){this.
24
25
  visto.delete(e);return}return o.huella}}anotar(e,n,o){for(this.visto.delete(e),this.visto.set(e,{huella:n,vistoMs:o});this.visto.size>this.maxEntradas;){let r=this.visto.keys().next();if(r.done===true)break;
25
- this.visto.delete(r.value);}}get tamano(){return this.visto.size}};var It="mid-conversation-output-config-2026-07-01",Je=["low","medium","high","xhigh","max"],Hn=new Set(["claude-fable-5-1","claude-mythos-5-1","claude-opus-5"]);function qn(t){return Hn.has(t)}function Ge(t){
26
- return t!==void 0&&Je.includes(t)}function Kn(t){return {role:"system",content:[],output_config:{effort:t}}}function Jn(t){if(t.output_config!==void 0&&t.output_config!==null)return true;let e=t.messages;
27
- return Array.isArray(e)?e.some(n=>typeof n=="object"&&n!==null&&n.output_config!==void 0):false}function Pt(t,e,n){let o=J(t,n);if(o===null||typeof o.model!="string"||!qn(o.model)||Jn(o))return null;let r=o.
28
- messages;return Array.isArray(r)?(o.messages=[...r,Kn(e)],{cuerpo:Buffer.from(JSON.stringify(o),"utf8"),nivel:e}):null}function Ot(t){let e=(t??"").split(",").map(n=>n.trim()).filter(Boolean);return e.
29
- includes(It)||e.push(It),e.join(",")}function Dt(t,e,n){return t===void 0||t===""?{linea:`esfuerzo por turno (D): APAGADO (BVSALA_GATEWAY_ESFUERZO=${Je.join("|")} lo arma)`,aviso:false}:Ge(t)?e?n?{linea:`\
30
- esfuerzo por turno (D): ARMADO a "${t}" (activo si la licencia trae per_turn_effort; fail-closed). Solo fable-5-1, mythos-5-1 y opus-5; el resto pasa intacto. NO reinicia la cach\xE9 de prefijo: el dial \
31
- se a\xF1ade al final, no reescribe nada anterior`,aviso:false}:{linea:"esfuerzo por turno (D): NO SE ARMA \u2014 el canal S4a est\xE1 CERRADO (BVSALA_GATEWAY_ELISION=0 apaga las palancas)",aviso:true}:{linea:"\
32
- esfuerzo por turno (D): NO SE ARMA \u2014 falta BIVELIO_SERVICE_KEY (sin clave no hay org que consultar). Abre un terminal nuevo o exporta la clave y relanza",aviso:true}:{linea:`esfuerzo por turno (D):\
33
- NO SE ARMA \u2014 "${t}" no es un nivel. Usa uno de: ${Je.join(", ")}`,aviso:true}}var xe={"gpt-4.1":{inputPer1k:.002,outputPer1k:.008,cachedInputPer1k:5e-4},"gpt-4.1-mini":{inputPer1k:4e-4,outputPer1k:.0016,cachedInputPer1k:1e-4},"gpt-4.1-nano":{inputPer1k:1e-4,outputPer1k:4e-4,cachedInputPer1k:25e-6},
26
+ this.visto.delete(r.value);}}get tamano(){return this.visto.size}};var It="mid-conversation-output-config-2026-07-01",We=["low","medium","high","xhigh","max"],Hn=new Set(["claude-fable-5-1","claude-mythos-5-1","claude-opus-5"]);function qn(t){return Hn.has(t)}function Je(t){
27
+ return t!==void 0&&We.includes(t)}function Kn(t){return {role:"system",content:[],output_config:{effort:t}}}function Wn(t){if(t.output_config!==void 0&&t.output_config!==null)return true;let e=t.messages;
28
+ return Array.isArray(e)?e.some(n=>typeof n=="object"&&n!==null&&n.output_config!==void 0):false}function Ot(t,e,n){let o=W(t,n);if(o===null||typeof o.model!="string"||!qn(o.model)||Wn(o))return null;let r=o.
29
+ messages;return Array.isArray(r)?(o.messages=[...r,Kn(e)],{cuerpo:Buffer.from(JSON.stringify(o),"utf8"),nivel:e}):null}function Pt(t){let e=(t??"").split(",").map(n=>n.trim()).filter(Boolean);return e.
30
+ includes(It)||e.push(It),e.join(",")}function Dt(t,e,n){return t===void 0||t===""?{linea:`per-turn effort: OFF (BVSALA_GATEWAY_ESFUERZO=${We.join("|")} turns it on)`,aviso:false}:Je(t)?e?n?{linea:`per-tu\
31
+ rn effort: ARMED at "${t}" (ON if your licence includes per_turn_effort; fail-closed). Only fable-5-1, mythos-5-1 and opus-5; every other model passes through unchanged. Does NOT reset the prefix cach\
32
+ e: the dial is appended at the end and nothing earlier is rewritten`,aviso:false}:{linea:"per-turn effort: NOT ARMED \u2014 the licence channel is CLOSED (BVSALA_GATEWAY_ELISION=0 turns off the levers)",
33
+ aviso:true}:{linea:"per-turn effort: NOT ARMED \u2014 BIVELIO_SERVICE_KEY is missing (without it there is no org whose licence to check). Open a new terminal or export the key and relaunch",aviso:true}:{linea:`\
34
+ per-turn effort: NOT ARMED \u2014 "${t}" is not a level. Use one of: ${We.join(", ")}`,aviso:true}}var xe={"gpt-4.1":{inputPer1k:.002,outputPer1k:.008,cachedInputPer1k:5e-4},"gpt-4.1-mini":{inputPer1k:4e-4,outputPer1k:.0016,cachedInputPer1k:1e-4},"gpt-4.1-nano":{inputPer1k:1e-4,outputPer1k:4e-4,cachedInputPer1k:25e-6},
34
35
  "gpt-4o":{inputPer1k:.0025,outputPer1k:.01,cachedInputPer1k:.00125},"gpt-4o-mini":{inputPer1k:15e-5,outputPer1k:6e-4,cachedInputPer1k:75e-6},"gpt-4-turbo":{inputPer1k:.01,outputPer1k:.03},"gpt-4":{inputPer1k:.03,
35
36
  outputPer1k:.06},"gpt-3.5-turbo":{inputPer1k:5e-4,outputPer1k:.0015},"gpt-5":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},"gpt-5-codex":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},
36
37
  "gpt-5-mini":{inputPer1k:25e-5,outputPer1k:.002,cachedInputPer1k:25e-6},"gpt-5-nano":{inputPer1k:5e-5,outputPer1k:4e-4,cachedInputPer1k:5e-6},"gpt-5.1-codex":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},
@@ -47,346 +48,345 @@ able-5":{inputPer1k:.01,outputPer1k:.05,cachedInputPer1k:.001},"claude-fable-5-1
47
48
  cachedInputPer1k:1e-4},"claude-3-5-sonnet":{inputPer1k:.003,outputPer1k:.015,cachedInputPer1k:3e-4},"claude-3-5-haiku":{inputPer1k:8e-4,outputPer1k:.004,cachedInputPer1k:8e-5},"claude-3-opus":{inputPer1k:.015,
48
49
  outputPer1k:.075,cachedInputPer1k:.0015},"claude-3-haiku":{inputPer1k:25e-5,outputPer1k:.00125,cachedInputPer1k:3e-5},"gemini-3.7-flash":{inputPer1k:75e-5,outputPer1k:.00375,cachedInputPer1k:75e-6},"g\
49
50
  emini-3.5-flash":{inputPer1k:.0015,outputPer1k:.009,cachedInputPer1k:15e-5},"gemini-3.5-flash-lite":{inputPer1k:3e-4,outputPer1k:.0025,cachedInputPer1k:3e-5},"gemini-3.1-pro-preview":{inputPer1k:.002,
50
- outputPer1k:.012,cachedInputPer1k:2e-4},"gemini-2.5-pro":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},"gemini-2.5-flash":{inputPer1k:3e-4,outputPer1k:.0025,cachedInputPer1k:3e-5}};function We(t,e,n){
51
- if(!n)return;let o=t[n];if(o)return o;let r=n.lastIndexOf("/"),s=r>=0?n.slice(r+1):n,a=t[s];if(a)return a;for(let i of e){if(!s.startsWith(i))continue;let c=s.charAt(i.length);if(c!==""&&c!=="-"&&c!==
52
- "."&&c!=="_")continue;let m=s.slice(i.length+1);if(!(/^\d/.test(m)&&!/^\d{4}/.test(m)))return t[i]}}function Ye(t){return Object.keys(t).sort((e,n)=>n.length-e.length)}var Mt=Ye(xe);function L(t){return We(
53
- xe,Mt,t)}var Gn=Object.fromEntries(Object.keys(xe).map(t=>[t,t]));function Lt(t){return We(Gn,Mt,t)}var $t={"claude-fable-5":"claude-fable-5-1"};function Wn(t,e){let n=L(t),o=L(e);if(n===void 0||o===void 0||n.inputPer1k!==o.inputPer1k||n.outputPer1k!==o.outputPer1k||N(t)!==N(e))return false;let r=n.cachedInputPer1k??
54
- n.inputPer1k;return (o.cachedInputPer1k??o.inputPer1k)<r}function Yn(t){let e=Lt(t);if(e===void 0||e!==t)return null;let n=$t[e];return n===void 0||n===e||!Wn(e,n)?null:{solicitado:t,servido:n}}var Qn=/(^|,)\s*context-1m/i;
55
- function Xn(t){let e=t.tool_choice;if(e===null||typeof e!="object")return false;let n=e.type;return n==="any"||n==="tool"}function jt(t,e,n={}){if(n.betas!==void 0&&Qn.test(n.betas))return null;let o=J(t,
56
- e);if(o===null||Xn(o))return null;let r=Yn(o.model);return r===null?null:(o.model=r.servido,{cuerpo:Buffer.from(JSON.stringify(o),"utf8"),cambio:r})}function Nt(t,e,n){let o=Object.entries($t).map(([r,
57
- s])=>`${r}\u2192${s}`).join(", ");return t!=="1"?{linea:"modelo hermano (C): APAGADO (BVSALA_GATEWAY_MODELO_HERMANO=1 lo arma)",aviso:false}:e?n?{linea:`modelo hermano (C): ARMADO (${o}; activo si la lic\
58
- encia trae sibling_model_routing; fail-closed). Cambia el model que ves en la respuesta; lectura de cach\xE9 a 0,025\xD7 en vez de 0,1\xD7 (cat\xE1logo). Encendido a mitad de un hilo paga una escritura fr\xEDa del p\
59
- refijo una vez. Si el proveedor rechaza (400) la petici\xF3n reescrita, se reenv\xEDa intacta una vez`,aviso:false}:{linea:"modelo hermano (C): NO SE ARMA \u2014 el canal S4a est\xE1 CERRADO (BVSALA_GATEWAY_ELIS\
60
- ION=0 apaga las tres palancas)",aviso:true}:{linea:"modelo hermano (C): NO SE ARMA \u2014 falta BIVELIO_SERVICE_KEY (sin clave no hay org que consultar). Abre un terminal nuevo o exporta la clave y relanza",
61
- aviso:true}}var zt=new Set;function O(t,e){zt.has(t)||(zt.add(t),console.warn(e));}var G="0.4.21",Bt="x-bvsala-version";var ee=class{options;now;flushIntervalMs;maxBatch;maxBuffer;buffer=[];seatId;ci;timer;inflight=null;fallosSeguidos=0;constructor(e){this.options=e,this.now=e.now??(()=>Date.now()),this.flushIntervalMs=
51
+ outputPer1k:.012,cachedInputPer1k:2e-4},"gemini-2.5-pro":{inputPer1k:.00125,outputPer1k:.01,cachedInputPer1k:125e-6},"gemini-2.5-flash":{inputPer1k:3e-4,outputPer1k:.0025,cachedInputPer1k:3e-5}};function Ge(t,e,n){
52
+ if(!n)return;let o=t[n];if(o)return o;let r=n.lastIndexOf("/"),s=r>=0?n.slice(r+1):n,i=t[s];if(i)return i;for(let a of e){if(!s.startsWith(a))continue;let c=s.charAt(a.length);if(c!==""&&c!=="-"&&c!==
53
+ "."&&c!=="_")continue;let m=s.slice(a.length+1);if(!(/^\d/.test(m)&&!/^\d{4}/.test(m)))return t[a]}}function Ye(t){return Object.keys(t).sort((e,n)=>n.length-e.length)}var Lt=Ye(xe);function M(t){return Ge(
54
+ xe,Lt,t)}var Jn=Object.fromEntries(Object.keys(xe).map(t=>[t,t]));function Mt(t){return Ge(Jn,Lt,t)}var $t={"claude-fable-5":"claude-fable-5-1"};function Gn(t,e){let n=M(t),o=M(e);if(n===void 0||o===void 0||n.inputPer1k!==o.inputPer1k||n.outputPer1k!==o.outputPer1k||j(t)!==j(e))return false;let r=n.cachedInputPer1k??
55
+ n.inputPer1k;return (o.cachedInputPer1k??o.inputPer1k)<r}function Yn(t){let e=Mt(t);if(e===void 0||e!==t)return null;let n=$t[e];return n===void 0||n===e||!Gn(e,n)?null:{solicitado:t,servido:n}}var Qn=/(^|,)\s*context-1m/i;
56
+ function Xn(t){let e=t.tool_choice;if(e===null||typeof e!="object")return false;let n=e.type;return n==="any"||n==="tool"}function Nt(t,e,n={}){if(n.betas!==void 0&&Qn.test(n.betas))return null;let o=W(t,
57
+ e);if(o===null||Xn(o))return null;let r=Yn(o.model);return r===null?null:(o.model=r.servido,{cuerpo:Buffer.from(JSON.stringify(o),"utf8"),cambio:r})}function jt(t,e,n){let o=Object.entries($t).map(([r,
58
+ s])=>`${r}\u2192${s}`).join(", ");return t!=="1"?{linea:"sibling model routing: OFF (BVSALA_GATEWAY_MODELO_HERMANO=1 turns it on)",aviso:false}:e?n?{linea:`sibling model routing: ARMED (${o}; ON if your \
59
+ licence includes sibling_model_routing; fail-closed). Changes the model you see in the response; cache reads cost 0.025\xD7 instead of 0.1\xD7 (list price). Turning it on mid-conversation pays one cold pref\
60
+ ix write, once. If the provider rejects the rewritten request (400), it is re-sent unchanged once`,aviso:false}:{linea:"sibling model routing: NOT ARMED \u2014 the licence channel is CLOSED (BVSALA_GATEWAY_EL\
61
+ ISION=0 turns off all three levers)",aviso:true}:{linea:"sibling model routing: NOT ARMED \u2014 BIVELIO_SERVICE_KEY is missing (without it there is no org whose licence to check). Open a new terminal or exp\
62
+ ort the key and relaunch",aviso:true}}var Bt=new Set;function P(t,e){Bt.has(t)||(Bt.add(t),console.warn(e));}var J="0.4.22",zt="x-bvsala-version";var ee=class{options;now;flushIntervalMs;maxBatch;maxBuffer;buffer=[];seatId;ci;timer;inflight=null;fallosSeguidos=0;constructor(e){this.options=e,this.now=e.now??(()=>Date.now()),this.flushIntervalMs=
62
63
  e.flushIntervalMs??15e3,this.maxBatch=e.maxBatch??50,this.maxBuffer=e.maxBuffer??1e3,this.ci=e.ci??false;}setSeat(e,n){e&&(this.seatId=e),this.ci=n;}record(e){this.buffer.push({report:e,at:this.now()}),this.
63
64
  buffer.length>this.maxBuffer&&this.buffer.splice(0,this.buffer.length-this.maxBuffer),this.buffer.length>=this.maxBatch&&this.flush();}start(){this.timer||(this.timer=setInterval(()=>{this.flush();},this.
64
65
  flushIntervalMs),this.timer.unref?.());}stop(){this.timer&&(clearInterval(this.timer),this.timer=void 0),this.flush();}async stopAndDrain(e){this.timer&&(clearInterval(this.timer),this.timer=void 0);let n=e??
65
66
  Math.ceil(this.maxBuffer/this.maxBatch)+4,o=0;for(let r=0;r<n&&this.buffer.length>0;r++){let s=this.buffer.length;if(await this.flush(),o=this.buffer.length>=s?o+1:0,o>=2)break}}async flush(){if(this.
66
67
  inflight)return this.inflight;if(this.buffer.length!==0){this.inflight=this.hacerFlush();try{await this.inflight;}finally{this.inflight=null;}}}async hacerFlush(){let e=this.options.fetchImpl??globalThis.
67
68
  fetch;if(!e)return;let n=this.buffer.splice(0,this.maxBatch);try{let o=n.map(s=>this.toWire(s)),r=await e(this.options.url,{method:"POST",headers:{"content-type":"application/json",authorization:`Bear\
68
- er ${this.options.serviceAccountKey}`,[Bt]:`${this.options.surface??"sdk"}/${G}`},body:JSON.stringify(o)});r.ok?this.fallosSeguidos=0:r.status>=500?(this.fallosSeguidos++,this.requeue(n),this.avisarDeFallo(
69
- `el servidor respondi\xF3 ${r.status}`)):r.status===401||r.status===403?(O(`metering-auth-${r.status}`,`[bv-sala] el env\xEDo de uso est\xE1 siendo rechazado con HTTP ${r.status} en ${this.options.url}\
70
- . Revisa 'serviceAccountKey' (\xBFrotada? \xBFrevocada? \xBFde otra organizaci\xF3n?). Mientras no funcione NO se reporta uso: el panel dir\xE1 que no ve tr\xE1fico y los asientos facturables saldr\xE1n a cero.`),
71
- this.options.onAuthError?.(r.status)):(this.fallosSeguidos++,this.avisarDeFallo(r.status===404||r.status===405?`HTTP ${r.status}: la URL de env\xEDo parece equivocada`:`el servidor rechaz\xF3 el lote con\
72
- HTTP ${r.status}`));}catch(o){this.fallosSeguidos++,this.requeue(n),this.avisarDeFallo(o instanceof Error?o.message:"error de red");}}avisarDeFallo(e){if(this.options.silenceWarnings)return;let n=this.
73
- fallosSeguidos;n<3||(n-3)%12!==0||console.warn(`[bv-sala] el env\xEDo de uso lleva ${n} intentos seguidos fallando contra ${this.options.url} (${e}). Las peticiones SIGUEN funcionando, pero el uso no \
74
- est\xE1 llegando: el panel dir\xE1 que no ve tr\xE1fico y los asientos facturables saldr\xE1n a cero. Comprueba 'meteringUrl' \u2014 por defecto es \`\${licenseServerUrl}/api/metering/usage\`.`);}requeue(e){
75
- this.buffer=[...e,...this.buffer],this.buffer.length>this.maxBuffer&&(this.buffer.length=this.maxBuffer);}toWire(e){let n=e.report;return {orgId:this.options.orgId??"self",...this.seatId?{seatId:this.seatId}:
76
- {},...this.ci?{ci:true}:{},requestId:n.requestId,model:n.model,at:e.at,avoidedLlm:n.calls.avoidedLlm,actualLlm:n.calls.actualLlm,baselineInputTokens:n.tokens.baselineInput,actualInputTokens:n.tokens.actualInput,
77
- eliminatedInputTokens:n.tokens.eliminatedInput,compressedInputTokens:n.tokens.compressedInput,baselineOutputTokens:n.tokens.baselineOutput,actualOutputTokens:n.tokens.actualOutput,providerCachedInputTokens:n.
78
- tokens.providerCachedInput??0,cacheResult:n.cache.result,...n.cost.eliminatedInputFloorCost!==void 0&&Number.isFinite(n.cost.eliminatedInputFloorCost)&&n.cost.eliminatedInputFloorCost>0&&n.tokens.eliminatedInput>
79
- 0?{eliminatedInputFloorCost:n.cost.eliminatedInputFloorCost}:{},baselineCost:n.cost.baselineEstimate,actualCost:n.cost.actual,netSavings:n.cost.netSavings,...n.cost.basis?{basis:n.cost.basis}:{},...n.
80
- attribution?.sessionId?{sessionId:n.attribution.sessionId}:{},...n.attribution?.agentId?{agentId:n.attribution.agentId}:{}}}};function Se(){return globalThis.process}function Zn(t){if(/^[0-9a-f]{12}$/.test(t)||/^\d{1,3}(\.\d{1,3}){3}$/.test(t)||/^ip-\d{1,3}(-\d{1,3}){3}$/i.test(t))return true;let e="[bcdfghjklmnpqrstvwxz2456789\
69
+ er ${this.options.serviceAccountKey}`,[zt]:`${this.options.surface??"sdk"}/${J}`},body:JSON.stringify(o)});r.ok?this.fallosSeguidos=0:r.status>=500?(this.fallosSeguidos++,this.requeue(n),this.avisarDeFallo(
70
+ `the server responded ${r.status}`)):r.status===401||r.status===403?(P(`metering-auth-${r.status}`,`[bv-sala] usage reporting is being rejected with HTTP ${r.status} at ${this.options.url}. Check 'ser\
71
+ viceAccountKey' (rotated? revoked? from another organization?). Until it works, no usage is reported: the dashboard will say it sees no traffic and billable seats will show zero.`),this.options.onAuthError?.(
72
+ r.status)):(this.fallosSeguidos++,this.avisarDeFallo(r.status===404||r.status===405?`HTTP ${r.status}: the reporting URL looks wrong`:`the server rejected the batch with HTTP ${r.status}`));}catch(o){this.
73
+ fallosSeguidos++,this.requeue(n),this.avisarDeFallo(o instanceof Error?o.message:"network error");}}avisarDeFallo(e){if(this.options.silenceWarnings)return;let n=this.fallosSeguidos;n<3||(n-3)%12!==0||
74
+ console.warn(`[bv-sala] usage reporting has failed ${n} times in a row against ${this.options.url} (${e}). Requests keep working, but usage is not arriving: the dashboard will say it sees no traffic a\
75
+ nd billable seats will show zero. Check 'meteringUrl' \u2014 by default it is \`\${licenseServerUrl}/api/metering/usage\`.`);}requeue(e){this.buffer=[...e,...this.buffer],this.buffer.length>this.maxBuffer&&
76
+ (this.buffer.length=this.maxBuffer);}toWire(e){let n=e.report;return {orgId:this.options.orgId??"self",...this.seatId?{seatId:this.seatId}:{},...this.ci?{ci:true}:{},requestId:n.requestId,model:n.model,at:e.
77
+ at,avoidedLlm:n.calls.avoidedLlm,actualLlm:n.calls.actualLlm,baselineInputTokens:n.tokens.baselineInput,actualInputTokens:n.tokens.actualInput,eliminatedInputTokens:n.tokens.eliminatedInput,compressedInputTokens:n.
78
+ tokens.compressedInput,baselineOutputTokens:n.tokens.baselineOutput,actualOutputTokens:n.tokens.actualOutput,providerCachedInputTokens:n.tokens.providerCachedInput??0,cacheResult:n.cache.result,...n.cost.
79
+ eliminatedInputFloorCost!==void 0&&Number.isFinite(n.cost.eliminatedInputFloorCost)&&n.cost.eliminatedInputFloorCost>0&&n.tokens.eliminatedInput>0?{eliminatedInputFloorCost:n.cost.eliminatedInputFloorCost}:
80
+ {},baselineCost:n.cost.baselineEstimate,actualCost:n.cost.actual,netSavings:n.cost.netSavings,...n.cost.basis?{basis:n.cost.basis}:{},...n.attribution?.sessionId?{sessionId:n.attribution.sessionId}:{},
81
+ ...n.attribution?.agentId?{agentId:n.attribution.agentId}:{}}}};function we(){return globalThis.process}function Zn(t){if(/^[0-9a-f]{12}$/.test(t)||/^\d{1,3}(\.\d{1,3}){3}$/.test(t)||/^ip-\d{1,3}(-\d{1,3}){3}$/i.test(t))return true;let e="[bcdfghjklmnpqrstvwxz2456789\
81
82
  ]";return new RegExp(`-${e}{8,10}-${e}{5}$`).test(t)}function eo(t){return !!(t.AWS_LAMBDA_FUNCTION_NAME??t.LAMBDA_TASK_ROOT??t.VERCEL??t.VERCEL_ENV??t.K_SERVICE??t.FUNCTION_TARGET??t.FUNCTIONS_WORKER_RUNTIME??
82
- t.NETLIFY)}async function to(){if(Se()?.execPath)try{let e=await import(["node","os"].join(":")),n=typeof e.hostname=="function"?e.hostname():"";return n.length>0?n:void 0}catch{return}}async function no(t,e=to){
83
- if(t.KUBERNETES_SERVICE_HOST||eo(t))return;let n=t.HOSTNAME??t.COMPUTERNAME??await e();if(!(!n||Zn(n)))return n}async function oo(){let t=Se();if(!t||!t.env)return;let e=t.env,n=await no(e),o=[t.platform,
83
+ t.NETLIFY)}async function to(){if(we()?.execPath)try{let e=await import(["node","os"].join(":")),n=typeof e.hostname=="function"?e.hostname():"";return n.length>0?n:void 0}catch{return}}async function no(t,e=to){
84
+ if(t.KUBERNETES_SERVICE_HOST||eo(t))return;let n=t.HOSTNAME??t.COMPUTERNAME??await e();if(!(!n||Zn(n)))return n}async function oo(){let t=we();if(!t||!t.env)return;let e=t.env,n=await no(e),o=[t.platform,
84
85
  t.arch,t.execPath,n,e.USER??e.USERNAME??e.LOGNAME,e.HOME??e.USERPROFILE].filter(r=>typeof r=="string"&&r.length>0);return o.length?o.join("|"):void 0}var ro=["CI","CONTINUOUS_INTEGRATION","GITHUB_ACTI\
85
- ONS","GITLAB_CI","CIRCLECI","TRAVIS","JENKINS_URL","BUILDKITE","TF_BUILD","TEAMCITY_VERSION","BITBUCKET_BUILD_NUMBER","CODEBUILD_BUILD_ID","HEROKU_TEST_RUN_ID","DRONE"];function Ee(){let t=Se()?.env;if(!t)
86
- return false;for(let e of ro){let n=t[e];if(n&&n!=="false"&&n!=="0")return true}return false}async function so(t,e){let n=globalThis.crypto,o=new TextEncoder;if(n?.subtle)try{let a=await n.subtle.importKey("raw",
87
- o.encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),i=await n.subtle.sign("HMAC",a,o.encode(e));return [...new Uint8Array(i)].map(c=>c.toString(16).padStart(2,"0")).join("")}catch{}let r=2166136261,s=`${t}\
88
- |${e}`;for(let a=0;a<s.length;a++)r^=s.charCodeAt(a),r=Math.imul(r,16777619);return (r>>>0).toString(16).padStart(8,"0")}async function we(t){let n=Se()?.env?.BVSALA_SEAT_ID;if(n&&ao.test(n))return n;let o=await oo();
89
- if(!o)return;let r=await so(t,o);if(r)return `m-${r.slice(0,32)}`}var ao=/^[A-Za-z0-9._:/-]{1,128}$/;var W=class{resto="";uso=null;elisionObservada=null;eventos={};observar(e){this.resto+=typeof e=="string"?e:e.toString("utf8");let n;for(;(n=this.resto.indexOf(`
86
+ ONS","GITLAB_CI","CIRCLECI","TRAVIS","JENKINS_URL","BUILDKITE","TF_BUILD","TEAMCITY_VERSION","BITBUCKET_BUILD_NUMBER","CODEBUILD_BUILD_ID","HEROKU_TEST_RUN_ID","DRONE"];function Se(){let t=we()?.env;if(!t)
87
+ return false;for(let e of ro){let n=t[e];if(n&&n!=="false"&&n!=="0")return true}return false}async function so(t,e){let n=globalThis.crypto,o=new TextEncoder;if(n?.subtle)try{let i=await n.subtle.importKey("raw",
88
+ o.encode(t),{name:"HMAC",hash:"SHA-256"},!1,["sign"]),a=await n.subtle.sign("HMAC",i,o.encode(e));return [...new Uint8Array(a)].map(c=>c.toString(16).padStart(2,"0")).join("")}catch{}let r=2166136261,s=`${t}\
89
+ |${e}`;for(let i=0;i<s.length;i++)r^=s.charCodeAt(i),r=Math.imul(r,16777619);return (r>>>0).toString(16).padStart(8,"0")}async function Ee(t){let n=we()?.env?.BVSALA_SEAT_ID;if(n&&io.test(n))return n;let o=await oo();
90
+ if(!o)return;let r=await so(t,o);if(r)return `m-${r.slice(0,32)}`}var io=/^[A-Za-z0-9._:/-]{1,128}$/;var G=class{resto="";uso=null;elisionObservada=null;eventos={};observar(e){this.resto+=typeof e=="string"?e:e.toString("utf8");let n;for(;(n=this.resto.indexOf(`
90
91
 
91
92
  `))>=0;){let o=this.resto.slice(0,n);this.resto=this.resto.slice(n+2);let r=/^event: (.+)$/m.exec(o)?.[1];if(r&&(this.eventos[r]=(this.eventos[r]??0)+1),r!=="message_start"&&r!=="message_delta")continue;
92
- let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let a=JSON.parse(s),i=Ut(a.context_management);i&&(this.elisionObservada=i);let c=a.usage??a.message?.usage;if(c){let m=c.cache_creation||this.uso?.cache_creation?
93
+ let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let i=JSON.parse(s),a=Ut(i.context_management);a&&(this.elisionObservada=a);let c=i.usage??i.message?.usage;if(c){let m=c.cache_creation||this.uso?.cache_creation?
93
94
  {...this.uso?.cache_creation,...c.cache_creation}:void 0;this.uso={...this.uso,...c,...m?{cache_creation:m}:{}};}}catch{}}}usage(){return this.uso}usageFinal(){return (this.eventos.message_delta??0)>0?this.
94
- uso:null}elision(){return this.elisionObservada}};function io(t,e){if(!e||e.atribuible!==true||!(e.tokensLimpiadosToolUses>0))return 0;let n=t.cachedInputPer1k;return typeof n!="number"||!Number.isFinite(
95
+ uso:null}elision(){return this.elisionObservada}};function ao(t,e){if(!e||e.atribuible!==true||!(e.tokensLimpiadosToolUses>0))return 0;let n=t.cachedInputPer1k;return typeof n!="number"||!Number.isFinite(
95
96
  n)||n<=0?0:e.tokensLimpiadosToolUses/1e3*n}function Ut(t){if(t===null||typeof t!="object")return null;let e=t.applied_edits;if(!Array.isArray(e)||e.length===0)return null;let n=0,o=0;for(let r of e){let s=r.
96
97
  cleared_input_tokens;typeof s=="number"&&Number.isFinite(s)&&s>0&&(n+=s,r.type==="clear_tool_uses_20250919"&&(o+=s));}return {borrados:e.length,tokensLimpiados:n,tokensLimpiadosToolUses:o}}function Vt(t){
97
- try{let e=JSON.parse(typeof t=="string"?t:t.toString("utf8"));return Ut(e.context_management)}catch{return null}}var Ae=class{resto="";uso=null;eventos={};observar(e){this.resto+=typeof e=="string"?e:
98
+ try{let e=JSON.parse(typeof t=="string"?t:t.toString("utf8"));return Ut(e.context_management)}catch{return null}}var Te=class{resto="";uso=null;eventos={};observar(e){this.resto+=typeof e=="string"?e:
98
99
  e.toString("utf8");let n;for(;(n=this.resto.indexOf(`
99
100
 
100
- `))>=0;){let o=this.resto.slice(0,n);this.resto=this.resto.slice(n+2);let r=/^event: (.+)$/m.exec(o)?.[1];r&&(this.eventos[r]=(this.eventos[r]??0)+1);let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let a=JSON.
101
- parse(s);if((r??a.type)!=="response.completed")continue;let i=a.response?.usage;i&&(this.uso=co(i));}catch{}}}usage(){return this.uso}usageFinal(){return this.uso}};function co(t){let e=Math.max(0,t.input_tokens??
101
+ `))>=0;){let o=this.resto.slice(0,n);this.resto=this.resto.slice(n+2);let r=/^event: (.+)$/m.exec(o)?.[1];r&&(this.eventos[r]=(this.eventos[r]??0)+1);let s=/^data: (.+)$/m.exec(o)?.[1];if(s)try{let i=JSON.
102
+ parse(s);if((r??i.type)!=="response.completed")continue;let a=i.response?.usage;a&&(this.uso=co(a));}catch{}}}usage(){return this.uso}usageFinal(){return this.uso}};function co(t){let e=Math.max(0,t.input_tokens??
102
103
  0),n=Math.min(Math.max(0,t.input_tokens_details?.cached_tokens??0),e),o=Math.min(Math.max(0,t.input_tokens_details?.cache_write_tokens??0),e-n);return {input_tokens:e-n-o,output_tokens:Math.max(0,t.output_tokens??
103
104
  0),cache_read_input_tokens:n,cache_creation_input_tokens:o}}function Ft(t,e,n){let o=t.cachedInputPer1k??t.inputPer1k;return e.entrada/1e3*t.inputPer1k+e.cacheLeida/1e3*o+(e.cacheEscrita-e.cacheEscrita1h)/
104
- 1e3*t.inputPer1k*N(n)+e.cacheEscrita1h/1e3*t.inputPer1k*2+e.salida/1e3*t.outputPer1k}function Ht(t,e){let n=L(t);if(!n)return null;let o=e.input_tokens??0,r=e.output_tokens??0,s=e.cache_read_input_tokens??
105
- 0,a=e.cache_creation,i=e.cache_creation_input_tokens??(a?.ephemeral_5m_input_tokens??0)+(a?.ephemeral_1h_input_tokens??0),c=Math.min(a?.ephemeral_1h_input_tokens??0,i),m={entrada:o,salida:r,cacheLeida:s,
106
- cacheEscrita:i,cacheEscrita1h:c};return {costeUsd:Ft(n,m,t),precio:n,tokens:m}}function qt(t,e){let o=Qe(t,e)?.model;return typeof o=="string"?o:""}function Qe(t,e){try{let n=t,o={maxOutputLength:lo};switch((e??
107
- "").trim().toLowerCase()){case "gzip":case "x-gzip":n=z.gunzipSync(t,o);break;case "deflate":n=z.inflateSync(t,o);break;case "br":n=z.brotliDecompressSync(t,o);break;case "zstd":{let r=z.zstdDecompressSync;
108
- if(typeof r!="function")return null;n=r(t,o);break}default:break}return JSON.parse(n.toString("utf8"))}catch{return null}}var lo=5*1024*1024;function Kt(){return typeof z.zstdDecompressSync=="function"}
109
- function Xe(t){try{return JSON.parse(typeof t=="string"?t:t.toString("utf8")).usage??null}catch{return null}}function Jt(t,e,n,o,r,s,a){return Yt(t,e,n,false,o,r,s,a)}function Gt(t,e,n,o){return Yt(t,e,n,
110
- true,o)}function Wt(t,e,n,o,r){return {requestId:t,model:e,...r&&(r.sessionId||r.agentId)?{attribution:r}:{},calls:{avoidedLlm:0,actualLlm:0},tokens:{baselineInput:0,actualInput:0,eliminatedInput:0,compressedInput:0,
111
- baselineOutput:0,actualOutput:0,providerCachedInput:0},cache:{result:"miss"},cost:{baselineEstimate:n,actual:o,netSavings:n>0?Math.max(-100,Math.min(1,(n-o)/n)):0,basis:"shadow"}}}function uo(t,e){let n=L(
112
- t.solicitado);return n===void 0?0:Math.max(0,Ft(n,e.tokens,t.solicitado)-e.costeUsd)}function Yt(t,e,n,o,r,s,a,i){let c=n.tokens.entrada+n.tokens.cacheLeida+n.tokens.cacheEscrita,m=n.tokens.cacheEscrita-
113
- n.tokens.cacheEscrita1h,g=!o&&a&&a.rebajadas>0&&a.yaEran5m===0&&m>0?m/1e3*n.precio.inputPer1k*(2-1.25):0,u=!o&&i?uo(i,n):0,h=n.costeUsd+g+u,k=g+u,l=o?0:io(n.precio,s);return {requestId:t,model:e,...r&&
105
+ 1e3*t.inputPer1k*j(n)+e.cacheEscrita1h/1e3*t.inputPer1k*2+e.salida/1e3*t.outputPer1k}function Ht(t,e){let n=M(t);if(!n)return null;let o=e.input_tokens??0,r=e.output_tokens??0,s=e.cache_read_input_tokens??
106
+ 0,i=e.cache_creation,a=e.cache_creation_input_tokens??(i?.ephemeral_5m_input_tokens??0)+(i?.ephemeral_1h_input_tokens??0),c=Math.min(i?.ephemeral_1h_input_tokens??0,a),m={entrada:o,salida:r,cacheLeida:s,
107
+ cacheEscrita:a,cacheEscrita1h:c};return {costeUsd:Ft(n,m,t),precio:n,tokens:m}}function qt(t,e){let o=Qe(t,e)?.model;return typeof o=="string"?o:""}function Qe(t,e){try{let n=t,o={maxOutputLength:lo};switch((e??
108
+ "").trim().toLowerCase()){case "gzip":case "x-gzip":n=B.gunzipSync(t,o);break;case "deflate":n=B.inflateSync(t,o);break;case "br":n=B.brotliDecompressSync(t,o);break;case "zstd":{let r=B.zstdDecompressSync;
109
+ if(typeof r!="function")return null;n=r(t,o);break}default:break}return JSON.parse(n.toString("utf8"))}catch{return null}}var lo=5*1024*1024;function Kt(){return typeof B.zstdDecompressSync=="function"}
110
+ function Xe(t){try{return JSON.parse(typeof t=="string"?t:t.toString("utf8")).usage??null}catch{return null}}function Wt(t,e,n,o,r,s,i){return Yt(t,e,n,false,o,r,s,i)}function Jt(t,e,n,o){return Yt(t,e,n,
111
+ true,o)}function Gt(t,e,n,o,r){return {requestId:t,model:e,...r&&(r.sessionId||r.agentId)?{attribution:r}:{},calls:{avoidedLlm:0,actualLlm:0},tokens:{baselineInput:0,actualInput:0,eliminatedInput:0,compressedInput:0,
112
+ baselineOutput:0,actualOutput:0,providerCachedInput:0},cache:{result:"miss"},cost:{baselineEstimate:n,actual:o,netSavings:n>0?Math.max(-100,Math.min(1,(n-o)/n)):0,basis:"shadow"}}}function uo(t,e){let n=M(
113
+ t.solicitado);return n===void 0?0:Math.max(0,Ft(n,e.tokens,t.solicitado)-e.costeUsd)}function Yt(t,e,n,o,r,s,i,a){let c=n.tokens.entrada+n.tokens.cacheLeida+n.tokens.cacheEscrita,m=n.tokens.cacheEscrita-
114
+ n.tokens.cacheEscrita1h,g=!o&&i&&i.rebajadas>0&&i.yaEran5m===0&&m>0?m/1e3*n.precio.inputPer1k*(2-1.25):0,u=!o&&a?uo(a,n):0,h=n.costeUsd+g+u,k=g+u,l=o?0:ao(n.precio,s);return {requestId:t,model:e,...r&&
114
115
  (r.sessionId||r.agentId)?{attribution:r}:{},calls:o?{avoidedLlm:1,actualLlm:0}:{avoidedLlm:0,actualLlm:1},tokens:{baselineInput:c,actualInput:o?0:c,eliminatedInput:o?0:s?.tokensLimpiados??0,compressedInput:0,
115
116
  baselineOutput:n.tokens.salida,actualOutput:o?0:n.tokens.salida,providerCachedInput:o?0:n.tokens.cacheLeida},cache:{result:o?"singleflight":"miss"},cost:o?{baselineEstimate:n.costeUsd,actual:0,netSavings:1,
116
117
  basis:"estimated"}:{baselineEstimate:h,actual:n.costeUsd,netSavings:h>0?Math.max(-100,Math.min(1,k/h)):0,basis:"measured",...l>0?{eliminatedInputFloorCost:l}:{}}}}var po=1024,de=120,mo=8,fo=256*1024,ho=/error|exception|traceback|warn|fatal|fail|panic|assert|denied|refused|timeout|\bat\s+\S+:\d+/i,Zt=/^\s*(export\s|import\s|function\s|class\s|interface\s|type\s|enum\s|def\s|fn\s|func\s|impl\s|struct\s|trait\s|package\s|module\s|@\w)/;
117
118
  function go(t){return Buffer.byteLength(typeof t=="string"?t:JSON.stringify(t??""))}function bo(t){return typeof t=="string"?t:Array.isArray(t)?t.map(e=>e&&typeof e=="object"&&typeof e.text=="string"?
118
119
  e.text:"").join(`
119
120
  `):""}var vo=/^\s*\d+[\t→]/;function en(t){return t.replace(vo,"")}function yo(t){if(!t)return "otro";let e=t.split(`
120
- `),n=Math.max(1,e.length),o=0,r=0,s=0;for(let a of e){let i=en(a);(/^[+-](?![+-])/.test(i)||i.startsWith("@@")||i.startsWith("diff --git"))&&(o+=1),Zt.test(i)&&(r+=1),(/^\s*(\d{4}-\d{2}-\d{2}|\[\d|\d{2}:\d{2}:\d{2})/.
121
- test(i)||/\b(INFO|DEBUG|TRACE|WARN|ERROR)\b/.test(i))&&(s+=1);}return o/n>.25?"diff":s/n>.3?"log":r/n>.08?"codigo":"otro"}function ko(t,e){if(e==="diff"||e==="otro")return Buffer.byteLength(t);let n=t.
121
+ `),n=Math.max(1,e.length),o=0,r=0,s=0;for(let i of e){let a=en(i);(/^[+-](?![+-])/.test(a)||a.startsWith("@@")||a.startsWith("diff --git"))&&(o+=1),Zt.test(a)&&(r+=1),(/^\s*(\d{4}-\d{2}-\d{2}|\[\d|\d{2}:\d{2}:\d{2})/.
122
+ test(a)||/\b(INFO|DEBUG|TRACE|WARN|ERROR)\b/.test(a))&&(s+=1);}return o/n>.25?"diff":s/n>.3?"log":r/n>.08?"codigo":"otro"}function ko(t,e){if(e==="diff"||e==="otro")return Buffer.byteLength(t);let n=t.
122
123
  split(`
123
- `),o=new Set;for(let s=0;s<n.length;s++){let a=en(n[s]);(e==="log"?ho.test(a):Zt.test(a))&&(o.add(s),e==="log"&&(s>0&&o.add(s-1),s<n.length-1&&o.add(s+1)));}let r=de;for(let s of o)r+=Buffer.byteLength(
124
- n[s])+1;return Math.min(r,Buffer.byteLength(t))}var xo=(t,e)=>t==="cache_control"?void 0:e;function So(t){if(typeof t=="string")return `text:${t}`;if(!t||typeof t!="object"||Array.isArray(t))return `jso\
124
+ `),o=new Set;for(let s=0;s<n.length;s++){let i=en(n[s]);(e==="log"?ho.test(i):Zt.test(i))&&(o.add(s),e==="log"&&(s>0&&o.add(s-1),s<n.length-1&&o.add(s+1)));}let r=de;for(let s of o)r+=Buffer.byteLength(
125
+ n[s])+1;return Math.min(r,Buffer.byteLength(t))}var xo=(t,e)=>t==="cache_control"?void 0:e;function wo(t){if(typeof t=="string")return `text:${t}`;if(!t||typeof t!="object"||Array.isArray(t))return `jso\
125
126
  n:${JSON.stringify(t??"")}`;let e=t;return e.type==="text"&&typeof e.text=="string"&&Object.keys(e).every(n=>n==="type"||n==="text"||n==="cache_control")?`text:${e.text}`:`json:${JSON.stringify(e,xo)}`}
126
- function Eo(t){return createHash("sha256").update(So(t)).digest("hex").slice(0,16)}function wo(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t.cache_control;return !e||typeof e!="object"?null:
127
- e.ttl==="1h"?"1h":"5m"}function tn(t){let e=[],n=(o,r)=>{let s=go(r),a={clave:Eo(r),tipo:o,marca:wo(r),bytes:s,bytesV1:s,clase:"otro",esError:false};if(o.endsWith(":tool_result")&&r&&typeof r=="object"){
128
- let i=r;a.esError=i.is_error===true;let c=bo(i.content);a.clase=yo(c);let m=s-Buffer.byteLength(c);a.bytesV1=Math.min(s,Math.max(0,m)+ko(c,a.clase));}e.push(a);};if(Array.isArray(t.tools))for(let o of t.tools)
127
+ function So(t){return createHash("sha256").update(wo(t)).digest("hex").slice(0,16)}function Eo(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let e=t.cache_control;return !e||typeof e!="object"?null:
128
+ e.ttl==="1h"?"1h":"5m"}function tn(t){let e=[],n=(o,r)=>{let s=go(r),i={clave:So(r),tipo:o,marca:Eo(r),bytes:s,bytesV1:s,clase:"otro",esError:false};if(o.endsWith(":tool_result")&&r&&typeof r=="object"){
129
+ let a=r;i.esError=a.is_error===true;let c=bo(a.content);i.clase=yo(c);let m=s-Buffer.byteLength(c);i.bytesV1=Math.min(s,Math.max(0,m)+ko(c,i.clase));}e.push(i);};if(Array.isArray(t.tools))for(let o of t.tools)
129
130
  n("tools",o);if(Array.isArray(t.system))for(let o of t.system)n("system",o);else (t.system??void 0)!==void 0&&n("system",t.system);for(let o of Array.isArray(t.messages)?t.messages:[]){if(!o||typeof o!=
130
- "object"){n("?:invalido",o);continue}let r=o,s=typeof r.role=="string"?r.role:"?";if(typeof r.content=="string")n(`${s}:text`,r.content);else if(Array.isArray(r.content))for(let a of r.content)n(`${s}\
131
- :${a?.type??"?"}`,a);}return e}function Ao(){return {nacimiento:new Map,turno:0,elididosV2m:new Set}}function To(t,e){let n=t.turno++;for(let o of e)t.nacimiento.has(o.clave)||t.nacimiento.set(o.clave,n);
132
- return n}function nn(t,e,n,o,r=fo){let s=e.reduce((u,h,k)=>h.tipo.endsWith(":tool_result")?k:u,-1),a=(u,h)=>u.tipo.endsWith(":tool_result")&&u.bytes>=po&&h!==s;return e.map((u,h)=>{if(!a(u,
133
- h))return {clave:u.clave,bytes:u.bytes};let k=o-(n.nacimiento.get(u.clave)??o);return k>=2&&k*u.bytes/1024>=mo?{clave:`${u.clave}:e`,bytes:de}:{clave:u.clave,bytes:u.bytes}});}function _o(t){let e=L(t);return e?{inputPer1k:e.inputPer1k,lecturaPer1k:e.cachedInputPer1k??e.inputPer1k,escrituraPer1k:e.inputPer1k*
134
- N(t),escritura1hPer1k:e.inputPer1k*Ve(t)}:null}function Qt(){return {lecturaUsd:0,escrituraUsd:0,entradaUsd:0,totalUsd:0,tokensLeidos:0,tokensEscritos:0,tokensEntrada:0,tokensInvalidados:0,invalidacionUsd:0}}
131
+ "object"){n("?:invalido",o);continue}let r=o,s=typeof r.role=="string"?r.role:"?";if(typeof r.content=="string")n(`${s}:text`,r.content);else if(Array.isArray(r.content))for(let i of r.content)n(`${s}\
132
+ :${i?.type??"?"}`,i);}return e}function To(){return {nacimiento:new Map,turno:0,elididosV2m:new Set}}function Ao(t,e){let n=t.turno++;for(let o of e)t.nacimiento.has(o.clave)||t.nacimiento.set(o.clave,n);
133
+ return n}function nn(t,e,n,o,r=fo){let s=e.reduce((u,h,k)=>h.tipo.endsWith(":tool_result")?k:u,-1),i=(u,h)=>u.tipo.endsWith(":tool_result")&&u.bytes>=po&&h!==s;return e.map((u,h)=>{if(!i(u,
134
+ h))return {clave:u.clave,bytes:u.bytes};let k=o-(n.nacimiento.get(u.clave)??o);return k>=2&&k*u.bytes/1024>=mo?{clave:`${u.clave}:e`,bytes:de}:{clave:u.clave,bytes:u.bytes}});}function _o(t){let e=M(t);return e?{inputPer1k:e.inputPer1k,lecturaPer1k:e.cachedInputPer1k??e.inputPer1k,escrituraPer1k:e.inputPer1k*
135
+ j(t),escritura1hPer1k:e.inputPer1k*Ve(t)}:null}function Qt(){return {lecturaUsd:0,escrituraUsd:0,entradaUsd:0,totalUsd:0,tokensLeidos:0,tokensEscritos:0,tokensEntrada:0,tokensInvalidados:0,invalidacionUsd:0}}
135
136
  var et={"1h":36e5,"5m":3e5},Co=20,Ro=6e3,Io=2e4,te=class{constructor(e=Io){this.tope=e;}tope;entradas=new Map;get size(){return this.entradas.size}viva(e,n){let o=this.entradas.get(e);return o&&n<=o.expira?
136
137
  o:null}escribir(e,n,o){this.entradas.delete(e),this.entradas.set(e,{expira:n+et[o],ttl:o}),this.entradas.size>this.tope&&this.podar(n);}refrescar(e,n){let o=this.entradas.get(e);o&&(this.entradas.delete(
137
138
  e),this.entradas.set(e,{expira:n+et[o.ttl],ttl:o.ttl}));}podar(e){for(let[n,o]of this.entradas)e>o.expira&&this.entradas.delete(n);for(;this.entradas.size>this.tope;){let n=this.entradas.keys().next().
138
- value;if(n===void 0)break;this.entradas.delete(n);}}};function Po(t,e){let n=new Array(e.length),o=createHash("sha256").update(`model:${t}`).digest("hex");for(let r=0;r<e.length;r++)o=createHash("sha256").update(o).update(
139
- "|").update(e[r]).digest("hex").slice(0,32),n[r]=o;return n}function Oo(t){let e=new Array(t.length),n=-1,o="";for(let r=0;r<t.length;r++){let s=t[r],a=s.endsWith(":tool_use")||s.endsWith(":tool_resul\
140
- t");a&&s===o||(n+=1),o=a?s:"",e[r]=n;}return e}function Xt(t,e){return t.endsWith(":image")?Math.min(e,Ro):e}var pe=class{constructor(e,n){this.transformar=e;this.cache=n??new te;}transformar;estado=Ao();cache;previo=[];previoUltimoBp=-1;previoTtl="\
141
- 5m";hayPrevio=false;atPrevio=0;acumulado=Qt();paso(e){let n=Qt(),o=To(this.estado,e.bloques),r=_o(e.model),s=this.transformar(e.bloques,this.estado,o);if(!r||e.entradaReal<=0||s.length===0)return this.previo=
142
- s,this.previoUltimoBp=-1,this.previoTtl="5m",this.hayPrevio=true,this.atPrevio=e.at,n;let a=s.map((d,b)=>e.bloques[b]?.tipo??""),i=s.map((d,b)=>e.bloques[b]?.marca??null),c=e.bloques.reduce((d,b)=>d+Xt(
143
- b.tipo,b.bytes),0),m=c>0?e.entradaReal/c:0,g=s.map((d,b)=>Xt(a[b],d.bytes)*m),u=Po(e.model,s.map(d=>d.clave)),h=Oo(a),k=i.map((d,b)=>d?b:-1).filter(d=>d>=0);if(k.length===0){for(let d=0;d<s.length;d++)
139
+ value;if(n===void 0)break;this.entradas.delete(n);}}};function Oo(t,e){let n=new Array(e.length),o=createHash("sha256").update(`model:${t}`).digest("hex");for(let r=0;r<e.length;r++)o=createHash("sha256").update(o).update(
140
+ "|").update(e[r]).digest("hex").slice(0,32),n[r]=o;return n}function Po(t){let e=new Array(t.length),n=-1,o="";for(let r=0;r<t.length;r++){let s=t[r],i=s.endsWith(":tool_use")||s.endsWith(":tool_resul\
141
+ t");i&&s===o||(n+=1),o=i?s:"",e[r]=n;}return e}function Xt(t,e){return t.endsWith(":image")?Math.min(e,Ro):e}var pe=class{constructor(e,n){this.transformar=e;this.cache=n??new te;}transformar;estado=To();cache;previo=[];previoUltimoBp=-1;previoTtl="\
142
+ 5m";hayPrevio=false;atPrevio=0;acumulado=Qt();paso(e){let n=Qt(),o=Ao(this.estado,e.bloques),r=_o(e.model),s=this.transformar(e.bloques,this.estado,o);if(!r||e.entradaReal<=0||s.length===0)return this.previo=
143
+ s,this.previoUltimoBp=-1,this.previoTtl="5m",this.hayPrevio=true,this.atPrevio=e.at,n;let i=s.map((d,b)=>e.bloques[b]?.tipo??""),a=s.map((d,b)=>e.bloques[b]?.marca??null),c=e.bloques.reduce((d,b)=>d+Xt(
144
+ b.tipo,b.bytes),0),m=c>0?e.entradaReal/c:0,g=s.map((d,b)=>Xt(i[b],d.bytes)*m),u=Oo(e.model,s.map(d=>d.clave)),h=Po(i),k=a.map((d,b)=>d?b:-1).filter(d=>d>=0);if(k.length===0){for(let d=0;d<s.length;d++)
144
145
  n.tokensEntrada+=g[d],n.entradaUsd+=g[d]/1e3*r.inputPer1k;return n.totalUsd=n.entradaUsd,this.previo=s,this.previoUltimoBp=-1,this.previoTtl="5m",this.hayPrevio=true,this.atPrevio=e.at,this.acumulado.tokensEntrada+=
145
146
  n.tokensEntrada,this.acumulado.entradaUsd+=n.entradaUsd,this.acumulado.totalUsd+=n.totalUsd,n}let l=-1;for(let d of k){let b=h[d]-(Co-1);for(let x=d;x>=0&&h[x]>=b;x--)if(this.cache.viva(u[x],e.at)){x>
146
- l&&(l=x);break}}for(let d=0;d<=l;d++)this.cache.refrescar(u[d],e.at);let p=k[k.length-1];for(let d of k)d>l&&this.cache.escribir(u[d],e.at,i[d]);let S=0,w=this.hayPrevio&&e.at-this.atPrevio>et[this.previoTtl]?
147
+ l&&(l=x);break}}for(let d=0;d<=l;d++)this.cache.refrescar(u[d],e.at);let p=k[k.length-1];for(let d of k)d>l&&this.cache.escribir(u[d],e.at,a[d]);let w=0,E=this.hayPrevio&&e.at-this.atPrevio>et[this.previoTtl]?
147
148
  []:this.previo.slice(l+1,this.previoUltimoBp+1),C=0;for(let d=0;d<s.length;d++){let b=s[d],x=g[d];if(d<=l){n.tokensLeidos+=x,n.lecturaUsd+=x/1e3*r.lecturaPer1k;continue}if(d>p){n.tokensEntrada+=x,n.entradaUsd+=
148
- x/1e3*r.inputPer1k;continue}for(;k[S]<d;)S+=1;let H=i[k[S]]==="1h"?r.escritura1hPer1k:r.escrituraPer1k;n.tokensEscritos+=x,n.escrituraUsd+=x/1e3*H;let A=C;for(;A<w.length&&w[A].clave!==b.clave;)A+=1;A<
149
- w.length&&(w[A].bytes===b.bytes&&(n.tokensInvalidados+=x,n.invalidacionUsd+=x/1e3*(H-r.lecturaPer1k)),C=A+1);}return this.previo=s,this.previoUltimoBp=p,this.previoTtl=i[p]??"5m",this.hayPrevio=true,this.
149
+ x/1e3*r.inputPer1k;continue}for(;k[w]<d;)w+=1;let H=a[k[w]]==="1h"?r.escritura1hPer1k:r.escrituraPer1k;n.tokensEscritos+=x,n.escrituraUsd+=x/1e3*H;let T=C;for(;T<E.length&&E[T].clave!==b.clave;)T+=1;T<
150
+ E.length&&(E[T].bytes===b.bytes&&(n.tokensInvalidados+=x,n.invalidacionUsd+=x/1e3*(H-r.lecturaPer1k)),C=T+1);}return this.previo=s,this.previoUltimoBp=p,this.previoTtl=a[p]??"5m",this.hayPrevio=true,this.
150
151
  atPrevio=e.at,n.totalUsd=n.lecturaUsd+n.escrituraUsd+n.entradaUsd,this.acumulado.lecturaUsd+=n.lecturaUsd,this.acumulado.escrituraUsd+=n.escrituraUsd,this.acumulado.entradaUsd+=n.entradaUsd,this.acumulado.
151
152
  totalUsd+=n.totalUsd,this.acumulado.tokensLeidos+=n.tokensLeidos,this.acumulado.tokensEscritos+=n.tokensEscritos,this.acumulado.tokensEntrada+=n.tokensEntrada,this.acumulado.tokensInvalidados+=n.tokensInvalidados,
152
- this.acumulado.invalidacionUsd+=n.invalidacionUsd,n}};function on(t){return t.map(e=>({clave:e.clave,bytes:e.bytes}))}var Mo=64,Te=class{cadenas=new Map;cachesPorModelo=new Map;observar(e){let n=e.uso.cache_creation_input_tokens??(e.uso.cache_creation?.ephemeral_5m_input_tokens??0)+(e.uso.cache_creation?.ephemeral_1h_input_tokens??
153
+ this.acumulado.invalidacionUsd+=n.invalidacionUsd,n}};function on(t){return t.map(e=>({clave:e.clave,bytes:e.bytes}))}var Lo=64,Ae=class{cadenas=new Map;cachesPorModelo=new Map;observar(e){let n=e.uso.cache_creation_input_tokens??(e.uso.cache_creation?.ephemeral_5m_input_tokens??0)+(e.uso.cache_creation?.ephemeral_1h_input_tokens??
153
154
  0),o=(e.uso.input_tokens??0)+(e.uso.cache_read_input_tokens??0)+n;if(o<=0)return null;let r={bloques:tn(e.cuerpo),at:e.at,model:e.model,entradaReal:o,escritura1h:(e.uso.cache_creation?.ephemeral_1h_input_tokens??
154
- 0)>0};if(r.bloques.length===0)return null;let s=this.cadenaPara(this.clave(e),e.model),a=s.base.paso(r),i=s.v0.paso(r);return a.totalUsd<=0&&i.totalUsd<=0?null:{baselineUsd:a.totalUsd,politicaUsd:i.totalUsd}}clave(e){
155
+ 0)>0};if(r.bloques.length===0)return null;let s=this.cadenaPara(this.clave(e),e.model),i=s.base.paso(r),a=s.v0.paso(r);return i.totalUsd<=0&&a.totalUsd<=0?null:{baselineUsd:i.totalUsd,politicaUsd:a.totalUsd}}clave(e){
155
156
  if(e.sessionId)return `s:${e.sessionId}\xB7a:${e.agentId??""}\xB7${e.model}`;let n=Array.isArray(e.cuerpo.messages)?e.cuerpo.messages[0]:void 0;return `m:${createHash("sha256").update(JSON.stringify(n??null)).digest(
156
157
  "hex").slice(0,16)}\xB7${e.model}`}cachesDe(e){let n=this.cachesPorModelo.get(e);return n||(n={base:new te,v0:new te},this.cachesPorModelo.set(e,n)),n}cadenaPara(e,n){let o=this.cadenas.get(e);if(!o){
157
- let r=this.cachesDe(n);if(o={base:new pe(s=>on(s),r.base),v0:new pe((s,a,i)=>nn("v0",s,a,i),r.v0),ultimoUso:0},this.cadenas.set(e,o),this.cadenas.size>Mo){let s=null,a=1/0;for(let[i,c]of this.cadenas)
158
- c.ultimoUso<a&&(a=c.ultimoUso,s=i);s!==null&&this.cadenas.delete(s);}}return o.ultimoUso=Date.now(),o}};var _e=class{url;clave;ttlMs;fetchImpl;log;features=new Set;validoHasta=0;enVuelo=null;avisado=false;constructor(e){this.url=e.url,this.clave=e.serviceAccountKey,this.ttlMs=e.ttlMs??3e5,this.fetchImpl=e.
158
+ let r=this.cachesDe(n);if(o={base:new pe(s=>on(s),r.base),v0:new pe((s,i,a)=>nn("v0",s,i,a),r.v0),ultimoUso:0},this.cadenas.set(e,o),this.cadenas.size>Lo){let s=null,i=1/0;for(let[a,c]of this.cadenas)
159
+ c.ultimoUso<i&&(i=c.ultimoUso,s=a);s!==null&&this.cadenas.delete(s);}}return o.ultimoUso=Date.now(),o}};var _e=class{url;clave;ttlMs;fetchImpl;log;features=new Set;validoHasta=0;enVuelo=null;avisado=false;constructor(e){this.url=e.url,this.clave=e.serviceAccountKey,this.ttlMs=e.ttlMs??3e5,this.fetchImpl=e.
159
160
  fetchImpl??fetch,this.log=e.log??(()=>{}),this.refrescar();}tiene(e){return Date.now()>this.validoHasta&&this.enVuelo===null&&this.refrescar(),this.features.has(e)}refrescar(){return this.enVuelo?this.
160
161
  enVuelo:(this.enVuelo=this.pedir().finally(()=>{this.enVuelo=null;}),this.enVuelo)}async pedir(){try{let e=await this.fetchImpl(this.url,{method:"GET",headers:{authorization:`Bearer ${this.clave}`},signal:AbortSignal.
161
- timeout(1e4)});if(!e.ok){this.cerrar(`entitlements HTTP ${e.status}`);return}let n=await e.json();if(!Array.isArray(n.features)||!n.features.every(r=>typeof r=="string")){this.cerrar("entitlements con\
162
- forma inesperada");return}this.features=new Set(n.features);let o=typeof n.refreshAfterSeconds=="number"&&n.refreshAfterSeconds>0?Math.min(n.refreshAfterSeconds*1e3,this.ttlMs):this.ttlMs;this.validoHasta=
163
- Date.now()+o,this.avisado=!1;}catch(e){this.cerrar(String(e));}}cerrar(e){this.features=new Set,this.validoHasta=Date.now()+Math.min(6e4,this.ttlMs),this.avisado||(this.avisado=true,this.log(`[gateway] en\
164
- titlements no disponibles (${e}) \u2014 palanca apagada (fail-closed)`));}};function rn(t){let e=0,n=0;for(;;){let r=t.indexOf('"input_schema"',n);if(r<0)break;e+=1,n=r+14;}let o=0;for(n=0;;){let r=t.indexOf('"tool_reference"',n);if(r<0)break;o+=1,n=r+16;}return {definiciones:e,
165
- referencias:o}}var Lo=40;function sn(t){return t.referencias>0||t.definiciones<Lo?null:`[bvsala gateway] tu cliente manda ${t.definiciones} definiciones de herramientas ENTERAS en cada petici\xF3n y no u\
166
- sa la b\xFAsqueda de herramientas. Con un proxy delante, Claude Code la apaga por defecto: \`bvsala claude\` la reactiva, pero solo en una VENTANA NUEVA. Si lanzaste el terminal a mano, exporta ENABLE_TOOL\
167
- _SEARCH=true. En el corpus de la casa ese bloque era el 24,5 % del dinero de entrada.`}var tt=5*1024*1024,jo=["authorization","x-api-key","anthropic-version","anthropic-beta"];function an(t,e,n,o=""){return createHash("sha256").update(t).update(`
162
+ timeout(1e4)});if(!e.ok){this.cerrar(`entitlements HTTP ${e.status}`);return}let n=await e.json();if(!Array.isArray(n.features)||!n.features.every(r=>typeof r=="string")){this.cerrar("entitlements res\
163
+ ponse has an unexpected shape");return}this.features=new Set(n.features);let o=typeof n.refreshAfterSeconds=="number"&&n.refreshAfterSeconds>0?Math.min(n.refreshAfterSeconds*1e3,this.ttlMs):this.ttlMs;
164
+ this.validoHasta=Date.now()+o,this.avisado=!1;}catch(e){this.cerrar(String(e));}}cerrar(e){this.features=new Set,this.validoHasta=Date.now()+Math.min(6e4,this.ttlMs),this.avisado||(this.avisado=true,this.
165
+ log(`[gateway] entitlements unavailable (${e}) \u2014 savings features disabled (fail-closed); traffic keeps flowing`));}};function rn(t){let e=0,n=0;for(;;){let r=t.indexOf('"input_schema"',n);if(r<0)break;e+=1,n=r+14;}let o=0;for(n=0;;){let r=t.indexOf('"tool_reference"',n);if(r<0)break;o+=1,n=r+16;}return {definiciones:e,
166
+ referencias:o}}var Mo=40;function sn(t){return t.referencias>0||t.definiciones<Mo?null:`[bvsala gateway] your client sends ${t.definiciones} FULL tool definitions in every request and does not use too\
167
+ l search. With a proxy in front, Claude Code turns tool search off by default: \`bvsala claude\` turns it back on, but only in a NEW WINDOW. If you launched the terminal by hand, export ENABLE_TOOL_SEAR\
168
+ CH=true. In our own corpus that block was 24.5% of input spend.`}var tt=5*1024*1024,No=["authorization","x-api-key","anthropic-version","anthropic-beta"];function an(t,e,n,o=""){return createHash("sha256").update(t).update(`
168
169
  `).update(e).update(`
169
170
  `).update(o).update(`
170
- `).update(n).digest("hex")}function cn(t){return jo.map(e=>`${e}=${t(e)??""}`).join(`
171
- `)}var Ce=class{enVuelo=new Map;primarias=0;gemelas=0;registrar(e){if(this.enVuelo.has(e))return null;this.primarias+=1;let n,o=new Promise(i=>n=i);this.enVuelo.set(e,{promesa:o,resolver:n});let r=[],
172
- s=0,a=false;return {capturar:i=>{if(!a){if(s+=i.length,s>tt){a=true,r.length=0;return}r.push(i);}},terminar:(i,c)=>{let m=!a&&i>=200&&i<300;this.enVuelo.get(e)?.resolver(m?{status:i,headers:c,chunks:r}:null),
171
+ `).update(n).digest("hex")}function cn(t){return No.map(e=>`${e}=${t(e)??""}`).join(`
172
+ `)}var Ce=class{enVuelo=new Map;primarias=0;gemelas=0;registrar(e){if(this.enVuelo.has(e))return null;this.primarias+=1;let n,o=new Promise(a=>n=a);this.enVuelo.set(e,{promesa:o,resolver:n});let r=[],
173
+ s=0,i=false;return {capturar:a=>{if(!i){if(s+=a.length,s>tt){i=true,r.length=0;return}r.push(a);}},terminar:(a,c)=>{let m=!i&&a>=200&&a<300;this.enVuelo.get(e)?.resolver(m?{status:a,headers:c,chunks:r}:null),
173
174
  this.enVuelo.delete(e);},abortar:()=>{this.enVuelo.get(e)?.resolver(null),this.enVuelo.delete(e);}}}async esperar(e){let n=this.enVuelo.get(e);if(!n)return null;let o=await n.promesa;return o&&(this.gemelas+=
174
- 1),o}tieneEnVuelo(e){return this.enVuelo.has(e)}};var Bo=["content-encoding","content-length","transfer-encoding"],Uo=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding",
175
+ 1),o}tieneEnVuelo(e){return this.enVuelo.has(e)}};var zo=["content-encoding","content-length","transfer-encoding"],Uo=new Set(["connection","keep-alive","proxy-connection","proxy-authenticate","proxy-authorization","te","trailer","transfer-encoding",
175
176
  "upgrade","expect"]);function ne(t){let e=t?.cause;if(e==null)return String(t);let n=e instanceof AggregateError&&e.errors.length>0?e.errors.map(o=>String(o)).join("; "):String(e);return `${String(t)} \
176
- \xB7 causa: ${n}`}var nt=class extends Error{code;puerto;constructor(e,n,o){super(e==="EADDRINUSE"?`el puerto ${n} ya est\xE1 en uso: no se pudo arrancar el gateway`:e==="EACCES"?`el puerto ${n} no se\
177
- puede abrir: puerto sin permiso`:`no se pudo escuchar en 127.0.0.1:${n} (${e})`,{cause:o}),this.name="ErrorDeEscucha",this.code=e,this.puerto=n;}};async function ln(t={}){let e=(t.upstream??"https://a\
178
- pi.anthropic.com").replace(/\/+$/,""),n=t.fetchImpl??fetch,o=t.log??(l=>console.log(l)),r;if(t.serviceAccountKey){r=new ee({url:t.meteringUrl??"https://savings.bivelio.com/api/metering/usage",serviceAccountKey:t.
179
- serviceAccountKey,surface:"gateway",...t.fetchImpl?{fetchImpl:t.fetchImpl}:{},onAuthError:p=>O("gateway-ingest-auth",`[bvsala gateway] la ingesta de uso rechaza la credencial (${p}). El tr\xE1fico sigue \
180
- fluyendo (avisar, nunca romper), pero este uso no est\xE1 llegando al panel. Revisa BIVELIO_SERVICE_KEY y el estado de la suscripci\xF3n en savings.bivelio.com.`)});let l=Ee();t.seatId?r.setSeat(t.seatId,
181
- l):we(t.serviceAccountKey).then(p=>r?.setSeat(p,l)).catch(()=>{}),r.start();}else O("gateway-sin-clave","[bvsala gateway] sin BIVELIO_SERVICE_KEY: el gateway reenv\xEDa el tr\xE1fico pero NO mide. Consigue t\
182
- u clave en savings.bivelio.com/dashboard/install para ver el gasto en tu panel.");let s=t.coalescer?new Ce:null,a=t.sombra&&r?new Te:null,i=new ye,c=t.elision!==false&&t.serviceAccountKey?new _e({url:new URL(
183
- "/api/org/entitlements",t.meteringUrl??"https://savings.bivelio.com/api/metering/usage").toString(),serviceAccountKey:t.serviceAccountKey,...t.fetchImpl?{fetchImpl:t.fetchImpl}:{},log:o}):null,m=l=>l===
184
- "/v1/messages"?new W:l.endsWith("/responses")?new Ae:null,g=(l,p,S,D,w,C,d,b,x,H,A)=>{if(!r||!S)return;let $=r,se=S,T=qt(p,C);if(!T){O(`gateway-sin-modelo:${w}`,`[bvsala gateway] no se pudo leer el mo\
185
- delo del cuerpo de ${w} (content-encoding: ${C??"ninguno"}); su uso se reenv\xEDa pero no se mide.`);return}let I=!D&&A?A.servido:T,P=Ht(I,S);if(P){let X=D?Gt(l,T,P,d):Jt(l,I,P,d,b,x,A);r.record(X);let Z=X.
186
- cost.eliminatedInputFloorCost;o(`[gateway] POST ${w} ${D?"\xB7 COALESCIDA (llamada evitada)":""} \xB7 ${I!==T?`${T}\u2192${I} (modelo hermano)`:T} \xB7 $${P.costeUsd.toFixed(6)} (in ${P.tokens.entrada}\
187
- , cache\u2191${P.tokens.cacheEscrita}${P.tokens.cacheEscrita1h?` (1h ${P.tokens.cacheEscrita1h})`:""} cache\u2193${P.tokens.cacheLeida}, out ${P.tokens.salida})`+(b?` \xB7 elisi\xF3n: ${b.borrados} bo\
188
- rrado(s), ${b.tokensLimpiados} tok limpiados por el proveedor`+(Z!==void 0?` \xB7 suelo $${Z.toFixed(6)} (lectura de cach\xE9)`:b.tokensLimpiadosToolUses>0?b.atribuible?" \xB7 sin precio de lectura en el\
189
- cat\xE1logo: sin suelo":" \xB7 diales del cliente: se cuenta, no se acredita":b.tokensLimpiados>0?" \xB7 solo thinking (config del cliente): se cuenta, no se acredita":""):""));let q=b?.atribuible===
190
- true&&b.tokensLimpiadosToolUses>0;a&&!D&&w==="/v1/messages"&&setImmediate(()=>{try{let E=Qe(p,C);if(!E||typeof E!="object")return;let y=a.observar({cuerpo:E,uso:se,model:T,at:H??Date.now(),...d?.sessionId!==
191
- void 0?{sessionId:d.sessionId}:{},...d?.agentId!==void 0?{agentId:d.agentId}:{}});if(!y)return;if(q){o(`[gateway] sombra \xB7 ${T} \xB7 el proveedor ya borr\xF3 por nuestros diales: la cadena avanza, no se \
192
- emite`);return}$.record(Wt(`${l}:shadow`,T,y.baselineUsd,y.politicaUsd,d)),o(`[gateway] sombra \xB7 ${T} \xB7 baseline $${y.baselineUsd.toFixed(6)} \u2192 v0 $${y.politicaUsd.toFixed(6)}`);}catch{}});}else
193
- O(`gateway-sin-precio:${T}`,`[bvsala gateway] el modelo "${T}" no est\xE1 en el cat\xE1logo de precios; su uso se reenv\xEDa pero no se mide.`);},u=createServer(async(l,p)=>{let S=randomUUID(),D=Date.now(),w=new AbortController;
194
- p.on("close",()=>{p.writableEnded||w.abort();});let C=null,d=new URL(l.url??"/",e),b=null,x=Buffer.alloc(0),H=false,A=null,$=null,se=(E,y)=>{if(E===null||A===null||!A.nuestros)return E;let K=y?.cache_creation,
195
- je=y?.cache_creation_input_tokens??(K?.ephemeral_5m_input_tokens??0)+(K?.ephemeral_1h_input_tokens??0),ae=(y?.input_tokens??0)+(y?.cache_read_input_tokens??0)+je+E.tokensLimpiados,ie=A.triggerDelCliente!==
196
- null&&ae>=A.triggerDelCliente;return {...E,atribuible:!ie}},T=l.headers["content-encoding"],I=Array.isArray(T)?T[0]:T,P=E=>{let y=Array.isArray(E)?E[0]:E;return y!==void 0&&/^[A-Za-z0-9._:/-]{1,128}$/.
197
- test(y)?y:void 0},X=P(l.headers["x-claude-code-session-id"]),Z=P(l.headers["x-claude-code-agent-id"]),q=X!==void 0||Z!==void 0?{...X!==void 0?{sessionId:X}:{},...Z!==void 0?{agentId:Z}:{}}:void 0;try{
198
- let E=[];for await(let f of l)E.push(f);let y=Buffer.concat(E);x=y,d=new URL(l.url??"/",e);let K=s!==null&&l.method==="POST"&&y.length>0,je=cn(f=>{let v=l.headers[f];return Array.isArray(v)?v.join(", "):
199
- v}),ae=K?an("POST",d.pathname+d.search,y,je):"";if(K)if(s.tieneEnVuelo(ae)){let f=await s.esperar(ae);if(f){p.writeHead(f.status,f.headers);let v=m(d.pathname)??new W;for(let ue of f.chunks)p.write(ue),
200
- v.observar(ue);p.end();let le=v.usage()??(d.pathname==="/v1/messages"?Xe(Buffer.concat(f.chunks)):null);g(S,y,le,!0,d.pathname,I,q);return}}else C=s.registrar(ae);let ie=l.headers.connection,On=new Set(
201
- (Array.isArray(ie)?ie.join(","):ie??"").split(",").map(f=>f.trim().toLowerCase()).filter(f=>f!=="")),V={};for(let[f,v]of Object.entries(l.headers))f==="host"||f==="content-length"||v===void 0||Uo.has(
202
- f)||On.has(f)||(V[f]=Array.isArray(v)?v.join(", "):v);let Dn={...V},M=y;if(c!==null&&t.dialesElision===!0&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("context_elision"))try{let f=Et(y,I);
203
- f!==null&&(M=f.cuerpo,A={nuestros:f.nuestros,triggerDelCliente:f.triggerDelCliente},V["anthropic-beta"]=At(V["anthropic-beta"]));}catch{}if(l.method==="POST"&&d.pathname==="/v1/messages"&&(I??"")==="")
204
- try{let f=sn(rn(y.toString("utf8")));f&&O("gateway-busqueda-herramientas",f);}catch{}let bt=c!==null&&t.modeloHermano===!0&&l.method==="POST"&&(d.pathname==="/v1/messages"||d.pathname==="/v1/messages/c\
205
- ount_tokens");if(bt&&!c.tiene("sibling_model_routing")&&O("gateway-hermano-sin-feature","[bvsala gateway] modelo hermano (C): la bandera est\xE1 puesta pero la licencia de la org NO trae sibling_model_ro\
206
- uting; la petici\xF3n se reenv\xEDa intacta (fail-closed)."),bt&&c.tiene("sibling_model_routing"))try{let v=jt(M,M===y?I:"identity",{betas:V["anthropic-beta"]});v!==null&&(M=v.cuerpo,$=v.cambio);}catch{}
207
- let he=!1;if(c!==null&&Ge(t.esfuerzo)&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("per_turn_effort"))try{let f=M===y?I:"identity",v=Pt(M,t.esfuerzo,f);v!==null&&(M=v.cuerpo,V["anthropic-b\
208
- eta"]=Ot(V["anthropic-beta"]),he=!0);}catch{}let ge=null;if(c!==null&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("cache_ttl_tuning"))try{let v=J(M,M===y?I:"identity");if(v!==null){let le=Tt(
209
- v),ue=`${q?.sessionId??"sin-sesion"}:${le.system}`,kt=Date.now(),Mn=i.ultima(ue,kt);if(i.anotar(ue,le,kt),Ct({fraccionNueva:_t(Mn,le)})){let be=Rt(v);be!==null&&(M=be.cuerpo,ge={rebajadas:be.rebajadas,
210
- yaEran5m:be.yaEran5m});}}}catch{}let _;try{_=await n(d,{method:l.method,headers:V,body:["GET","HEAD"].includes(l.method??"")?void 0:M,redirect:"manual",signal:w.signal});}catch(f){C?.abortar(),p.headersSent||
211
- (p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: upstream unreachable",detail:ne(f)}))),o(`[gateway] ${l.method} ${d.pathname} \u2192 upstream inalcanzable\
212
- : ${ne(f)}`);return}if(($!==null||he)&&_.status===400&&!w.signal.aborted){let f="";try{f=(await _.text()).slice(0,300).replace(/\s+/g," ");}catch{}o(`[gateway] el upstream rechaz\xF3 (400) la petici\xF3n ree\
213
- scrita${$?` a ${$.servido}`:""}${he?" (esfuerzo por turno)":""}; se reenv\xEDa intacta \xB7 ${f}`),$=null,he=!1,ge=null,A=null;try{_=await n(d,{method:l.method,headers:Dn,body:y,redirect:"manual",signal:w.
177
+ \xB7 cause: ${n}`}var nt=class extends Error{code;puerto;constructor(e,n,o){super(e==="EADDRINUSE"?`port ${n} is already in use: the gateway could not start`:e==="EACCES"?`port ${n} cannot be opened: \
178
+ permission denied`:`could not listen on 127.0.0.1:${n} (${e})`,{cause:o}),this.name="ErrorDeEscucha",this.code=e,this.puerto=n;}};async function ln(t={}){let e=(t.upstream??"https://api.anthropic.com").
179
+ replace(/\/+$/,""),n=t.fetchImpl??fetch,o=t.log??(l=>console.log(l)),r;if(t.serviceAccountKey){r=new ee({url:t.meteringUrl??"https://savings.bivelio.com/api/metering/usage",serviceAccountKey:t.serviceAccountKey,
180
+ surface:"gateway",...t.fetchImpl?{fetchImpl:t.fetchImpl}:{},onAuthError:p=>P("gateway-ingest-auth",`[bvsala gateway] the usage ingest is rejecting the credential (${p}). Traffic keeps flowing (warn, n\
181
+ ever break), but this usage is not reaching your dashboard. Check BIVELIO_SERVICE_KEY and your subscription status at savings.bivelio.com.`)});let l=Se();t.seatId?r.setSeat(t.seatId,l):Ee(t.serviceAccountKey).
182
+ then(p=>r?.setSeat(p,l)).catch(()=>{}),r.start();}else P("gateway-sin-clave","[bvsala gateway] no BIVELIO_SERVICE_KEY: the gateway forwards traffic but does not meter it. Get your key at savings.biveli\
183
+ o.com/dashboard/install to see your spend in the dashboard.");let s=t.coalescer?new Ce:null,i=t.sombra&&r?new Ae:null,a=new ye,c=t.elision!==false&&t.serviceAccountKey?new _e({url:new URL("/api/org/entit\
184
+ lements",t.meteringUrl??"https://savings.bivelio.com/api/metering/usage").toString(),serviceAccountKey:t.serviceAccountKey,...t.fetchImpl?{fetchImpl:t.fetchImpl}:{},log:o}):null,m=l=>l==="/v1/messages"?
185
+ new G:l.endsWith("/responses")?new Te:null,g=(l,p,w,D,E,C,d,b,x,H,T)=>{if(!r||!w)return;let $=r,se=w,A=qt(p,C);if(!A){P(`gateway-sin-modelo:${E}`,`[bvsala gateway] could not read the model from the ${E}\
186
+ request body (content-encoding: ${C??"none"}); its usage is forwarded but not metered.`);return}let I=!D&&T?T.servido:A,O=Ht(I,w);if(O){let X=D?Jt(l,A,O,d):Wt(l,I,O,d,b,x,T);r.record(X);let Z=X.cost.
187
+ eliminatedInputFloorCost;o(`[gateway] POST ${E} ${D?"\xB7 COALESCED (call avoided)":""} \xB7 ${I!==A?`${A}\u2192${I} (sibling model)`:A} \xB7 $${O.costeUsd.toFixed(6)} (in ${O.tokens.entrada}, cache\u2191${O.
188
+ tokens.cacheEscrita}${O.tokens.cacheEscrita1h?` (1h ${O.tokens.cacheEscrita1h})`:""} cache\u2193${O.tokens.cacheLeida}, out ${O.tokens.salida})`+(b?` \xB7 elision: ${b.borrados} clear(s), ${b.tokensLimpiados}\
189
+ tok cleared by the provider`+(Z!==void 0?` \xB7 floor $${Z.toFixed(6)} (cache read price)`:b.tokensLimpiadosToolUses>0?b.atribuible?" \xB7 no cache read price in the catalog: no floor":" \xB7 your own c\
190
+ ontext_management settings: counted, not credited":b.tokensLimpiados>0?" \xB7 thinking only (your config): counted, not credited":""):""));let q=b?.atribuible===true&&b.tokensLimpiadosToolUses>0;i&&!D&&
191
+ E==="/v1/messages"&&setImmediate(()=>{try{let S=Qe(p,C);if(!S||typeof S!="object")return;let y=i.observar({cuerpo:S,uso:se,model:A,at:H??Date.now(),...d?.sessionId!==void 0?{sessionId:d.sessionId}:{},
192
+ ...d?.agentId!==void 0?{agentId:d.agentId}:{}});if(!y)return;if(q){o(`[gateway] shadow estimate \xB7 ${A} \xB7 the provider already cleared context via our settings: chain advances, nothing emitted`);
193
+ return}$.record(Gt(`${l}:shadow`,A,y.baselineUsd,y.politicaUsd,d)),o(`[gateway] shadow estimate \xB7 ${A} \xB7 baseline $${y.baselineUsd.toFixed(6)} \u2192 with elision $${y.politicaUsd.toFixed(6)}`);}catch{}});}else
194
+ P(`gateway-sin-precio:${A}`,`[bvsala gateway] model "${A}" is not in the price catalog; its usage is forwarded but not metered.`);},u=createServer(async(l,p)=>{let w=randomUUID(),D=Date.now(),E=new AbortController;p.on(
195
+ "close",()=>{p.writableEnded||E.abort();});let C=null,d=new URL(l.url??"/",e),b=null,x=Buffer.alloc(0),H=false,T=null,$=null,se=(S,y)=>{if(S===null||T===null||!T.nuestros)return S;let K=y?.cache_creation,
196
+ Ne=y?.cache_creation_input_tokens??(K?.ephemeral_5m_input_tokens??0)+(K?.ephemeral_1h_input_tokens??0),ie=(y?.input_tokens??0)+(y?.cache_read_input_tokens??0)+Ne+S.tokensLimpiados,ae=T.triggerDelCliente!==
197
+ null&&ie>=T.triggerDelCliente;return {...S,atribuible:!ae}},A=l.headers["content-encoding"],I=Array.isArray(A)?A[0]:A,O=S=>{let y=Array.isArray(S)?S[0]:S;return y!==void 0&&/^[A-Za-z0-9._:/-]{1,128}$/.
198
+ test(y)?y:void 0},X=O(l.headers["x-claude-code-session-id"]),Z=O(l.headers["x-claude-code-agent-id"]),q=X!==void 0||Z!==void 0?{...X!==void 0?{sessionId:X}:{},...Z!==void 0?{agentId:Z}:{}}:void 0;try{
199
+ let S=[];for await(let f of l)S.push(f);let y=Buffer.concat(S);x=y,d=new URL(l.url??"/",e);let K=s!==null&&l.method==="POST"&&y.length>0,Ne=cn(f=>{let v=l.headers[f];return Array.isArray(v)?v.join(", "):
200
+ v}),ie=K?an("POST",d.pathname+d.search,y,Ne):"";if(K)if(s.tieneEnVuelo(ie)){let f=await s.esperar(ie);if(f){p.writeHead(f.status,f.headers);let v=m(d.pathname)??new G;for(let ue of f.chunks)p.write(ue),
201
+ v.observar(ue);p.end();let le=v.usage()??(d.pathname==="/v1/messages"?Xe(Buffer.concat(f.chunks)):null);g(w,y,le,!0,d.pathname,I,q);return}}else C=s.registrar(ie);let ae=l.headers.connection,Pn=new Set(
202
+ (Array.isArray(ae)?ae.join(","):ae??"").split(",").map(f=>f.trim().toLowerCase()).filter(f=>f!=="")),V={};for(let[f,v]of Object.entries(l.headers))f==="host"||f==="content-length"||v===void 0||Uo.has(
203
+ f)||Pn.has(f)||(V[f]=Array.isArray(v)?v.join(", "):v);let Dn={...V},L=y;if(c!==null&&t.dialesElision===!0&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("context_elision"))try{let f=St(y,I);
204
+ f!==null&&(L=f.cuerpo,T={nuestros:f.nuestros,triggerDelCliente:f.triggerDelCliente},V["anthropic-beta"]=Tt(V["anthropic-beta"]));}catch{}if(l.method==="POST"&&d.pathname==="/v1/messages"&&(I??"")==="")
205
+ try{let f=sn(rn(y.toString("utf8")));f&&P("gateway-busqueda-herramientas",f);}catch{}let bt=c!==null&&t.modeloHermano===!0&&l.method==="POST"&&(d.pathname==="/v1/messages"||d.pathname==="/v1/messages/c\
206
+ ount_tokens");if(bt&&!c.tiene("sibling_model_routing")&&P("gateway-hermano-sin-feature","[bvsala gateway] sibling model routing: the flag is set but your org's license does not include sibling_model_r\
207
+ outing; the request is forwarded unchanged (fail-closed)."),bt&&c.tiene("sibling_model_routing"))try{let v=Nt(L,L===y?I:"identity",{betas:V["anthropic-beta"]});v!==null&&(L=v.cuerpo,$=v.cambio);}catch{}
208
+ let he=!1;if(c!==null&&Je(t.esfuerzo)&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("per_turn_effort"))try{let f=L===y?I:"identity",v=Ot(L,t.esfuerzo,f);v!==null&&(L=v.cuerpo,V["anthropic-b\
209
+ eta"]=Pt(V["anthropic-beta"]),he=!0);}catch{}let ge=null;if(c!==null&&l.method==="POST"&&d.pathname==="/v1/messages"&&c.tiene("cache_ttl_tuning"))try{let v=W(L,L===y?I:"identity");if(v!==null){let le=At(
210
+ v),ue=`${q?.sessionId??"sin-sesion"}:${le.system}`,kt=Date.now(),Ln=a.ultima(ue,kt);if(a.anotar(ue,le,kt),Ct({fraccionNueva:_t(Ln,le)})){let be=Rt(v);be!==null&&(L=be.cuerpo,ge={rebajadas:be.rebajadas,
211
+ yaEran5m:be.yaEran5m});}}}catch{}let _;try{_=await n(d,{method:l.method,headers:V,body:["GET","HEAD"].includes(l.method??"")?void 0:L,redirect:"manual",signal:E.signal});}catch(f){C?.abortar(),p.headersSent||
212
+ (p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: upstream unreachable",detail:ne(f)}))),o(`[gateway] ${l.method} ${d.pathname} \u2192 upstream unreachable:\
213
+ ${ne(f)}`);return}if(($!==null||he)&&_.status===400&&!E.signal.aborted){let f="";try{f=(await _.text()).slice(0,300).replace(/\s+/g," ");}catch{}o(`[gateway] the upstream rejected (400) the rewritten \
214
+ request${$?` to ${$.servido}`:""}${he?" (per-turn effort)":""}; forwarding it unchanged \xB7 ${f}`),$=null,he=!1,ge=null,T=null;try{_=await n(d,{method:l.method,headers:Dn,body:y,redirect:"manual",signal:E.
214
215
  signal});}catch(v){C?.abortar(),p.headersSent||(p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: upstream unreachable",detail:ne(v)}))),o(`[gateway] ${l.
215
- method} ${d.pathname} \u2192 upstream inalcanzable en el reintento: ${ne(v)}`);return}}let Ne=Object.fromEntries(_.headers.entries());for(let f of Bo)delete Ne[f];p.writeHead(_.status,Ne);let vt=_.headers.
216
- get("content-type")??"",ze=vt.includes("text/event-stream")||vt===""&&_.ok&&m(d.pathname)!==null;b=ze?m(d.pathname):null;let ce=!ze&&_.ok&&r!==void 0&&l.method==="POST"&&d.pathname==="/v1/messages"&&(_.
217
- headers.get("content-type")??"").includes("application/json")?[]:null,yt=0;if(_.body)for await(let f of _.body){let v=Buffer.from(f);p.write(v),b?.observar(v),ce&&(yt+=v.length,yt>tt?ce=null:ce.push(v)),
218
- C?.capturar(v);}if(p.end(),C?.terminar(_.status,Ne),b&&_.ok){H=!0;let f=b.usage(),v=se(b instanceof W?b.elision():null,f);g(S,y,f,!1,d.pathname,I,q,v,ge,D,$),r||o(`[gateway] ${l.method} ${d.pathname} \u2192\
219
- ${_.status} (stream) \xB7 no medido: sin BIVELIO_SERVICE_KEY`);}else if(ce){let f=Buffer.concat(ce),v=Xe(f);g(S,y,v,!1,d.pathname,I,q,se(Vt(f),v),ge,D,$);}else o(`[gateway] ${l.method} ${d.pathname} \u2192 ${_.
220
- status}${ze?" (stream)":""}`);}catch(E){if(C?.abortar(),w.signal.aborted){let y=!H&&b?b.usageFinal():null;if(y){o(`[gateway] ${l.method} ${d.pathname} \u2192 el cliente cerr\xF3 tras el usage final; se mide ig\
221
- ual`);let K=se(b instanceof W?b.elision():null,y);g(S,x,y,false,d.pathname,I,q,K,void 0,D,$);}else o(`[gateway] ${l.method} ${d.pathname} \u2192 el cliente abort\xF3; upstream cancelado`);}else if(o(`[gate\
222
- way] ${l.method} ${d.pathname} \u2192 error sirviendo: ${ne(E)}`),!p.headersSent){p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: stream interrupted",
223
- detail:ne(E)}));return}p.writableEnded||p.end();}});u.on("upgrade",(l,p)=>{o(`[gateway] ${l.method} ${new URL(l.url??"/","http://x").pathname} \u2192 426 (WebSocket no soportado; el cliente cae a HTTP)`),
224
- p.once("error",()=>{}),p.once("finish",()=>p.destroy()),p.end(`HTTP/1.1 426 Upgrade Required\r
216
+ method} ${d.pathname} \u2192 upstream unreachable on retry: ${ne(v)}`);return}}let je=Object.fromEntries(_.headers.entries());for(let f of zo)delete je[f];p.writeHead(_.status,je);let vt=_.headers.get(
217
+ "content-type")??"",Be=vt.includes("text/event-stream")||vt===""&&_.ok&&m(d.pathname)!==null;b=Be?m(d.pathname):null;let ce=!Be&&_.ok&&r!==void 0&&l.method==="POST"&&d.pathname==="/v1/messages"&&(_.headers.
218
+ get("content-type")??"").includes("application/json")?[]:null,yt=0;if(_.body)for await(let f of _.body){let v=Buffer.from(f);p.write(v),b?.observar(v),ce&&(yt+=v.length,yt>tt?ce=null:ce.push(v)),C?.capturar(
219
+ v);}if(p.end(),C?.terminar(_.status,je),b&&_.ok){H=!0;let f=b.usage(),v=se(b instanceof G?b.elision():null,f);g(w,y,f,!1,d.pathname,I,q,v,ge,D,$),r||o(`[gateway] ${l.method} ${d.pathname} \u2192 ${_.status}\
220
+ (stream) \xB7 not metered: no BIVELIO_SERVICE_KEY`);}else if(ce){let f=Buffer.concat(ce),v=Xe(f);g(w,y,v,!1,d.pathname,I,q,se(Vt(f),v),ge,D,$);}else o(`[gateway] ${l.method} ${d.pathname} \u2192 ${_.status}${Be?
221
+ " (stream)":""}`);}catch(S){if(C?.abortar(),E.signal.aborted){let y=!H&&b?b.usageFinal():null;if(y){o(`[gateway] ${l.method} ${d.pathname} \u2192 client closed after the final usage; metered anyway`);let K=se(
222
+ b instanceof G?b.elision():null,y);g(w,x,y,false,d.pathname,I,q,K,void 0,D,$);}else o(`[gateway] ${l.method} ${d.pathname} \u2192 client aborted; upstream cancelled`);}else if(o(`[gateway] ${l.method} ${d.
223
+ pathname} \u2192 error while serving: ${ne(S)}`),!p.headersSent){p.writeHead(502,{"content-type":"application/json"}),p.end(JSON.stringify({error:"bvsala gateway: stream interrupted",detail:ne(S)}));return}
224
+ p.writableEnded||p.end();}});u.on("upgrade",(l,p)=>{o(`[gateway] ${l.method} ${new URL(l.url??"/","http://x").pathname} \u2192 426 (WebSocket not supported; the client falls back to HTTP)`),p.once("err\
225
+ or",()=>{}),p.once("finish",()=>p.destroy()),p.end(`HTTP/1.1 426 Upgrade Required\r
225
226
  connection: close\r
226
227
  content-length: 0\r
227
228
  \r
228
229
  `);});let h=t.puerto??8402;try{await new Promise((l,p)=>{u.once("error",p),u.listen(h,"127.0.0.1",()=>l());});}catch(l){r?.stop();let p=l?.code;throw new nt(typeof p=="string"?p:"EUNKNOWN",h,l)}return {puerto:u.
229
230
  address().port,cerrar:async()=>{await r?.stopAndDrain(),await new Promise(l=>u.close(()=>l()));}}}var ot=["claude","codex","goose","qwen"];function Re(t){return ot.includes(t??"")}function dn(t,e={}){let n=e.path??process.env.PATH??"",o=e.
230
- existe??existsSync,r=null;for(let s of n.split(";").filter(a=>a.length>0)){for(let a of [".exe",".com"]){let i=join(s,t+a);if(o(i))return {tipo:"exe",ruta:i}}if(r===null)for(let a of [".cmd",".bat"]){let i=join(s,t+
231
- a);if(o(i)){r=i;break}}}return r!==null?{tipo:"solo-shim",ruta:r}:{tipo:"no-encontrado"}}function pn(t={}){let n=(t.env??process.env).BVSALA_CODEX_AUTH?.toLowerCase();if(n==="api-key"||n==="apikey")return "\
231
+ existe??existsSync,r=null;for(let s of n.split(";").filter(i=>i.length>0)){for(let i of [".exe",".com"]){let a=join(s,t+i);if(o(a))return {tipo:"exe",ruta:a}}if(r===null)for(let i of [".cmd",".bat"]){let a=join(s,t+
232
+ i);if(o(a)){r=a;break}}}return r!==null?{tipo:"solo-shim",ruta:r}:{tipo:"no-encontrado"}}function pn(t={}){let n=(t.env??process.env).BVSALA_CODEX_AUTH?.toLowerCase();if(n==="api-key"||n==="apikey")return "\
232
233
  api-key";if(n==="chatgpt")return "chatgpt";let o=null;try{o=t.leerAuth?t.leerAuth():null;}catch{o=null;}if(o===null)return "api-key";try{let r=JSON.parse(o);return typeof r.auth_mode=="string"?r.auth_mode.
233
234
  toLowerCase().replace(/[^a-z]/g,"")==="apikey"?"api-key":"chatgpt":r.tokens!==void 0&&r.tokens!==null?"chatgpt":"api-key"}catch{return "api-key"}}var Fo={"api-key":{upstream:"https://api.openai.com",prefijo:"\
234
235
  /v1"},chatgpt:{upstream:"https://chatgpt.com",prefijo:"/backend-api/codex"}};function rt(t,e,n,o={},r=process.env){let s=`http://127.0.0.1:${e}`;switch(t){case "claude":return {bin:"claude",args:n,env:{
235
236
  ANTHROPIC_BASE_URL:s,ENABLE_TOOL_SEARCH:r.ENABLE_TOOL_SEARCH??"true"}};case "goose":return {bin:"goose",args:n,env:{ANTHROPIC_HOST:s}};case "qwen":return {bin:"qwen",args:n,env:{ANTHROPIC_BASE_URL:s},nota:'\
236
- Qwen Code solo enruta por aqu\xED con `security.auth.selectedType: "anthropic"` en ~/.qwen/settings.json (ver `bvsala doctor`).'};case "codex":{let a=o.modoCodex??"api-key",i=Fo[a];return {bin:"codex",args:[
237
- "-c",`openai_base_url="${s}${i.prefijo}"`,...n],env:{},upstream:i.upstream,nota:a==="chatgpt"?"Auth ChatGPT detectada: el gateway enruta a chatgpt.com/backend-api/codex. El tr\xE1fico de suscripci\xF3n se m\
238
- ide (equivalente a precio de lista) pero no se monetiza: no hay factura por tokens.":"Auth por API key: el gateway enruta a api.openai.com/v1."}}}}var Ho=["gateway","doctor","skill"],mn=new Set(["help","--help","-h"]),fn=new Set(["version","--version","-v"]);function hn(t){let[e,...n]=t;return e===void 0||mn.has(e)?{tipo:"ayuda",salida:0}:fn.has(
237
+ Qwen Code only routes through here with `security.auth.selectedType: "anthropic"` in ~/.qwen/settings.json (see `bvsala doctor`).'};case "codex":{let i=o.modoCodex??"api-key",a=Fo[i];return {bin:"codex",
238
+ args:["-c",`openai_base_url="${s}${a.prefijo}"`,...n],env:{},upstream:a.upstream,nota:i==="chatgpt"?"ChatGPT auth detected: the gateway routes to chatgpt.com/backend-api/codex. Subscription traffic is\
239
+ metered (list-price equivalent) but not billed: there is no per-token invoice.":"API key auth: the gateway routes to api.openai.com/v1."}}}}var Ho=["gateway","doctor","skill"],mn=new Set(["help","--help","-h"]),fn=new Set(["version","--version","-v"]);function hn(t){let[e,...n]=t;return e===void 0||mn.has(e)?{tipo:"ayuda",salida:0}:fn.has(
239
240
  e)?{tipo:"version"}:Re(e)?{tipo:"arrancar",comando:e,resto:n}:Ho.includes(e)?n.some(o=>mn.has(o))?{tipo:"ayuda",salida:0}:n.some(o=>fn.has(o))?{tipo:"version"}:{tipo:"arrancar",comando:e,resto:n}:{tipo:"\
240
- ayuda",salida:1}}function gn(t,e,n){let o=t??{},r=typeof o.puerto=="number"?o.puerto:n;if(o.code==="EADDRINUSE")return `el puerto ${r} ya est\xE1 en uso (\xBFotro gateway o un terminal lanzado antes?): usa -\
241
- -puerto ${r+1} o apaga el otro proceso`;if(o.code==="EACCES")return `el puerto ${r} no se puede abrir: puerto sin permiso (EACCES; los puertos por debajo de 1024 piden privilegios): usa --puerto ${Math.
242
- max(1024,r)} o superior`;let s=t instanceof Error?t.message:String(t);return `no se pudo arrancar el gateway de \`bvsala ${e}\`: ${s}`}function qo(t){let e=Number.parseInt(t.slice(8,10),10);return Number.isFinite(e)&&e%2===0?"palanca":"control"}function bn(t){let e;try{e=JSON.parse(t());}catch{return null}if(typeof e!="object"||e===null)return null;let n=e,o=n.espia;return typeof o!="number"||!Number.isInteger(o)||o<1||o>65535?null:{activo:n.
243
- activo!==false,espia:o}}function vn(t){let{enrolamiento:e,espiaVivo:n,upstreamEnv:o,elisionEnv:r,hoyUtcIso:s}=t;if(!e)return {linea:"",aviso:false};if(!e.activo)return {linea:"anillo 2: enrolamiento en pausa \
244
- (activo:false)",aviso:false};if(!n)return {linea:`anillo 2: enrolado pero el esp\xEDa NO escucha en :${e.espia} \u2014 NO encadeno (el tr\xE1fico de hoy no entra en el corpus). Arr\xE1ncalo con \`pnpm anillo2:espia\`.`,
245
- aviso:true};let a=qo(s),i=`http://127.0.0.1:${e.espia}`,c=o!==void 0&&o!=="",m=r!==void 0&&r!=="",g=[];return g.push(`anillo 2: ${s} UTC \u2192 brazo ${a.toUpperCase()}`),g.push(c?`upstream respetado de\
246
- l entorno (${o})`:`encadenado a :${e.espia}`),m&&g.push(`elisi\xF3n respetada del entorno (=${r})`),g.push("diales de elisi\xF3n APAGADOS (2026-09-09): el brazo ya no los arma"),g.push("asignaci\xF3n POR\
247
- SESI\xD3N desde el 2026-09-10 (brazoDeSesion), no por paridad del d\xEDa"),{...c?{}:{upstream:i},brazo:a,linea:g.join(", "),aviso:false}}var Wo="comprobar: en el log del gateway debe aparecer `[gateway] POST /v1/messages \u2026`; si ve\
248
- s `/v1/v1/messages`, sobra un /v1; si ves `POST /messages` a secas, falta uno.",Yo=[{id:"claude",nombre:"Claude Code",binario:"claude",configs:[],detectable:true,receta:({base:t})=>["usa `bvsala claude`\
249
- \u2014 arranca el gateway en un puerto ef\xEDmero, lanza `claude` apunt\xE1ndole y lo apaga al salir.",`a mano: ANTHROPIC_BASE_URL=${t} claude (sin /v1: el cliente a\xF1ade /v1/messages)`]},{id:"co\
250
- dex",nombre:"Codex CLI",binario:"codex",configs:[join(".codex","config.toml"),join(".codex","auth.json")],detectable:true,receta:({base:t,rutas:e})=>["usa `bvsala codex` \u2014 el prefijo del base URL depende del \
251
- modo de auth y lo resuelve el envoltorio:",` API key \u2192 openai_base_url=${t}/v1`,` suscripci\xF3n ChatGPT \u2192 openai_base_url=${t}/backend-api/codex (tiene que terminar exactament\
252
- e ah\xED)`,`${e[1]}: solo se comprueba que exista; nunca se lee.`]},{id:"opencode",nombre:"opencode",binario:"opencode",configs:[join(".config","opencode","opencode.json")],detectable:true,receta:({baseV1:t,
253
- rutas:e})=>[`mecanismo: ${e[0]} \u2192 provider.anthropic.options.baseURL (el opencode.json del proyecto pisa al global). CON /v1.`,"{",' "provider": {',' "anthropic": {',` "options": { "base\
254
- URL": "${t}" }`," }"," }","}",'alternativa: "baseURL": "{env:BVSALA_BASE_URL}" y exportar la variable; trampa: sin la variable, baseURL queda vac\xEDo.']},{id:"goose",nombre:"Goose",binario:"goose",
255
- configs:[join(".config","goose","config.yaml")],detectable:true,receta:({base:t,rutas:e})=>[`mecanismo: variable de entorno ANTHROPIC_HOST (pisa ${e[0]}). SIN /v1.`,`ANTHROPIC_HOST=${t} ANTHROPIC_API_KEY=\u2026\
256
- goose`]},{id:"crush",nombre:"Crush",binario:"crush",configs:[join(".config","crush","crushrc")],detectable:true,receta:({baseV1:t,rutas:e})=>[`mecanismo: ${e[0]} (Bash); crush.json est\xE1 deprecado. CON /v1\
257
- y SIN barra final (el SDK Go resuelve relativo: /v1/ \u2192 /v1/v1/messages).`,`provider add bvsala --type anthropic --base-url ${t} --api-key "$ANTHROPIC_API_KEY"`]},{id:"qwen",nombre:"Qwen Code",binario:"\
258
- qwen",configs:[join(".qwen","settings.json")],detectable:true,receta:({base:t,rutas:e})=>[`mecanismo: variable de entorno ANTHROPIC_BASE_URL + ${e[0]} con selectedType = anthropic. SIN /v1 (el SDK TS ofici\
259
- al a\xF1ade /v1/messages).`,`ANTHROPIC_BASE_URL=${t} ANTHROPIC_API_KEY=\u2026 qwen`,`comprueba a mano que ${e[0]} lleve selectedType: anthropic \u2014 el doctor no lee el archivo.`]},{id:"droid",nombre:"\
260
- Droid (Factory)",binario:"droid",configs:[join(".factory","settings.json")],detectable:true,receta:({base:t,rutas:e})=>[`mecanismo: ${e[0]} \u2192 customModels[] con provider "anthropic". SIN /v1 [inferido: val\
261
- idar en mano contra el log del gateway].`,"{",' "customModels": ['," {",' "model": "<id de modelo Anthropic>",',' "displayName": "<nombre visible>",',' "provider": "anthropic",',` \
262
- "baseUrl": "${t}",`,' "apiKey": "<tu API key de Anthropic>"'," }"," ]","}"]},{id:"cline",nombre:"Cline",configs:[],detectable:false,receta:({baseV1:t})=>["mecanismo: UI \u2192 proveedor Anthropic\
263
- \u2192 marca \xABUse custom base URL\xBB. CON /v1 (Cline fija https://api.anthropic.com/v1 y solo normaliza la URL oficial exacta).",`Base URL: ${t}`]},{id:"kilo",nombre:"Kilo Code",configs:[],detectable:false,
264
- receta:({baseV1:t})=>["mecanismo: UI \u2192 proveedor custom, Provider API \xABAnthropic Messages\xBB. Probar CON /v1 primero (linaje Cline) [inferido: validar en mano].",`Base URL: ${t}`]}];function Qo(t,e){
265
- let n=t.split(delimiter).filter(r=>r.length>0),o=process.platform==="win32"?["",".exe",".cmd",".bat"]:[""];return r=>n.some(s=>o.some(a=>e(join(s,r+a))))}async function yn(t,e={}){if(!Number.isInteger(t)||t<1||
266
- t>65535)throw new RangeError(`[bvsala doctor] puerto inv\xE1lido: ${String(t)}`);let n=e.home??homedir(),o=e.existe??existsSync,r=e.enPath??Qo(e.path??process.env.PATH??"",o),s=`http://127.0.0.1:${t}`,a=`${s}/v1`,
267
- i=[],c=[`bvsala doctor \u2014 gateway local en ${s}`,"Solo imprime: no escribe ninguna configuraci\xF3n. Detecci\xF3n por existencia (binarios en PATH y","archivos de configuraci\xF3n de usuario); nunca \
268
- se lee el contenido de ning\xFAn archivo.",""];for(let m of Yo){let g=m.configs.map(p=>join(n,p)),u=m.binario!==void 0&&r(m.binario),h=g.filter(p=>o(p)),k=u||h.length>0;k&&i.push(m.id);let l;m.detectable?
269
- k?l=`detectado \u2014 ${[...u?[`binario \`${m.binario}\` en PATH`]:[],...h.map(S=>`config presente: ${S}`)].join("; ")}`:l="no detectado":l="sin detecci\xF3n autom\xE1tica (extensi\xF3n de editor)",c.
270
- push(`\xBB ${m.id}: ${l}`),c.push(` ${m.nombre}`);for(let p of m.receta({base:s,baseV1:a,rutas:g}))c.push(` ${p}`);c.push(` ${Wo}`),c.push("");}return c.push("Regla BYOK: las recetas del anillo Anth\
271
- ropic (Goose, Qwen Code, opencode, Crush, Droid, Cline, Kilo) son","para API key. Nunca enrutes por el gateway el OAuth de una suscripci\xF3n Claude Pro/Max desde un harness","de terceros \u2014 Anthropic \
272
- lo proh\xEDbe. Claude Code y Codex con suscripci\xF3n se enrutan y se miden, pero no","se monetizan: no hay factura por tokens.","Detalle por terminal: docs/terminales/<terminal>.md"),{texto:c.join(`
273
- `),detectados:i}}var kn="savings-mode";function xn(t,e){for(let n of t){let o=join(n,"skills",kn,"SKILL.md");if(e.existe(o))return o}return null}var Sn="<!-- bvsala:savings-mode:inicio -->",Ie="\
274
- <!-- bvsala:savings-mode:fin -->";function Xo(t){let e=t.replace(/^---\n[\s\S]*?\n---\n*/,"").trim();return `${Sn}
241
+ ayuda",salida:1}}function gn(t,e,n){let o=t??{},r=typeof o.puerto=="number"?o.puerto:n;if(o.code==="EADDRINUSE")return `port ${r} is already in use (another gateway, or a terminal launched earlier?): u\
242
+ se --puerto ${r+1} or stop the other process`;if(o.code==="EACCES")return `port ${r} cannot be opened: permission denied (EACCES; ports below 1024 need privileges): use --puerto ${Math.max(1024,r)} or \
243
+ higher`;let s=t instanceof Error?t.message:String(t);return `could not start the gateway for \`bvsala ${e}\`: ${s}`}function qo(t){let e=Number.parseInt(t.slice(8,10),10);return Number.isFinite(e)&&e%2===0?"palanca":"control"}function bn(t){let e;try{e=JSON.parse(t());}catch{return null}if(typeof e!="object"||e===null)return null;let n=e,o=n.espia;return typeof o!="number"||!Number.isInteger(o)||o<1||o>65535?null:{activo:n.
244
+ activo!==false,espia:o}}function vn(t){let{enrolamiento:e,espiaVivo:n,upstreamEnv:o,elisionEnv:r,hoyUtcIso:s}=t;if(!e)return {linea:"",aviso:false};if(!e.activo)return {linea:"A/B plan: enrolment paused (acti\
245
+ vo:false)",aviso:false};if(!n)return {linea:`A/B plan: enrolled but the capture proxy is NOT listening on :${e.espia} \u2014 NOT chaining (today's traffic will not enter the corpus). Start it with \`pnpm anillo\
246
+ 2:espia\`.`,aviso:true};let i=qo(s),a=`http://127.0.0.1:${e.espia}`,c=o!==void 0&&o!=="",m=r!==void 0&&r!=="",g=[];return g.push(`A/B plan: ${s} UTC \u2192 arm ${i.toUpperCase()}`),g.push(c?`upstream ke\
247
+ pt from the environment (${o})`:`chained to :${e.espia}`),m&&g.push(`elision flag kept from the environment (=${r})`),g.push("context elision dials OFF (2026-09-09): the arm no longer turns them on"),
248
+ g.push("assignment PER SESSION since 2026-09-10 (brazoDeSesion), not by day parity"),{...c?{}:{upstream:a},brazo:i,linea:g.join(", "),aviso:false}}var Go="check: the gateway log must show `[gateway] POST /v1/messages \u2026`; if you see `/v1/v1/\
249
+ messages`, there is one /v1 too many; if you see a bare `POST /messages`, one is missing.",Yo=[{id:"claude",nombre:"Claude Code",binario:"claude",configs:[],detectable:true,receta:({base:t})=>["use `bvs\
250
+ ala claude` \u2014 starts the gateway on an ephemeral port, launches `claude` pointed at it and shuts it down on exit.",`by hand: ANTHROPIC_BASE_URL=${t} claude (no /v1: the client appends /v1/messa\
251
+ ges)`]},{id:"codex",nombre:"Codex CLI",binario:"codex",configs:[join(".codex","config.toml"),join(".codex","auth.json")],detectable:true,receta:({base:t,rutas:e})=>["use `bvsala codex` \u2014 the base URL prefix d\
252
+ epends on the auth mode and the wrapper resolves it:",` API key \u2192 openai_base_url=${t}/v1`,` ChatGPT subscription \u2192 openai_base_url=${t}/backend-api/codex (must end exactly t\
253
+ here)`,`${e[1]}: only checked for existence; never read.`]},{id:"opencode",nombre:"opencode",binario:"opencode",configs:[join(".config","opencode","opencode.json")],detectable:true,receta:({baseV1:t,rutas:e})=>[
254
+ `mechanism: ${e[0]} \u2192 provider.anthropic.options.baseURL (the project's opencode.json overrides the global one). WITH /v1.`,"{",' "provider": {',' "anthropic": {',` "options": { "baseURL\
255
+ ": "${t}" }`," }"," }","}",'alternative: "baseURL": "{env:BVSALA_BASE_URL}" and export the variable; pitfall: without the variable, baseURL is empty.']},{id:"goose",nombre:"Goose",binario:"goose",
256
+ configs:[join(".config","goose","config.yaml")],detectable:true,receta:({base:t,rutas:e})=>[`mechanism: ANTHROPIC_HOST environment variable (overrides ${e[0]}). NO /v1.`,`ANTHROPIC_HOST=${t} ANTHROPIC_API_\
257
+ KEY=\u2026 goose`]},{id:"crush",nombre:"Crush",binario:"crush",configs:[join(".config","crush","crushrc")],detectable:true,receta:({baseV1:t,rutas:e})=>[`mechanism: ${e[0]} (Bash); crush.json is deprecated\
258
+ . WITH /v1 and NO trailing slash (the Go SDK resolves relative paths: /v1/ \u2192 /v1/v1/messages).`,`provider add bvsala --type anthropic --base-url ${t} --api-key "$ANTHROPIC_API_KEY"`]},{id:"qwen",
259
+ nombre:"Qwen Code",binario:"qwen",configs:[join(".qwen","settings.json")],detectable:true,receta:({base:t,rutas:e})=>[`mechanism: ANTHROPIC_BASE_URL environment variable + ${e[0]} with selectedType = anthr\
260
+ opic. NO /v1 (the official TS SDK appends /v1/messages).`,`ANTHROPIC_BASE_URL=${t} ANTHROPIC_API_KEY=\u2026 qwen`,`check by hand that ${e[0]} has selectedType: anthropic \u2014 the doctor does not read the\
261
+ file.`]},{id:"droid",nombre:"Droid (Factory)",binario:"droid",configs:[join(".factory","settings.json")],detectable:true,receta:({base:t,rutas:e})=>[`mechanism: ${e[0]} \u2192 customModels[] with provider "ant\
262
+ hropic". NO /v1 [inferred: validate by hand against the gateway log].`,"{",' "customModels": ['," {",' "model": "<Anthropic model id>",',' "displayName": "<display name>",',' "provi\
263
+ der": "anthropic",',` "baseUrl": "${t}",`,' "apiKey": "<your Anthropic API key>"'," }"," ]","}"]},{id:"cline",nombre:"Cline",configs:[],detectable:false,receta:({baseV1:t})=>['mechanism: UI\
264
+ \u2192 Anthropic provider \u2192 tick "Use custom base URL". WITH /v1 (Cline defaults to https://api.anthropic.com/v1 and only normalizes the exact official URL).',`Base URL: ${t}`]},{id:"kilo",nombre:"\
265
+ Kilo Code",configs:[],detectable:false,receta:({baseV1:t})=>['mechanism: UI \u2192 custom provider, Provider API "Anthropic Messages". Try WITH /v1 first (Cline lineage) [inferred: validate by hand].',`B\
266
+ ase URL: ${t}`]}];function Qo(t,e){let n=t.split(delimiter).filter(r=>r.length>0),o=process.platform==="win32"?["",".exe",".cmd",".bat"]:[""];return r=>n.some(s=>o.some(i=>e(join(s,r+i))))}async function yn(t,e={}){
267
+ if(!Number.isInteger(t)||t<1||t>65535)throw new RangeError(`[bvsala doctor] invalid port: ${String(t)}`);let n=e.home??homedir(),o=e.existe??existsSync,r=e.enPath??Qo(e.path??process.env.PATH??"",o),s=`http://127.\
268
+ 0.0.1:${t}`,i=`${s}/v1`,a=[],c=[`bvsala doctor \u2014 local gateway at ${s}`,"Print only: it writes no configuration. Detection by existence (binaries on the PATH and","user configuration files); the \
269
+ contents of a file are never read.",""];for(let m of Yo){let g=m.configs.map(p=>join(n,p)),u=m.binario!==void 0&&r(m.binario),h=g.filter(p=>o(p)),k=u||h.length>0;k&&a.push(m.id);let l;m.detectable?k?l=`d\
270
+ etected \u2014 ${[...u?[`binary \`${m.binario}\` on PATH`]:[],...h.map(w=>`config present: ${w}`)].join("; ")}`:l="not detected":l="no automatic detection (editor extension)",c.push(`\xBB ${m.id}: ${l}`),
271
+ c.push(` ${m.nombre}`);for(let p of m.receta({base:s,baseV1:i,rutas:g}))c.push(` ${p}`);c.push(` ${Go}`),c.push("");}return c.push("BYOK rule: the recipes for the Anthropic-compatible terminals (Goo\
272
+ se, Qwen Code, opencode, Crush, Droid, Cline,","Kilo) are for API keys. Never route the OAuth of a Claude Pro/Max subscription through the gateway from a","third-party harness \u2014 Anthropic forbids it. \
273
+ Claude Code and Codex on a subscription are routed and metered,","but not billed: there is no per-token invoice.","Per-terminal detail: docs/terminales/<terminal>.md"),{texto:c.join(`
274
+ `),detectados:a}}var kn="savings-mode";function xn(t,e){for(let n of t){let o=join(n,"skills",kn,"SKILL.md");if(e.existe(o))return o}return null}var wn="<!-- bvsala:savings-mode:inicio -->",Ie="\
275
+ <!-- bvsala:savings-mode:fin -->";function Xo(t){let e=t.replace(/^---\n[\s\S]*?\n---\n*/,"").trim();return `${wn}
275
276
  ${e}
276
277
 
277
278
  _Always on (AGENTS.md has no activation step). Delete this block to turn it off; \`bvsala skill --codex --forzar\` restores it._
278
- ${Ie}`}function En(t){let e=xn(t.origenes,t.fs);if(!e)return {ok:false,destino:"",mensaje:`[bvsala skill] no encuentro el SKILL.md empaquetado (ra\xEDces probadas: ${t.origenes.join(", ")})`};let n=Xo(t.fs.
279
- leer(e)),o=t.proyecto?t.cwd:join(t.home,".codex"),r=join(o,"AGENTS.md");if(!t.fs.existe(r))return t.fs.crearDir(o),t.fs.escribir(r,n+`
280
- `),{ok:true,destino:r,mensaje:st(r,"instalado")};let s=t.fs.leer(r),a=s.indexOf(Sn),i=s.indexOf(Ie);return a===-1||i===-1||i<a?(t.fs.escribir(r,s.trimEnd()+`
279
+ ${Ie}`}function Sn(t){let e=xn(t.origenes,t.fs);if(!e)return {ok:false,destino:"",mensaje:`[bvsala skill] cannot find the bundled SKILL.md (roots tried: ${t.origenes.join(", ")})`};let n=Xo(t.fs.leer(e)),
280
+ o=t.proyecto?t.cwd:join(t.home,".codex"),r=join(o,"AGENTS.md");if(!t.fs.existe(r))return t.fs.crearDir(o),t.fs.escribir(r,n+`
281
+ `),{ok:true,destino:r,mensaje:st(r,"installed")};let s=t.fs.leer(r),i=s.indexOf(wn),a=s.indexOf(Ie);return i===-1||a===-1||a<i?(t.fs.escribir(r,s.trimEnd()+`
281
282
 
282
283
  `+n+`
283
- `),{ok:true,destino:r,mensaje:st(r,"a\xF1adido al AGENTS.md existente")}):s.slice(a,i+Ie.length)===n?{ok:true,destino:r,mensaje:`[bvsala skill] ya instalado y al d\xEDa: ${r}`}:t.forzar?(t.fs.escribir(r,s.
284
- slice(0,a)+n+s.slice(i+Ie.length)),{ok:true,destino:r,mensaje:st(r,"bloque actualizado")}):{ok:false,destino:r,mensaje:`[bvsala skill] el bloque savings-mode de ${r} tiene contenido DISTINTO \u2014 no lo piso; \
285
- usa --forzar para actualizarlo`}}function st(t,e){return `[bvsala skill] ${e}: ${t}
286
- [bvsala skill] AGENTS.md va SIEMPRE activo en Codex (no hay paso de activaci\xF3n); borra el bloque para apagarlo.
287
- [bvsala skill] para medir su efecto: trabaja unos d\xEDas con \xE9l y otros sin \xE9l bajo \`bvsala codex\`, y compara en el panel.`}function wn(t){let e=xn(t.origenes,t.fs);if(!e)return {ok:false,destino:"",
288
- mensaje:`[bvsala skill] no encuentro el SKILL.md empaquetado (ra\xEDces probadas: ${t.origenes.join(", ")})`};let n=t.proyecto?join(t.cwd,".claude","skills"):join(t.home,".claude","skills"),o=join(n,kn),r=join(o,
289
- "SKILL.md"),s=t.fs.leer(e);if(t.fs.existe(r)&&!t.forzar){let a=t.fs.leer(r)===s;return {ok:a,destino:r,mensaje:a?`[bvsala skill] ya instalado y al d\xEDa: ${r}`:`[bvsala skill] ya existe ${r} con conte\
290
- nido DISTINTO \u2014 no lo piso; usa --forzar para sobrescribir`}}return t.fs.crearDir(o),t.fs.escribir(r,s),{ok:true,destino:r,mensaje:`[bvsala skill] instalado: ${r}
291
- [bvsala skill] arranca (o reinicia) Claude Code y p\xEDdele \xABsavings mode\xBB \u2014 o deja que se active solo al hablar de l\xEDmites.
292
- [bvsala skill] para medir su efecto de verdad: trabaja unos d\xEDas con \xE9l y otros sin \xE9l bajo \`bvsala claude\`, y compara en el panel.`}}var at=class{id;exact=false;constructor(e="heuristic"){this.id=e;}count(e){if(e.length===0)return 0;let n=e.match(/[A-Za-z]+|\d+|\s+|[^\sA-Za-z\d]/g)??[],o=0;for(let r of n){let s=r[0];if(/\s/.test(s)){let a=(r.
293
- match(/\n/g)??[]).length;o+=a+Math.floor(Math.max(0,r.length-1)/2);}else /[A-Za-z]/.test(s)?o+=Math.max(1,Math.ceil(r.length/4)):/\d/.test(s)?o+=Math.max(1,Math.ceil(r.length/2)):o+=1;}return Math.max(o,
294
- Math.ceil(e.length/4))}},it=new at;var ct=class{estimators=new Map;modelToTokenizer=new Map;fallbackId;constructor(e=it){this.fallbackId=e.id,this.estimators.set(e.id,e);}register(e){return this.estimators.set(e.id,e),this}mapModel(e,n){
284
+ `),{ok:true,destino:r,mensaje:st(r,"appended to the existing AGENTS.md")}):s.slice(i,a+Ie.length)===n?{ok:true,destino:r,mensaje:`[bvsala skill] already installed and up to date: ${r}`}:t.forzar?(t.fs.escribir(
285
+ r,s.slice(0,i)+n+s.slice(a+Ie.length)),{ok:true,destino:r,mensaje:st(r,"block updated")}):{ok:false,destino:r,mensaje:`[bvsala skill] the savings-mode block in ${r} has DIFFERENT content \u2014 not overwriting \
286
+ it; use --forzar to update it`}}function st(t,e){return `[bvsala skill] ${e}: ${t}
287
+ [bvsala skill] AGENTS.md is ALWAYS active in Codex (there is no activation step); delete the block to turn it off.
288
+ [bvsala skill] to measure its effect: work a few days with it and a few without, under \`bvsala codex\`, and compare in the dashboard.`}function En(t){let e=xn(t.origenes,t.fs);if(!e)return {ok:false,destino:"",
289
+ mensaje:`[bvsala skill] cannot find the bundled SKILL.md (roots tried: ${t.origenes.join(", ")})`};let n=t.proyecto?join(t.cwd,".claude","skills"):join(t.home,".claude","skills"),o=join(n,kn),r=join(o,"SKILL.md"),
290
+ s=t.fs.leer(e);if(t.fs.existe(r)&&!t.forzar){let i=t.fs.leer(r)===s;return {ok:i,destino:r,mensaje:i?`[bvsala skill] already installed and up to date: ${r}`:`[bvsala skill] ${r} already exists with DIF\
291
+ FERENT content \u2014 not overwriting it; use --forzar to overwrite`}}return t.fs.crearDir(o),t.fs.escribir(r,s),{ok:true,destino:r,mensaje:`[bvsala skill] installed: ${r}
292
+ [bvsala skill] start (or restart) Claude Code and ask for "savings mode" \u2014 or let it activate on its own when you mention limits.
293
+ [bvsala skill] to measure its real effect: work a few days with it and a few without, under \`bvsala claude\`, and compare in the dashboard.`}}var it=class{id;exact=false;constructor(e="heuristic"){this.id=e;}count(e){if(e.length===0)return 0;let n=e.match(/[A-Za-z]+|\d+|\s+|[^\sA-Za-z\d]/g)??[],o=0;for(let r of n){let s=r[0];if(/\s/.test(s)){let i=(r.
294
+ match(/\n/g)??[]).length;o+=i+Math.floor(Math.max(0,r.length-1)/2);}else /[A-Za-z]/.test(s)?o+=Math.max(1,Math.ceil(r.length/4)):/\d/.test(s)?o+=Math.max(1,Math.ceil(r.length/2)):o+=1;}return Math.max(o,
295
+ Math.ceil(e.length/4))}},at=new it;var ct=class{estimators=new Map;modelToTokenizer=new Map;fallbackId;constructor(e=at){this.fallbackId=e.id,this.estimators.set(e.id,e);}register(e){return this.estimators.set(e.id,e),this}mapModel(e,n){
295
296
  return this.modelToTokenizer.set(e,n),this}resolve(e){let n=e.tokenizer?this.estimators.get(e.tokenizer):void 0;if(n)return n;if(e.model){let o=this.modelToTokenizer.get(e.model);if(o){let r=this.estimators.
296
297
  get(o);if(r)return r}}return this.estimators.get(this.fallbackId)}count(e,n){return this.resolve(n).count(e)}};new ct;new TextEncoder;new TextDecoder;new TextDecoder;var hr={"gpt-4o-mini":{deltaTokens:-16,salidaDelBrazoA:72,contrato:"triage (6 fields, long enums)",pares:8},"gpt-4o":{deltaTokens:3,salidaDelBrazoA:75,contrato:"triage (6 fields, long enums)",pares:8}};
297
- Ye(hr);function Er(t,e=300){return new Promise(n=>{let o=connect({port:t,host:"127.0.0.1"}),r=s=>{o.destroy(),n(s);};o.setTimeout(e,()=>r(false)),o.once("connect",()=>r(true)),o.once("error",()=>r(false));})}var Le=hn(process.
298
- argv.slice(2));function wr(){console.log(`bvsala ${G} \u2014 BiVelio Savings Layer
298
+ Ye(hr);function Sr(t,e=300){return new Promise(n=>{let o=connect({port:t,host:"127.0.0.1"}),r=s=>{o.destroy(),n(s);};o.setTimeout(e,()=>r(false)),o.once("connect",()=>r(true)),o.once("error",()=>r(false));})}var Me=hn(process.
299
+ argv.slice(2));function Er(){console.log(`bvsala ${J} \u2014 BiVelio Savings Layer
299
300
 
300
- Comandos:
301
- gateway [--puerto N] Proxy local medidor (por defecto 8402) para terminales que
302
- respetan un base URL: Claude Code, opencode, Goose, Crush\u2026
303
- claude [args\u2026] Lanza Claude Code a trav\xE9s del gateway, sin configurar nada.
304
- codex [args\u2026] Lanza Codex CLI a trav\xE9s del gateway (API key: api.openai.com;
305
- auth ChatGPT: chatgpt.com/backend-api/codex \u2014 se mide, no se monetiza).
306
- goose [args\u2026] Lanza Goose (ANTHROPIC_HOST) a trav\xE9s del gateway.
307
- qwen [args\u2026] Lanza Qwen Code (ANTHROPIC_BASE_URL) a trav\xE9s del gateway.
308
- doctor [--puerto N] Detecta terminales instalados e imprime su configuraci\xF3n exacta.
301
+ Commands:
302
+ gateway [--puerto N] Local metering proxy (default port 8402) for terminals that
303
+ honour a base URL: Claude Code, opencode, Goose, Crush\u2026
304
+ claude [args\u2026] Run Claude Code through the gateway. No configuration needed.
305
+ codex [args\u2026] Run Codex CLI through the gateway (API key: api.openai.com;
306
+ ChatGPT auth: chatgpt.com/backend-api/codex \u2014 metered, not billed).
307
+ goose [args\u2026] Run Goose (ANTHROPIC_HOST) through the gateway.
308
+ qwen [args\u2026] Run Qwen Code (ANTHROPIC_BASE_URL) through the gateway.
309
+ doctor [--puerto N] Detect installed terminals and print the exact configuration for each.
309
310
  skill [--codex] [--proyecto] [--forzar]
310
- Instala el skill \xABsavings-mode\xBB de Claude Code (~/.claude/skills;
311
- con --proyecto, en .claude/skills del directorio actual): salida
312
- tersa + herramientas quir\xFArgicas para estirar los topes de
313
- suscripci\xF3n. Con --codex, la misma disciplina como bloque marcado
314
- de AGENTS.md (~/.codex; con --proyecto, ./AGENTS.md) \u2014 Codex no
315
- tiene skills y AGENTS.md va siempre activo. Su efecto se mide con
316
- el gateway, no se promete.
317
- --version | -v Imprime solo la versi\xF3n.
318
- --help | -h Esta ayuda; tambi\xE9n detr\xE1s de gateway, doctor o skill
319
- (\`bvsala gateway --help\` no arranca nada).
311
+ Install the "savings-mode" skill for Claude Code (~/.claude/skills;
312
+ with --proyecto, .claude/skills in the current directory): terse
313
+ output plus surgical tool use to stretch subscription limits.
314
+ With --codex, the same discipline as a marked block in AGENTS.md
315
+ (~/.codex; with --proyecto, ./AGENTS.md) \u2014 Codex has no skills and
316
+ AGENTS.md is always active. Its effect is measured by the gateway,
317
+ not promised.
318
+ --version | -v Print the version only.
319
+ --help | -h This help; also accepted after gateway, doctor or skill
320
+ (\`bvsala gateway --help\` starts nothing).
320
321
 
321
- Entorno:
322
- BIVELIO_SERVICE_KEY Clave de servicio (bvk_\u2026): habilita metering + asiento.
323
- BVSALA_SEAT_ID Identidad de asiento estable por r\xE9plica (opcional).
324
- BVSALA_GATEWAY_UPSTREAM Upstream alternativo (por defecto el del terminal).
325
- BVSALA_GATEWAY_COALESCE =1 enciende la coalescencia de duplicados (F2, experimental).
326
- BVSALA_GATEWAY_SHADOW =1 enciende el carril sombra de la elisi\xF3n (S2): calcula qu\xE9
327
- habr\xEDa ahorrado la pol\xEDtica v0 SIN tocar el tr\xE1fico y lo
328
- reporta aparte (jam\xE1s entra en la comisi\xF3n).
329
- BVSALA_GATEWAY_ELISION =0 APAGA las palancas del proveedor (S4a), armadas por
330
- defecto. Cada una se enciende de verdad solo si la licencia de
331
- tu organizaci\xF3n trae su feature (consulta fail-closed):
332
- \xB7 context_elision \u2014 ajusta SOLO el bloque context_management
333
- de cada /v1/messages; el borrado lo hace el proveedor.
334
- \xB7 cache_ttl_tuning \u2014 rebaja el TTL de escritura de cach\xE9 de
335
- 1h (2x el precio de entrada) a 5m (1,25x) mientras la sesi\xF3n
336
- siga caliente. NO toca un solo token que el modelo lea:
337
- cambia la tarifa, no el contenido. En el corpus del esp\xEDa
338
- el 99,1 % de los turnos encadena en menos de 5 minutos, y
339
- ese sobreprecio era el 8,0 % de la factura.
340
- Ante cualquier duda, la petici\xF3n viaja intacta. (Hasta 0.4.13
341
- la bandera era al rev\xE9s: \xAB=1 arma\xBB; en un A/B p\xE1sala SIEMPRE
342
- expl\xEDcita, =1 o =0, y no depender\xE1s de la versi\xF3n instalada.)
322
+ Environment:
323
+ BIVELIO_SERVICE_KEY Service key (bvk_\u2026): enables metering and seat identity.
324
+ BVSALA_SEAT_ID Stable seat identity per replica (optional).
325
+ BVSALA_GATEWAY_UPSTREAM Alternative upstream (default: the terminal's own).
326
+ BVSALA_GATEWAY_COALESCE =1 enables duplicate-request coalescing (experimental).
327
+ BVSALA_GATEWAY_SHADOW =1 enables the shadow lane for context elision (experimental):
328
+ computes what the elision policy would have saved WITHOUT touching
329
+ traffic and reports it separately (never counts toward the fee).
330
+ BVSALA_GATEWAY_ELISION =0 turns OFF the provider-side levers, which are armed by default.
331
+ Each one only takes effect if your organization's license carries
332
+ its feature (checked fail-closed):
333
+ \xB7 context_elision \u2014 adjusts ONLY the context_management block
334
+ of each /v1/messages; the provider does the trimming.
335
+ \xB7 cache_ttl_tuning \u2014 lowers the cache-write TTL from 1h (2x the
336
+ input price) to 5m (1.25x) while the session stays warm. It does
337
+ NOT touch a single token the model reads: it changes the rate,
338
+ not the content. In measured traffic, 99.1% of turns chain within
339
+ 5 minutes, and that surcharge was 8.0% of the bill.
340
+ When in doubt, the request travels untouched. (Up to 0.4.13 the flag
341
+ was inverted: "=1 arms". In an A/B always pass it explicitly, =1 or
342
+ =0, so you do not depend on the installed version.)
343
343
  BVSALA_GATEWAY_MODELO_HERMANO
344
- =1 sirve claude-fable-5 con claude-fable-5-1 (palanca C): misma
345
- tarifa de entrada, salida y escritura de cach\xE9; la LECTURA de
346
- cach\xE9 baja de 0,001 a 0,00025 por 1K tokens (cat\xE1logo). El
347
- ahorro es una resta de dos precios sobre el mismo usage:
348
- se mide sin A/B y entra en el titular medido del panel.
349
- Solo si la licencia trae sibling_model_routing (fail-closed).
350
- CAMBIA el model que ves en la respuesta (/cost, /status), y
351
- encenderlo a mitad de un hilo paga UNA escritura fr\xEDa del
352
- prefijo (cada modelo tiene su cach\xE9): arr\xE1ncalo con el terminal.
353
- Apagar = quitar la variable o =0.
344
+ =1 serves claude-fable-5 with claude-fable-5-1 (sibling model routing):
345
+ same input, output and cache-write rates; cache READS drop from
346
+ 0.001 to 0.00025 per 1K tokens (catalog). The saving is the difference
347
+ of two prices over the same usage: measured without an A/B, and it
348
+ counts toward the measured headline in the dashboard.
349
+ Only if the license carries sibling_model_routing (fail-closed).
350
+ It CHANGES the model you see in the response (/cost, /status), and
351
+ turning it on mid-thread pays ONE cold write of the prefix (each
352
+ model has its own cache): start it together with the terminal.
353
+ Off = unset the variable or =0.
354
354
  BVSALA_GATEWAY_ESFUERZO
355
- =low|medium|high|xhigh|max arma el esfuerzo por turno (palanca D):
356
- a\xF1ade al final de cada /v1/messages un mensaje con
357
- output_config.effort, sin tocar la ra\xEDz (no reinicia la cach\xE9 de
358
- prefijo). Solo si la licencia trae per_turn_effort (fail-closed)
359
- y solo con claude-fable-5-1, claude-mythos-5-1 y claude-opus-5;
360
- con otro modelo la petici\xF3n viaja intacta. Es la \xFAnica palanca
361
- que puede tocar la calidad de la respuesta: va APAGADA por
362
- defecto y a\xFAn no est\xE1 medida contra la verja de calidad.
363
- BVSALA_CODEX_AUTH api-key | chatgpt \u2014 fuerza el modo de auth de Codex.
364
- BVSALA_GATEWAY_LOG =1 ense\xF1a el log por petici\xF3n tambi\xE9n dentro de los envoltorios
365
- (por defecto va silenciado para no pisar el TUI; mira el panel).
366
- CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 (Claude Code, solo API key) tus modelos en /model.
355
+ =low|medium|high|xhigh|max sets per-turn effort (experimental):
356
+ appends a message with output_config.effort to each /v1/messages
357
+ without touching the root (does not reset the prefix cache). Only if
358
+ the license carries per_turn_effort (fail-closed) and only with
359
+ claude-fable-5-1, claude-mythos-5-1 and claude-opus-5; with any other
360
+ model the request travels untouched. It is the only lever that can
361
+ affect response quality: OFF by default and not yet measured against
362
+ the quality gate.
363
+ BVSALA_CODEX_AUTH api-key | chatgpt \u2014 forces the Codex auth mode.
364
+ BVSALA_GATEWAY_LOG =1 shows the per-request log inside the wrappers too (silenced by
365
+ default so it does not overwrite the TUI; use the dashboard instead).
366
+ CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY=1 (Claude Code, API key only) your models in /model.
367
367
 
368
- El gateway nunca corta el tr\xE1fico: sin clave o con la ingesta ca\xEDda, reenv\xEDa y avisa.
369
- Tus prompts jam\xE1s salen de tu m\xE1quina \u2014 solo cuentas y coste. M\xE1s: savings.bivelio.com`);}Le.tipo==="ayuda"&&(wr(),process.exit(Le.salida));Le.tipo==="version"&&(console.log(G),process.exit(
370
- 0));var{comando:R,resto:U}=Le,$e=Re(R);if(R==="skill"){let t=dirname(fileURLToPath(import.meta.url)),e={origenes:[join(t,"..",".."),join(t,"..")],home:homedir(),cwd:process.cwd(),proyecto:U.includes("--proyecto"),forzar:U.includes(
371
- "--forzar"),fs:{existe:existsSync,leer:o=>readFileSync(o,"utf8"),escribir:(o,r)=>writeFileSync(o,r),crearDir:o=>mkdirSync(o,{recursive:true})}},n=U.includes("--codex")?En(e):wn(e);console.log(n.mensaje),process.exit(n.ok?0:1);}var mt=U.indexOf(
372
- "--puerto"),Q=$e?0:mt>=0?Number(U[mt+1]):8402;(!Number.isInteger(Q)||(R==="doctor"?Q<1:Q<0)||Q>65535)&&(console.error(`[bvsala ${R}] puerto inv\xE1lido: ${U[mt+1]}`),process.exit(1));if(R==="doctor"){
373
- U.includes("--write")&&(console.error("[bvsala doctor] --write todav\xEDa no existe: el doctor solo imprime; copia el bloque a mano."),process.exit(1));let t=await yn(Q);console.log(t.texto),process.exit(
374
- 0);}var j=$e?rt(R,0,U,{modoCodex:R==="codex"?pn({leerAuth:()=>readFileSync(join(homedir(),".codex","auth.json"),"utf8")}):void 0}):null,re=process.env.BIVELIO_SERVICE_KEY,Ar=$e&&process.env.BVSALA_GATEWAY_LOG!=="1",pt=j?.
375
- upstream?null:bn(()=>readFileSync(process.env.BVSALA_ANILLO2_FICHERO??join(homedir(),".bvsala","anillo2.json"),"utf8")),fe=vn({enrolamiento:pt,espiaVivo:pt!==null&&await Er(pt.espia),upstreamEnv:process.env.BVSALA_GATEWAY_UPSTREAM,
368
+ The gateway never blocks traffic: without a key, or with ingestion down, it forwards and warns.
369
+ Your prompts never leave your machine \u2014 only counts and cost. More: savings.bivelio.com`);}Me.tipo==="ayuda"&&(Er(),process.exit(Me.salida));Me.tipo==="version"&&(console.log(J),process.exit(0));var{
370
+ comando:R,resto:U}=Me,$e=Re(R);if(R==="skill"){let t=dirname(fileURLToPath(import.meta.url)),e={origenes:[join(t,"..",".."),join(t,"..")],home:homedir(),cwd:process.cwd(),proyecto:U.includes("--proyecto"),forzar:U.includes("-\
371
+ -forzar"),fs:{existe:existsSync,leer:o=>readFileSync(o,"utf8"),escribir:(o,r)=>writeFileSync(o,r),crearDir:o=>mkdirSync(o,{recursive:true})}},n=U.includes("--codex")?Sn(e):En(e);console.log(n.mensaje),process.exit(n.ok?0:1);}var mt=U.indexOf(
372
+ "--puerto"),Q=$e?0:mt>=0?Number(U[mt+1]):8402;(!Number.isInteger(Q)||(R==="doctor"?Q<1:Q<0)||Q>65535)&&(console.error(`[bvsala ${R}] invalid port: ${U[mt+1]}`),process.exit(1));if(R==="doctor"){U.includes(
373
+ "--write")&&(console.error("[bvsala doctor] --write does not exist yet: the doctor only prints; copy the block by hand."),process.exit(1));let t=await yn(Q);console.log(t.texto),process.exit(0);}var N=$e?
374
+ rt(R,0,U,{modoCodex:R==="codex"?pn({leerAuth:()=>readFileSync(join(homedir(),".codex","auth.json"),"utf8")}):void 0}):null,re=process.env.BIVELIO_SERVICE_KEY,Tr=$e&&process.env.BVSALA_GATEWAY_LOG!=="1",pt=N?.upstream?
375
+ null:bn(()=>readFileSync(process.env.BVSALA_ANILLO2_FICHERO??join(homedir(),".bvsala","anillo2.json"),"utf8")),fe=vn({enrolamiento:pt,espiaVivo:pt!==null&&await Sr(pt.espia),upstreamEnv:process.env.BVSALA_GATEWAY_UPSTREAM,
376
376
  elisionEnv:process.env.BVSALA_GATEWAY_ELISION,hoyUtcIso:new Date().toISOString().slice(0,10)}),ft=process.env.BVSALA_GATEWAY_UPSTREAM??fe.upstream,De=process.env.BVSALA_GATEWAY_ELISION??fe.elision,F;try{
377
- F=await ln({puerto:Q,...Ar?{log:()=>{}}:{},...re?{serviceAccountKey:re}:{},...process.env.BVSALA_SEAT_ID?{seatId:process.env.BVSALA_SEAT_ID}:{},...ft?{upstream:ft}:j?.upstream?{upstream:j.upstream}:{},
377
+ F=await ln({puerto:Q,...Tr?{log:()=>{}}:{},...re?{serviceAccountKey:re}:{},...process.env.BVSALA_SEAT_ID?{seatId:process.env.BVSALA_SEAT_ID}:{},...ft?{upstream:ft}:N?.upstream?{upstream:N.upstream}:{},
378
378
  ...process.env.BVSALA_GATEWAY_COALESCE==="1"?{coalescer:!0}:{},...process.env.BVSALA_GATEWAY_SHADOW==="1"?{sombra:!0}:{},elision:De!=="0",...process.env.BVSALA_GATEWAY_DIALES_ELISION==="1"?{dialesElision:!0}:
379
379
  {},...process.env.BVSALA_GATEWAY_MODELO_HERMANO==="1"?{modeloHermano:!0}:{},...process.env.BVSALA_GATEWAY_ESFUERZO?{esfuerzo:process.env.BVSALA_GATEWAY_ESFUERZO}:{},...process.env.BVSALA_METERING_URL?
380
- {meteringUrl:process.env.BVSALA_METERING_URL}:{}});}catch(t){console.error(`[bvsala ${R}] ${gn(t,R,Q)}`),process.exit(1);}var Tr=ft??j?.upstream??"https://api.anthropic.com";console.log(`[bvsala gateway\
381
- ] v${G} escuchando en http://127.0.0.1:${F.puerto} \u2192 ${Tr}`);console.log(`[bvsala gateway] medici\xF3n: ${re?"ACTIVA (clave de servicio presente)":"APAGADA (falta BIVELIO_SERVICE_KEY)"}`);{let t=wt(
382
- De,re!==void 0,process.env.BVSALA_GATEWAY_DIALES_ELISION==="1");(t.aviso?console.error:console.log)(`[bvsala gateway] ${t.linea}`);let e=Nt(process.env.BVSALA_GATEWAY_MODELO_HERMANO,re!==void 0,De!=="\
383
- 0");(e.aviso?console.error:console.log)(`[bvsala gateway] ${e.linea}`);let n=Dt(process.env.BVSALA_GATEWAY_ESFUERZO,re!==void 0,De!=="0");(n.aviso?console.error:console.log)(`[bvsala gateway] ${n.linea}`),
384
- fe.linea&&(fe.aviso?console.error:console.log)(`[bvsala gateway] ${fe.linea}`);}process.env.BVSALA_GATEWAY_UPSTREAM&&j?.upstream&&process.env.BVSALA_GATEWAY_UPSTREAM!==j.upstream&&console.error(`[bvsal\
385
- a ${R}] AVISO: BVSALA_GATEWAY_UPSTREAM=${process.env.BVSALA_GATEWAY_UPSTREAM} pisa el upstream de este terminal (${j.upstream}). Si no es lo que quieres, quita la variable.`);R==="codex"&&!Kt()&&console.
386
- error(`[bvsala codex] AVISO: este Node (${process.version}) no descomprime zstd y Codex manda el cuerpo en zstd: el tr\xE1fico pasa pero NO se mide. Usa Node \u2265 22.15.`);if(j&&$e){let t=rt(R,F.puerto,
387
- U,{...j.upstream?{modoCodex:j.upstream.includes("chatgpt.com")?"chatgpt":"api-key"}:{}});t.nota&&console.log(`[bvsala ${R}] ${t.nota}`);let e=t.bin;if(process.platform==="win32"){let o=dn(t.bin);o.tipo===
388
- "exe"?e=o.ruta:o.tipo==="solo-shim"&&(console.error(`[bvsala ${R}] \`${t.bin}\` est\xE1 instalado como shim de npm (${o.ruta}) y Windows no puede lanzarlo de forma segura con argumentos (CVE-2024-2798\
389
- 0). Inst\xE1lalo con su instalador nativo (deja un ${t.bin}.exe en el PATH) o usa la receta manual de docs/terminales/.`),await F.cerrar(),process.exit(127));}let n=spawn(e,t.args,{stdio:"inherit",env:{...process.
390
- env,...t.env}});n.on("error",o=>{console.error(`[bvsala ${R}] no se pudo lanzar \`${t.bin}\`: ${String(o)}. \xBFEst\xE1 instalado y en el PATH?`),F.cerrar().then(()=>process.exit(127));}),n.on("exit",(o,r)=>{
391
- F.cerrar().then(()=>process.exit(o??(r?1:0)));});}else console.log(`[bvsala gateway] apunta tu terminal (o usa \`bvsala ${ot.join("|")}\`): ANTHROPIC_BASE_URL=http://127.0.0.1:${F.puerto} claude \xB7 \
392
- bvsala doctor para el resto`);var Pn=async()=>{await F.cerrar(),process.exit(0);};process.on("SIGINT",()=>{Pn();});process.on("SIGTERM",()=>{Pn();});
380
+ {meteringUrl:process.env.BVSALA_METERING_URL}:{}});}catch(t){console.error(`[bvsala ${R}] ${gn(t,R,Q)}`),process.exit(1);}var Ar=ft??N?.upstream??"https://api.anthropic.com";console.log(`[bvsala gateway\
381
+ ] v${J} listening on http://127.0.0.1:${F.puerto} \u2192 ${Ar}`);console.log(`[bvsala gateway] metering: ${re?"ON (service key present)":"OFF (BIVELIO_SERVICE_KEY not set)"}`);{let t=Et(De,re!==void 0,
382
+ process.env.BVSALA_GATEWAY_DIALES_ELISION==="1");(t.aviso?console.error:console.log)(`[bvsala gateway] ${t.linea}`);let e=jt(process.env.BVSALA_GATEWAY_MODELO_HERMANO,re!==void 0,De!=="0");(e.aviso?console.
383
+ error:console.log)(`[bvsala gateway] ${e.linea}`);let n=Dt(process.env.BVSALA_GATEWAY_ESFUERZO,re!==void 0,De!=="0");(n.aviso?console.error:console.log)(`[bvsala gateway] ${n.linea}`),fe.linea&&(fe.aviso?
384
+ console.error:console.log)(`[bvsala gateway] ${fe.linea}`);}process.env.BVSALA_GATEWAY_UPSTREAM&&N?.upstream&&process.env.BVSALA_GATEWAY_UPSTREAM!==N.upstream&&console.error(`[bvsala ${R}] WARNING: BVS\
385
+ ALA_GATEWAY_UPSTREAM=${process.env.BVSALA_GATEWAY_UPSTREAM} overrides this terminal's upstream (${N.upstream}). If that is not what you want, unset the variable.`);R==="codex"&&!Kt()&&console.error(`[\
386
+ bvsala codex] WARNING: this Node (${process.version}) cannot decompress zstd and Codex sends its body in zstd: traffic passes through but is NOT metered. Use Node >= 22.15.`);if(N&&$e){let t=rt(R,F.puerto,
387
+ U,{...N.upstream?{modoCodex:N.upstream.includes("chatgpt.com")?"chatgpt":"api-key"}:{}});t.nota&&console.log(`[bvsala ${R}] ${t.nota}`);let e=t.bin;if(process.platform==="win32"){let o=dn(t.bin);o.tipo===
388
+ "exe"?e=o.ruta:o.tipo==="solo-shim"&&(console.error(`[bvsala ${R}] \`${t.bin}\` is installed as an npm shim (${o.ruta}) and Windows cannot launch it safely with arguments (CVE-2024-27980). Install it \
389
+ with its native installer (which leaves a ${t.bin}.exe on the PATH) or use the manual recipe in docs/terminales/.`),await F.cerrar(),process.exit(127));}let n=spawn(e,t.args,{stdio:"inherit",env:{...process.
390
+ env,...t.env}});n.on("error",o=>{console.error(`[bvsala ${R}] could not launch \`${t.bin}\`: ${String(o)}. Is it installed and on the PATH?`),F.cerrar().then(()=>process.exit(127));}),n.on("exit",(o,r)=>{
391
+ F.cerrar().then(()=>process.exit(o??(r?1:0)));});}else console.log(`[bvsala gateway] point your terminal at it (or use \`bvsala ${ot.join("|")}\`): ANTHROPIC_BASE_URL=http://127.0.0.1:${F.puerto} claud\
392
+ e \xB7 bvsala doctor for the others`);var On=async()=>{await F.cerrar(),process.exit(0);};process.on("SIGINT",()=>{On();});process.on("SIGTERM",()=>{On();});