@bivelio/savings-layer 0.4.25 → 0.4.28

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.
@@ -42,11 +42,17 @@ igual.
42
42
  | [Codex CLI](codex.md) | `http://127.0.0.1:8402/v1` (API key) · `…/backend-api/codex` (ChatGPT) | **con** | `codex -c openai_base_url=…` | `bvsala codex` ✅ | [V] validado en mano el 30-08 (426 → fallback HTTP, stream íntegro) |
43
43
  | [Goose](goose.md) | `http://127.0.0.1:8402` | **sin** | env `ANTHROPIC_HOST` | `bvsala goose` ✅ | [V] |
44
44
  | [Qwen Code](qwen-code.md) | `http://127.0.0.1:8402` | **sin** | env `ANTHROPIC_BASE_URL` + `~/.qwen/settings.json` | `bvsala qwen` ✅ | [V] |
45
- | [opencode](opencode.md) | `http://127.0.0.1:8402/v1` | **con** | `~/.config/opencode/opencode.json` | — (config de usuario) | [V] |
45
+ | [opencode](opencode.md) | `http://127.0.0.1:8402/v1` | **con** | env `OPENCODE_CONFIG_CONTENT` o `~/.config/opencode/opencode.json` | `bvsala opencode` ✅ (sin escribir en disco) | [V] en código (v1.18.32) |
46
+ | [Kilo Code](kilo.md) (CLI y extensiones) | `http://127.0.0.1:8402/v1` | **con** | env `KILO_CONFIG_CONTENT` o `~/.config/kilo/kilo.json` | `bvsala kilo` ✅ (CLI) | [V] en código (v7.8.1, fork de opencode) |
47
+ | [Copilot CLI](copilot.md) (solo BYOK) | anthropic: `http://127.0.0.1:8402` · openai: `…/v1` | anthropic **sin**, openai **con** | env `COPILOT_PROVIDER_BASE_URL` | `bvsala copilot` ✅ (tipo anthropic) | [V] en la doc oficial; sin validar en mano. Tipo openai: a mano, sin validar (WebSocket → 426) |
48
+ | [Copilot en VS Code](copilot.md#copilot-en-vs-code-byok-custom-endpoint) (solo BYOK) | `http://127.0.0.1:8402/v1/messages` (ruta completa) | ruta completa | UI → Custom Endpoint (`chatLanguageModels.json`) | — | [V] en la doc oficial; sin validar en mano |
49
+ | [Aider](aider.md) | Anthropic: `http://127.0.0.1:8402` · OpenAI: `…/v1` | Anthropic **sin**, OpenAI **con** | env `ANTHROPIC_API_BASE` / `OPENAI_API_BASE` (litellm) | `bvsala aider` ✅ | [V] en código (litellm 1.82.3); OpenAI en stream solo con `BVSALA_GATEWAY_USAGE_INJECT=1` |
50
+ | [Continue](continue.md) (CLI `cn` y extensiones) | `http://127.0.0.1:8402/v1` | **con** | `~/.continue/config.yaml` (`apiBase`) | — (config de usuario) | [V] en código |
46
51
  | [Crush](crush.md) | `http://127.0.0.1:8402/v1` | **con**, sin barra final | `~/.config/crush/crushrc` | — (config de usuario) | [V] |
47
52
  | [Droid](droid.md) | `http://127.0.0.1:8402` | sin **[I]** | `~/.factory/settings.json` | — (config de usuario) | validar en mano |
48
53
  | [Cline](cline.md) | `http://127.0.0.1:8402/v1` | **con** | UI | — | [V] |
49
- | [Kilo Code](kilo.md) | `http://127.0.0.1:8402/v1` | con **[I]** | UI | — | validar en mano |
54
+ | [Gemini CLI](no-soportados.md#gemini-cli--no-se-mide-todavía) | — | — | env `GOOGLE_GEMINI_BASE_URL` | — | **no se mide**: el gateway no lee el dialecto de Gemini |
55
+ | [Cursor](no-soportados.md#cursor--no-se-puede) | — | — | sus servidores, no tu máquina | — | **no se puede**: Cursor no alcanza `127.0.0.1` |
50
56
  | [Python y cualquier SDK](../../README.md#python-and-any-other-language-the-local-gateway) (OpenAI · Anthropic) | OpenAI: `http://127.0.0.1:8403/v1` · Anthropic: `http://127.0.0.1:8402` | OpenAI **con**, Anthropic **sin** | `base_url` del SDK | `bvsala gateway --upstream openai` | [V] en test (upstream falso) |
51
57
 
52
58
  Reglas de la columna «Base URL»: **nunca barra final**, **nunca redirecciones**
@@ -54,12 +60,28 @@ Reglas de la columna «Base URL»: **nunca barra final**, **nunca redirecciones*
54
60
  en vez de seguirlo). Y nunca copies el URL de un terminal a otro: cada SDK añade
55
61
  su propio tramo de ruta y el error es silencioso (§ «Comprobar que mide»).
56
62
 
57
- Claude Code, Codex, Goose y Qwen Code tienen **envoltorio propio** (`bvsala
58
- claude|codex|goose|qwen`): arrancan un gateway en puerto efímero, lanzan el
59
- terminal apuntándole y lo apagan al salir. Cero configuración persistente. La
63
+ Claude Code, Codex, Goose, Qwen Code, opencode, Kilo, Copilot CLI (BYOK) y Aider
64
+ tienen **envoltorio propio** (`bvsala
65
+ claude|codex|goose|qwen|opencode|kilo|copilot|aider`): arrancan un gateway en
66
+ puerto efímero, lanzan el terminal apuntándole y lo apagan al salir. Cero
67
+ configuración persistente: opencode y Kilo reciben la suya por variable
68
+ (`OPENCODE_CONFIG_CONTENT`, `KILO_CONFIG_CONTENT`), fusionada con la tuya. La
60
69
  receta manual de cada guía hace exactamente lo mismo. Para el resto,
61
70
  `bvsala doctor` imprime el bloque exacto de cada terminal instalado.
62
71
 
72
+ **Al salir, cada envoltorio dice lo que midió.** Dentro de un envoltorio el log
73
+ por petición va callado para no pisar el TUI, así que al terminar imprime por
74
+ stderr una línea como esta:
75
+
76
+ ```
77
+ [bvsala copilot] metering in this run: 41 request(s) metered with the provider's usage · 3 forwarded but NOT metered (nothing is estimated in their place): 3 × the request did not ask for usage (stream_options.include_usage). What each reason means and what to do: docs/terminales/README.md.
78
+ ```
79
+
80
+ Los motivos son los de la tabla `· not metered: …` de abajo. Si ninguna petición
81
+ pasó por el gateway, lo dice también: el terminal no estaba apuntado. `bvsala
82
+ gateway` imprime la misma línea al pararlo con Ctrl-C. Es local, como el aviso de
83
+ tráfico degradado: no viaja a BiVelio.
84
+
63
85
  ## Lo común a todas las guías
64
86
 
65
87
  ### 1 · Instalar y arrancar el gateway
@@ -192,12 +214,15 @@ Y una condición dura: el terminal debe estar instalado con su **instalador
192
214
  nativo** (un `claude.exe` / `codex.exe` en el PATH — Claude Code, Codex, Goose
193
215
  y Qwen Code lo tienen [V]). El shim `.cmd` que deja `npm i -g <terminal>` no
194
216
  puede lanzarse de forma segura con argumentos (CVE-2024-27980) y `bvsala` se
195
- niega con una guía en vez de fallar con un ENOENT mudo.
217
+ niega con una guía en vez de fallar con un ENOENT mudo. opencode, Kilo y Copilot
218
+ CLI también se instalan por npm: en Windows, usa su binario nativo (releases de
219
+ GitHub, winget o scoop) **[I: no validado en Windows]**. Aider se instala con pip,
220
+ que deja un `aider.exe`.
196
221
 
197
222
  ### 4 · Regla BYOK, sin excepción
198
223
 
199
- Todas las recetas del anillo 2 (Goose, Qwen Code, opencode, Crush, Droid, Cline,
200
- Kilo) son **API key exclusivamente**. Anthropic prohíbe usar el OAuth de Claude
224
+ Todas las recetas del anillo 2 (Goose, Qwen Code, opencode, Kilo, Copilot, Aider,
225
+ Crush, Droid, Cline, Continue) son **API key exclusivamente**. Anthropic prohíbe usar el OAuth de Claude
201
226
  Pro/Max en harnesses de terceros (opencode lo documenta; Crush retiró su OAuth en
202
227
  ene-2026). El gateway reenviaría un Bearer de suscripción sin inmutarse — y eso
203
228
  no se promociona ni se documenta como receta.
@@ -235,10 +260,33 @@ byte:**
235
260
  cache TTL, the advisor, context management, OAuth, and betas that do not exist
236
261
  yet). Three kinds stay on the hop from your client to the gateway, as HTTP
237
262
  requires: hop-by-hop headers (RFC 7230 §6.1, plus `expect` and any header named
238
- in `Connection`), `host` and `content-length`.
263
+ in `Connection`), `host` and `content-length`. One more stays with the gateway
264
+ because it is addressed to it: `x-bvsala-report`, with which a client asks for
265
+ its per-request report (below).
239
266
  - **The response**: the same bytes and headers, except the transport ones
240
267
  (`content-encoding`, `content-length`, `transfer-encoding`), because the
241
- gateway receives the body already decompressed.
268
+ gateway receives the body already decompressed. A request sent with
269
+ `x-bvsala-report: 1` also gets one header added, `x-bvsala-request-id`; a
270
+ request without it gets none.
271
+
272
+ **Per-request reports, in any language.** Send `x-bvsala-report: 1` with a
273
+ request and the gateway keeps the report it recorded for your dashboard for
274
+ that request, and answers with `x-bvsala-request-id`. Read it back from the
275
+ same gateway with `GET /_bvsala/v1/reports/<x-bvsala-request-id>` or
276
+ `GET /_bvsala/v1/reports?response_id=<the id of the provider's response>`;
277
+ add `wait_ms=<milliseconds>` (30,000 at most) to wait for a request still in
278
+ flight. The answer carries `status` (`metered`, `not_metered` with the same
279
+ `reason` as the log line, or `pending`) and, when metered, the `SavingsReport`
280
+ the gateway queued for your dashboard: nothing is computed for this answer.
281
+ `GET /_bvsala/v1/status` returns the gateway's version, upstream origin and
282
+ license state. These routes are answered by the gateway and never forwarded;
283
+ the gateway listens on 127.0.0.1 only, they send no CORS headers, they answer
284
+ 403 to a `Host` other than 127.0.0.1 or localhost (so a web page cannot reach
285
+ them by rebinding its own name to 127.0.0.1), no route lists reports (you need
286
+ the id), and like every other route they answer 402 when the license does not
287
+ serve. The gateway
288
+ keeps the last 1,000 reports for one hour at most, in memory; nothing is
289
+ written to disk and nothing more is sent to BiVelio.
242
290
 
243
291
  "No lever armed" means any of: a license without that lever's feature,
244
292
  `BVSALA_GATEWAY_ELISION=0`, or a lever you have not turned on (the sibling
@@ -254,7 +302,7 @@ would fail it.
254
302
  | Lever | Applies to | What changes | What does not |
255
303
  |---|---|---|---|
256
304
  | Cache TTL (`cache_ttl_tuning`, on by default with PRO) | `/v1/messages` requests where a large part of the prompt is new (session start, compaction); a chained turn travels untouched | every `cache_control` with `ttl: "1h"` becomes `"5m"`, the top-level one included (a 1-hour top-level marker behind 5-minute block markers would be a 400) | a key named `cache_control` inside a tool call's `input` or a tool's `input_schema` is content and stays as it is; so does the advisor's `caching`; headers are untouched |
257
- | Per-turn effort (`BVSALA_GATEWAY_ESFUERZO=low…max`) | `/v1/messages` for `claude-fable-5-1`, `claude-mythos-5-1` and `claude-opus-5`, when the request carries no `output_config` of its own (top level or in a message) | one message appended at the end of `messages`, `{"role":"system","content":[],"output_config":{"effort":…}}`, and `mid-conversation-output-config-2026-07-01` appended after your `anthropic-beta` value | your `anthropic-beta` value is kept verbatim in front; nothing earlier in `messages` moves |
305
+ | Per-turn effort (`BVSALA_GATEWAY_ESFUERZO=low` or `medium`) | `/v1/messages` for `claude-fable-5-1`, `claude-mythos-5-1`, `claude-opus-5-5` and `claude-opus-5`, when the level is below the model's default effort (`high`; `medium` on `claude-opus-5-5`) and the request carries no `output_config` of its own (top level or in a message); a level at or above the default leaves the request untouched | one message appended at the end of `messages`, `{"role":"system","content":[],"output_config":{"effort":…}}`, and `mid-conversation-output-config-2026-07-01` appended after your `anthropic-beta` value | your `anthropic-beta` value is kept verbatim in front; nothing earlier in `messages` moves |
258
306
  | Sibling model (`BVSALA_GATEWAY_MODELO_HERMANO=1`) | `/v1/messages` and `/v1/messages/count_tokens` for `claude-fable-5`, except with a forced `tool_choice` or the 1M-context beta | `model` becomes `claude-fable-5-1` | headers are untouched |
259
307
  | Context-editing dials (`BVSALA_GATEWAY_DIALES_ELISION=1`; measured as a net loss, off) | `/v1/messages` | your `clear_tool_uses_20250919` edit in `context_management` is replaced by ours | your other edits are kept, `clear_thinking_20251015` first; your `anthropic-beta` value is kept verbatim, with `context-management-2025-06-27` appended if it was not there |
260
308
  | Usage in Chat Completions streams (`BVSALA_GATEWAY_USAGE_INJECT=1`, off) | streaming `…/chat/completions` requests without `stream_options` of their own | `"stream_options":{"include_usage":true},` inserted as text right after the opening `{` | the rest of the body, byte for byte (nothing is re-serialized); headers are untouched; a 400 or 422 gets your original request once more |
@@ -0,0 +1,74 @@
1
+ # Aider a través del gateway
2
+
3
+ **Fuente:** `docs/investigacion/2026-09-27/agentes-de-codigo.md`: código de
4
+ `Aider-AI/aider` (rama `main` a 2026-05-22; la última release publicada es la
5
+ v0.86, de agosto de 2025) y de `BerriAI/litellm` 1.82.3, la versión que fija
6
+ `main` de aider. Aider llama a los modelos a través de litellm, así que las
7
+ variables que mandan son las de litellm [V].
8
+
9
+ ## Qué necesitas
10
+
11
+ - `aider` en el PATH y tu **API key** (`ANTHROPIC_API_KEY` o `OPENAI_API_KEY`).
12
+ - `bvsala` instalado, `BIVELIO_SERVICE_KEY` exportada y una licencia BiVelio activa (ver [README](README.md)).
13
+
14
+ ## Envoltorio: `bvsala aider [args…]`
15
+
16
+ ```bash
17
+ bvsala aider # modelos de Anthropic
18
+ BVSALA_GATEWAY_UPSTREAM=openai bvsala aider --model gpt-… # modelos de OpenAI
19
+ ```
20
+
21
+ Un gateway habla con un solo proveedor, así que el envoltorio enruta uno:
22
+
23
+ - **Anthropic (por defecto).** Con `ANTHROPIC_API_KEY` presente, aider elige
24
+ Sonnet por defecto [V]. El envoltorio exporta `ANTHROPIC_API_BASE`, que litellm
25
+ lee **antes** que `ANTHROPIC_BASE_URL`, y litellm añade `/v1/messages` si la
26
+ URL no termina así: **sin `/v1`** [V].
27
+ - **OpenAI**, con `BVSALA_GATEWAY_UPSTREAM=openai`: exporta `OPENAI_BASE_URL` y
28
+ `OPENAI_API_BASE` (litellm las lee en ese orden), **con `/v1`** [V].
29
+
30
+ Lo que no es del proveedor enrutado va directo y no se mide (por ejemplo, un
31
+ modelo débil de otro proveedor). Al salir, el envoltorio imprime cuántas
32
+ peticiones midió y cuáles reenvió sin medir, y por qué.
33
+
34
+ ### La trampa de OpenAI: el usage en stream
35
+
36
+ litellm solo añade `stream_options: {"include_usage": true}` cuando habla con
37
+ `api.openai.com` **directamente** (`get_stream_options`: sin `api_base`, o con
38
+ host `api.openai.com`) [V]. Detrás del gateway el host es `127.0.0.1`, así que
39
+ los streams de OpenAI **no piden su contador** y se reenvían sin medir. Dos
40
+ salidas:
41
+
42
+ ```bash
43
+ BVSALA_GATEWAY_UPSTREAM=openai BVSALA_GATEWAY_USAGE_INJECT=1 bvsala aider --model gpt-…
44
+ BVSALA_GATEWAY_UPSTREAM=openai bvsala aider --model gpt-… --no-stream
45
+ ```
46
+
47
+ La primera añade la opción en el gateway (el último chunk llega con `choices`
48
+ vacío, lo mismo que aider recibe cuando habla con OpenAI directo); la segunda
49
+ pide respuestas enteras, que traen el `usage` en el JSON. Anthropic no tiene
50
+ esta trampa: su stream siempre trae el contador.
51
+
52
+ ## Manual
53
+
54
+ ```bash
55
+ bvsala gateway # 8402, Anthropic
56
+ ANTHROPIC_API_BASE=http://127.0.0.1:8402 aider # SIN /v1
57
+
58
+ bvsala gateway --upstream openai --port 8403
59
+ OPENAI_API_BASE=http://127.0.0.1:8403/v1 aider --model gpt-… # CON /v1
60
+ ```
61
+
62
+ ## La trampa típica
63
+
64
+ - `--openai-api-base` en la línea de comandos o `openai-api-base:` en
65
+ `.aider.conf.yml` pisan la variable (aider la reescribe al arrancar) [V].
66
+ - Un `api_base` en `extra_params` de `.aider.model.settings.yml` manda sobre
67
+ todo: esa petición no pasa por el gateway.
68
+
69
+ ## Qué ya mide aider
70
+
71
+ Aider enseña tokens y coste por mensaje, con los precios de litellm; cuando la
72
+ respuesta no trae `usage`, cuenta los tokens con su tokenizador local, que es
73
+ una estimación [V]. El gateway añade el contador del proveedor en cada
74
+ petición, el aviso de lo que no pudo medir y el panel del equipo.
@@ -0,0 +1,47 @@
1
+ # Continue (CLI `cn` y extensiones) a través del gateway
2
+
3
+ **Fuente:** `docs/investigacion/2026-09-27/agentes-de-codigo.md`: código de
4
+ `continuedev/continue` (extensión v2.1.0, junio de 2026) y su doc de `cn`. Continue
5
+ se configura por **archivo** (`~/.continue/config.yaml`, el mismo para la CLI y
6
+ las extensiones de VS Code y JetBrains) [V]: no hay envoltorio, `bvsala doctor`
7
+ imprime el bloque.
8
+
9
+ ## La forma del URL [V]
10
+
11
+ El proveedor `anthropic` de Continue tiene `apiBase` por defecto
12
+ `https://api.anthropic.com/v1/`, añade la barra final si falta y resuelve
13
+ `new URL("messages", apiBase)`. Por eso: **con `/v1`**; la barra final la pone él.
14
+
15
+ ## `~/.continue/config.yaml`
16
+
17
+ ```yaml
18
+ models:
19
+ - name: Claude via bvsala
20
+ provider: anthropic
21
+ model: <Anthropic model id>
22
+ apiKey: <your Anthropic API key>
23
+ apiBase: http://127.0.0.1:8402/v1
24
+ ```
25
+
26
+ Y `bvsala gateway` corriendo aparte. En la CLI también vale `cn --config <fichero>`
27
+ con un fichero que lleve ese `apiBase`.
28
+
29
+ ## Comprobar
30
+
31
+ ```
32
+ [gateway] POST /v1/messages · <modelo> · $0.001234 (in 120, cache↑0 cache↓0, out 8)
33
+ ```
34
+
35
+ - `[gateway] POST /messages → 404` → falta el `/v1`.
36
+ - nada en el log → el modelo activo no es el de este bloque.
37
+
38
+ ## La trampa típica
39
+
40
+ - **Gateway apagado con la config escrita**: Continue falla con ese modelo hasta
41
+ que arranques `bvsala gateway`.
42
+ - El autocompletado usa su propio modelo (`roles: [autocomplete]`): si no apunta
43
+ al gateway, no se mide.
44
+
45
+ ## Nota BYOK
46
+
47
+ **API key exclusivamente.** Nunca el OAuth de una suscripción Claude Pro/Max.
@@ -0,0 +1,114 @@
1
+ # GitHub Copilot (CLI y VS Code) a través del gateway — solo con tu propia clave
2
+
3
+ **Fuente:** `docs/investigacion/2026-09-27/agentes-de-codigo.md`: la doc oficial
4
+ de GitHub «Using your own LLM models in GitHub Copilot CLI» (fuente en
5
+ `github/docs`, consultada el 2026-09-27) y la de VS Code «AI language models in
6
+ VS Code» (fechada el 16-09-2026).
7
+
8
+ **Lo primero:** Copilot solo pasa por un base URL cuando usas **tu propia clave
9
+ de proveedor (BYOK)**. Los modelos alojados por GitHub van a GitHub y no se
10
+ pueden medir aquí; su gasto lo enseña `/usage` (créditos de IA de GitHub y
11
+ tokens por modelo). Con BYOK, la CLI funciona **sin cuenta de GitHub** [V].
12
+
13
+ ## Copilot CLI
14
+
15
+ ### Qué necesitas
16
+
17
+ - `copilot` en el PATH (en Windows, el instalador nativo: el shim `.cmd` de npm no
18
+ se puede lanzar con argumentos de forma segura y `bvsala` se niega con una guía).
19
+ - BYOK configurado y funcionando **directo** [V]:
20
+
21
+ ```bash
22
+ export COPILOT_PROVIDER_TYPE=anthropic
23
+ export COPILOT_PROVIDER_BASE_URL=https://api.anthropic.com
24
+ export COPILOT_PROVIDER_API_KEY=… # tu clave de Anthropic
25
+ export COPILOT_MODEL=<modelo de Claude>
26
+ ```
27
+
28
+ (Con OpenAI, el tipo por defecto, el base URL es `https://api.openai.com/v1`: ver «El tipo `openai`» abajo.)
29
+ - `bvsala` instalado, `BIVELIO_SERVICE_KEY` exportada y una licencia BiVelio activa (ver [README](README.md)).
30
+
31
+ ### Envoltorio: `bvsala copilot [args…]`
32
+
33
+ ```bash
34
+ bvsala copilot
35
+ ```
36
+
37
+ Con `COPILOT_PROVIDER_TYPE=anthropic`, arranca el gateway contra
38
+ `api.anthropic.com` y lanza `copilot` con `COPILOT_PROVIDER_BASE_URL` apuntando
39
+ al gateway, **con la misma ruta que ya tenías**. Tu modelo y tu clave no se
40
+ tocan. Al salir imprime cuántas peticiones midió y cuáles reenvió sin medir, y
41
+ por qué.
42
+
43
+ Se niega, antes de arrancar nada y con el motivo, cuando:
44
+
45
+ - no hay `COPILOT_PROVIDER_BASE_URL` (estás en los modelos de GitHub: nada que medir);
46
+ - el tipo es `openai` (el de por defecto): todavía no, ver abajo;
47
+ - el tipo es `azure` u otro, o el base URL no es `https://api.anthropic.com`: el
48
+ gateway solo tarifa la API del propio proveedor a su precio de lista, y Azure,
49
+ un modelo local o un revendedor facturan distinto (o nada).
50
+
51
+ ### Manual (tipo `anthropic`)
52
+
53
+ ```bash
54
+ bvsala gateway # Anthropic, 8402
55
+ COPILOT_PROVIDER_BASE_URL=http://127.0.0.1:8402 copilot # SIN /v1
56
+ ```
57
+
58
+ Cada `POST /v1/messages` se mide con su contador (Messages siempre lo trae).
59
+
60
+ ### El tipo `openai`: sin envoltorio hasta validarlo en mano
61
+
62
+ La doc oficial lo describe como «any other OpenAI Chat Completions API‑compatible
63
+ endpoint» [V], pero el changelog de la CLI dice que desde la 1.0.64 también
64
+ habla la **Responses API por WebSocket** con BYOK («Add websocket responses
65
+ support for BYOK OpenAI-compatible providers») [V]. El gateway responde **426**
66
+ a todo intento de WebSocket (así cae Codex a HTTP), y **no sabemos** si la CLI
67
+ de Copilot hace lo mismo: la CLI no es de código abierto. Un envoltorio que
68
+ puede romper la sesión no se publica sin probarlo. A mano, y mirando el log:
69
+
70
+ ```bash
71
+ bvsala gateway --upstream openai --port 8403
72
+ COPILOT_PROVIDER_BASE_URL=http://127.0.0.1:8403/v1 copilot # CON /v1
73
+ ```
74
+
75
+ - `… → 426 (WebSocket not supported …)` seguido de líneas `POST /v1/responses`
76
+ o `POST /v1/chat/completions` con `$` → la CLI cayó a HTTP y se mide.
77
+ - Solo el 426 y la sesión falla → no funciona detrás del gateway; vuelve a la
78
+ URL directa.
79
+ - `not metered: the request did not ask for usage` → relanza el gateway con
80
+ `BVSALA_GATEWAY_USAGE_INJECT=1`.
81
+
82
+ ### Lo que no pasa por aquí
83
+
84
+ Lo que va a GitHub con BYOK igualmente (`/delegate`, búsqueda de código,
85
+ telemetría sin `COPILOT_OFFLINE=true`) no es inferencia de tu proveedor [V].
86
+
87
+ ## Copilot en VS Code (BYOK, «Custom Endpoint»)
88
+
89
+ No se detecta ni se envuelve (es una extensión): la receta la imprime `bvsala doctor`.
90
+
91
+ 1. **Manage Language Models** → **Add Models** → **Custom Endpoint**.
92
+ 2. Tipo de API: **Messages** (Anthropic). VS Code abre `chatLanguageModels.json`.
93
+ 3. En el modelo, la **ruta completa** en `url`:
94
+
95
+ ```json
96
+ "url": "http://127.0.0.1:8402/v1/messages"
97
+ ```
98
+
99
+ VS Code usa tal cual una URL que ya lleva `/messages`, `/chat/completions` o
100
+ `/responses`; si no, añade la ruta y, si falta, `/v1` [V]. Con la ruta
101
+ completa no hay nada que adivinar.
102
+ 4. `bvsala gateway` corriendo aparte.
103
+
104
+ El BYOK de VS Code cubre el chat y las tareas auxiliares (títulos, mensajes de
105
+ commit) [V]; las sugerencias en línea y los embeddings siguen en GitHub y no se
106
+ miden aquí. Las tareas auxiliares usan `chat.utilityModel` y
107
+ `chat.utilitySmallModel`, no el modelo del chat: si quieres medirlas, apúntalas
108
+ también a un modelo del Custom Endpoint.
109
+
110
+ ## Nota BYOK
111
+
112
+ Tu clave, tu factura: es exactamente el caso que el gateway mide. Nunca enrutes
113
+ por aquí el OAuth de una suscripción Claude Pro/Max: Anthropic lo prohíbe en
114
+ harnesses de terceros.
@@ -1,50 +1,73 @@
1
1
  # Kilo Code a través del gateway
2
2
 
3
- **Fuente:** `docs/PASARELA-S3-TERMINALES.md` §3. Kilo Code configura por **UI**
4
- (proveedor custom con Provider API «Anthropic Messages»; la config queda en
5
- `kilo.json`, detectable) [V]. **La forma del base URL es inferida [I] por
6
- linaje Cline: probar con `/v1` primero y validar en mano contra el log del
7
- gateway.** No hay validación hecha; esta guía lo dice tal cual.
3
+ **Fuente:** `docs/investigacion/2026-09-27/agentes-de-codigo.md` (código de
4
+ `Kilo-Org/kilocode` v7.8.1, 2026-09-25). Desde la serie 7, Kilo es un fork de
5
+ opencode (`.opencode-version`: v1.18.26): la CLI (`kilo`), la extensión de VS
6
+ Code y la de JetBrains leen la misma configuración, `kilo.json`, y el proveedor
7
+ `anthropic` se apunta con `provider.anthropic.options.baseURL`. Su capa de
8
+ protocolo añade `/messages` a `https://api.anthropic.com/v1`: **con `/v1`** [V].
9
+ Esto sustituye a la receta anterior (proveedor custom por UI, forma inferida).
8
10
 
9
11
  ## Qué necesitas
10
12
 
11
- - Kilo Code instalado en VS Code.
12
- - Tu **API key** de Anthropic.
13
- - `bvsala gateway` corriendo en `http://127.0.0.1:8402` (necesita `BIVELIO_SERVICE_KEY` y una licencia BiVelio activa; ver [README](README.md)).
13
+ - Kilo (CLI `kilo`, o la extensión) con el proveedor **`anthropic`** y tu **API key** de Anthropic.
14
+ - `bvsala` instalado, `BIVELIO_SERVICE_KEY` exportada y una licencia BiVelio activa (la recoge el gateway con la clave; ver [README](README.md)).
14
15
 
15
- ## Pasos en la UI
16
+ **Lo que no pasa por aquí:** el proveedor por defecto de Kilo es su propia
17
+ pasarela (Kilo Gateway, modelos alojados por Kilo con su cuenta). Ese tráfico va
18
+ a Kilo, no a un base URL tuyo, y no se mide. Tampoco los demás proveedores.
16
19
 
17
- 1. Ajustes de Kilo Code → añade un **proveedor custom**.
18
- 2. **Provider API**: «Anthropic Messages».
19
- 3. Pega tu **API key** de Anthropic.
20
- 4. **Base URL** — prueba primero:
20
+ ## Envoltorio (CLI): `bvsala kilo [args…]`
21
21
 
22
- ```
23
- http://127.0.0.1:8402/v1
24
- ```
22
+ ```bash
23
+ export BIVELIO_SERVICE_KEY=bvk_…
24
+ bvsala kilo
25
+ ```
25
26
 
26
- **Sin barra final.**
27
- 5. Elige el modelo y guarda.
27
+ Kilo acepta la configuración entera en `KILO_CONFIG_CONTENT` y la fusiona por
28
+ encima de la global y de la de proyecto [V]. El envoltorio arranca un gateway
29
+ efímero y lanza `kilo` con esa variable apuntando el proveedor `anthropic` a
30
+ `http://127.0.0.1:<puerto>/v1`. **No escribe nada en disco.** Si ya exportas tu
31
+ propio `KILO_CONFIG_CONTENT`, lo fusiona y solo cambia ese `baseURL`; si no es un
32
+ objeto JSON, se niega antes de arrancar sin repetir su valor. Al salir imprime
33
+ cuántas peticiones midió y cuáles reenvió sin medir, y por qué.
28
34
 
29
- ## Validar en mano (obligatorio antes de fiarte)
35
+ ## Manual (CLI y extensiones): `~/.config/kilo/kilo.json`
30
36
 
31
- Manda un mensaje trivial y mira la ventana del gateway:
37
+ ```json
38
+ {
39
+ "provider": {
40
+ "anthropic": {
41
+ "options": {
42
+ "baseURL": "http://127.0.0.1:8402/v1"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ ```
32
48
 
33
- - `[gateway] POST /v1/messages · <modelo> · $…` → **con `/v1`** es la forma
34
- buena; confírmalo en S3 (pasar el [I] a [V]).
35
- - `[gateway] POST /v1/v1/messages → 404` → Kilo ya añade `/v1`: quita el `/v1`
36
- del base URL (`http://127.0.0.1:8402`) y repite.
37
- - `[gateway] POST /messages → 404` → falta el `/v1`; vuelve a ponerlo.
38
- - nada en el log → el proveedor custom no es el activo.
49
+ **Con `/v1`, sin barra final.** Precedencia (de menos a más): global
50
+ (`~/.config/kilo/kilo.json`), `KILO_CONFIG`, proyecto (`./kilo.json`,
51
+ `.kilo/kilo.json`), `KILO_CONFIG_CONTENT`, gestionada [V]. Arranca el gateway
52
+ aparte con `bvsala gateway`.
53
+
54
+ ## Comprobar
55
+
56
+ ```
57
+ [gateway] POST /v1/messages · <modelo> · $0.001234 (in 120, cache↑0 cache↓0, out 8)
58
+ ```
59
+
60
+ - `[gateway] POST /messages → 404` → falta el `/v1`.
61
+ - `[gateway] POST /v1/v1/messages → 404` → sobra un `/v1`.
62
+ - nada en el log → el modelo activo no es del proveedor `anthropic` (¿Kilo Gateway?).
39
63
 
40
64
  ## La trampa típica
41
65
 
42
- - **Adivinar el `/v1`**: de ahí el paso de validación. Un URL mal formado falla
43
- en silencio (404 del upstream, nada medido, error genérico en la UI).
44
- - **Gateway apagado**: `kilo.json` recuerda el base URL; sin `bvsala gateway`
45
- corriendo, Kilo fallará hasta que lo arranques o cambies de proveedor.
66
+ - **Gateway apagado con la config escrita**: `kilo.json` recuerda el base URL;
67
+ sin `bvsala gateway` corriendo, Kilo falla hasta que lo arranques o quites la
68
+ clave. El envoltorio no tiene esta trampa.
46
69
 
47
70
  ## Nota BYOK
48
71
 
49
72
  **API key exclusivamente.** Anthropic prohíbe usar el OAuth de Claude Pro/Max en
50
- harnesses de terceros; esta receta es la del proveedor custom con API key.
73
+ harnesses de terceros; esta receta es la del proveedor `anthropic` con API key.
@@ -0,0 +1,41 @@
1
+ # Lo que el gateway no mide hoy, y por qué
2
+
3
+ **Fuente:** `docs/investigacion/2026-09-27/agentes-de-codigo.md`. Decir que no es
4
+ parte de la receta: un terminal apuntado al gateway que no se puede medir sería
5
+ pagar una licencia por un proxy vacío.
6
+
7
+ ## Gemini CLI — no se mide todavía
8
+
9
+ Gemini CLI acepta un base URL propio: `GOOGLE_GEMINI_BASE_URL` (y
10
+ `GOOGLE_VERTEX_BASE_URL` con Vertex), en HTTPS salvo que apunte a `localhost` o
11
+ `127.0.0.1` [V: `docs/reference/configuration.md` de `google-gemini/gemini-cli`,
12
+ 2026-09-27]. El problema no es enrutarlo: es que habla la **API de Gemini**
13
+ (`…/models/<modelo>:streamGenerateContent`, contador en `usageMetadata`), y el
14
+ gateway solo sabe leer el contador de Anthropic Messages y de OpenAI Chat
15
+ Completions y Responses. Cada petición se reenviaría con la línea
16
+ `not metered: no usage dialect for this route`, sin coste.
17
+
18
+ Hasta que el gateway lea ese dialecto, el contador de Gemini CLI es su propio
19
+ `/stats model` (tokens por modelo y cuota) [V]. Qué haría falta para medirlo, y
20
+ por qué no se ha hecho hoy (los precios de contexto largo de los modelos Pro),
21
+ está en el informe.
22
+
23
+ ## Cursor — no se puede
24
+
25
+ Cursor manda **todas** las peticiones, también las de tu propia clave, a través
26
+ de sus servidores: «all requests are routed through Cursor's servers for final
27
+ prompt building» [V: cursor.com/docs/settings/api-keys, 2026-09-27]. Un gateway
28
+ en `127.0.0.1` es inalcanzable desde ahí, y exponerlo a Internet no es una
29
+ opción (escucha solo en loopback a propósito). Las claves propias, además, solo
30
+ cubren los modelos de chat; Tab sigue con los modelos de Cursor [V]. El contador
31
+ de Cursor es su panel de uso.
32
+
33
+ ## Copilot con modelos de GitHub — no pasa por un base URL
34
+
35
+ Sin BYOK, Copilot (CLI y VS Code) usa los modelos alojados por GitHub, que van a
36
+ GitHub y no a un base URL tuyo. Con BYOK sí se mide: ver [copilot.md](copilot.md).
37
+
38
+ ## Kilo Gateway — no pasa por un base URL tuyo
39
+
40
+ El proveedor por defecto de Kilo son sus modelos alojados; ese tráfico va a Kilo.
41
+ Con el proveedor `anthropic` y tu clave sí se mide: ver [kilo.md](kilo.md).
@@ -1,15 +1,40 @@
1
1
  # opencode a través del gateway
2
2
 
3
- **Fuente:** `docs/PASARELA-S3-TERMINALES.md` §3 [V]. opencode enruta por
4
- **archivo de configuración**, no por variable de entorno, y su capa
5
- `packages/llm` añade `/messages` al `baseURL`: por eso aquí el URL lleva `/v1`.
3
+ **Fuente:** `docs/PASARELA-S3-TERMINALES.md` §3 [V] y
4
+ `docs/investigacion/2026-09-27/agentes-de-codigo.md` (código de
5
+ `anomalyco/opencode` v1.18.32, 2026-09-27). opencode enruta por **configuración**,
6
+ y su capa de protocolo añade `/messages` a `https://api.anthropic.com/v1` por
7
+ defecto: por eso aquí el URL lleva `/v1` [V].
6
8
 
7
9
  ## Qué necesitas
8
10
 
9
11
  - `opencode` instalado, proveedor `anthropic` con tu **API key** de Anthropic.
10
12
  - `bvsala` instalado, `BIVELIO_SERVICE_KEY` exportada y una licencia BiVelio activa (la recoge el gateway con la clave; ver [README](README.md)).
11
13
 
12
- ## Config de usuario: `~/.config/opencode/opencode.json`
14
+ ## Envoltorio: `bvsala opencode [args…]`
15
+
16
+ ```bash
17
+ export BIVELIO_SERVICE_KEY=bvk_…
18
+ bvsala opencode
19
+ ```
20
+
21
+ opencode acepta la configuración entera en la variable `OPENCODE_CONFIG_CONTENT`
22
+ y la fusiona **por encima** de la global y de la de proyecto (`config.ts`: se
23
+ carga después de `opencode.json` global, del de proyecto y de `.opencode/`; solo
24
+ la config de cuenta remota y la gestionada por la empresa van después) [V]. El
25
+ envoltorio arranca un gateway efímero y lanza `opencode` con esa variable
26
+ apuntando `provider.anthropic.options.baseURL` a `http://127.0.0.1:<puerto>/v1`.
27
+ **No escribe nada en disco** y todo muere al salir.
28
+
29
+ - Si ya exportas tu propio `OPENCODE_CONFIG_CONTENT`, el envoltorio lo
30
+ **fusiona**: solo cambia `provider.anthropic.options.baseURL`; tu clave, tus
31
+ modelos y lo demás siguen igual. Si no es un objeto JSON (por ejemplo, JSONC
32
+ con comentarios), se niega antes de arrancar y no repite su valor.
33
+ - Solo pasa por el gateway el proveedor `anthropic`. Los demás proveedores
34
+ (OpenAI, OpenCode Zen, OpenRouter…) van directos y no se miden.
35
+ - Al salir imprime cuántas peticiones midió y cuáles reenvió sin medir, y por qué.
36
+
37
+ ## Manual: `~/.config/opencode/opencode.json`
13
38
 
14
39
  ```json
15
40
  {
@@ -27,11 +52,6 @@
27
52
  [V]. El `opencode.json` de proyecto pisa al global: si tienes uno en el repo,
28
53
  ahí es donde manda.
29
54
 
30
- No hay envoltorio `bvsala opencode`: el enrutado es config de usuario. El
31
- `bvsala doctor --write opencode` previsto en S3 hará un merge JSON conservador
32
- de este bloque (con copia `.bak`) en tu config de usuario, nunca en archivos
33
- del repo.
34
-
35
55
  ### Alternativa [V]: `baseURL` desde variable de entorno
36
56
 
37
57
  ```json
@@ -56,10 +76,12 @@ opencode
56
76
 
57
77
  - **Sin `/v1`** → opencode pide `http://127.0.0.1:8402/messages` → `[gateway] POST /messages → 404`.
58
78
  - **Barra final** (`…/v1/`) → no.
59
- - **Config de proyecto** con otro `baseURL` pisando al global sin que lo sepas.
79
+ - **Config de proyecto** con otro `baseURL` pisando al global sin que lo sepas
80
+ (con el envoltorio no pasa: su variable manda sobre los dos).
60
81
  - **Gateway apagado con la config escrita**: opencode falla hasta que arranques
61
82
  `bvsala gateway` de nuevo. La config es persistente; el gateway, no. (La
62
- variante `{env:…}` sin exportar la variable también falla, por vacío.)
83
+ variante `{env:…}` sin exportar la variable también falla, por vacío.) El
84
+ envoltorio no tiene esta trampa.
63
85
 
64
86
  ## Nota BYOK
65
87