From 84e50152adcf0704f6a27405e99e3eef65a6a0ac Mon Sep 17 00:00:00 2001 From: borjaperfra Date: Wed, 30 Sep 2026 12:00:40 +0200 Subject: [PATCH 1/2] fix(codex): que los perfiles no manden las herramientas que el cluster no sabe streamear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Codex 0.159 aƱade por su cuenta dos herramientas a cada peticion: las de sub-agentes como una herramienta "namespace" y la busqueda como "web_search". Con cualquiera de las dos en la peticion, /responses deja de emitir items para qwen3.8-flash y mimo y solo devuelve response.completed, del que Codex no lee respuesta: el turno acaba sin texto y sin llamada a herramienta. Ninguna de las dos tiene adonde ir en el cluster. Los perfiles que escribimos por modelo ponen ahora web_search = "disabled" y multi_agent = false en [features]. Va en los perfiles y no en config.toml porque esos ficheros son nuestros y se van con el disconnect; un web_search = "disabled" en la raiz se quedaria y le quitaria la busqueda tambien a las sesiones de OpenAI de la persona. El test comprueba las dos claves en cada perfil, y que web_search quede antes de [features], donde TOML lo meteria dentro de la tabla y Codex no lo leeria. Co-Authored-By: Claude Opus 5.5 --- internal/tui/config_test.go | 28 ++++++++++++++++++++++++++++ internal/tui/tui.go | 14 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/internal/tui/config_test.go b/internal/tui/config_test.go index 1879c7a..973a857 100644 --- a/internal/tui/config_test.go +++ b/internal/tui/config_test.go @@ -552,6 +552,34 @@ func TestCodexProfilesGiveEveryModelItsOwnWindow(t *testing.T) { // Codex refuses a --profile with a dot in it ("pass a plain name such as // `work`"), which is every id on the cluster that carries a version number. +// With the sub-agent namespace or web_search in the request, /responses +// answers qwen3.8-flash and mimo with a lone response.completed, and Codex +// ends the turn empty. The profiles are what keeps those tools out. +func TestCodexProfilesLeaveOutTheToolsTheClusterCannotStream(t *testing.T) { + path := tempConfig(t, "config.toml") + if err := writeCodexConfig(path, testKey); err != nil { + t.Fatal(err) + } + home := filepath.Dir(path) + for _, m := range catalog.ChatModels() { + data, err := os.ReadFile(filepath.Join(home, codexProfileName(m.ID)+".config.toml")) + if err != nil { + t.Fatal(err) + } + body := string(data) + if !strings.Contains(body, `web_search = "disabled"`) { + t.Errorf("%s: the profile still sends web_search:\n%s", m.ID, body) + } + if !strings.Contains(body, "[features]\nmulti_agent = false") { + t.Errorf("%s: the profile still sends the sub-agent namespace:\n%s", m.ID, body) + } + // Before the first header, or TOML puts it inside [features]. + if strings.Index(body, "web_search") > strings.Index(body, "[features]") { + t.Errorf("%s: web_search landed inside [features], where Codex does not read it:\n%s", m.ID, body) + } + } +} + func TestCodexProfileNamesAreNamesCodexAccepts(t *testing.T) { for _, m := range catalog.ChatModels() { name := codexProfileName(m.ID) diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 6212f99..5b1703d 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -2502,12 +2502,26 @@ wire_api = "responses" // // No API key goes in them: the provider section in config.toml is what // carries it, and a key in eight files is a key to rotate in eight files. +// +// They also switch off the two tools Codex adds on its own. Codex 0.159 +// sends the sub-agent tools as a "namespace" tool and web search as a +// "web_search" one, and with either in the request the cluster's /responses +// stops streaming items for qwen3.8-flash and mimo: all that comes back is +// response.completed, which Codex does not read an answer from, so the turn +// ends with no text and no tool call. Neither tool has anywhere to go on the +// cluster anyway. Here and not in config.toml, because these files are ours +// and leave with the disconnect - a web_search = "disabled" at the root +// would stay behind and take search from the member's OpenAI sessions too. func writeCodexProfiles(codexHome string) error { for _, m := range catalog.ChatModels() { body := fmt.Sprintf(`# Written by the NaN CLI. Disconnecting Codex removes it. model = %q model_provider = "nan" model_context_window = %d +web_search = "disabled" + +[features] +multi_agent = false `, m.ID, m.Context) path := filepath.Join(codexHome, codexProfileName(m.ID)+".config.toml") if err := writeConfigFile(path, []byte(body)); err != nil { From 32caa3aaad1f0579855c57f8bcebbfe71fbbe28e Mon Sep 17 00:00:00 2001 From: borjaperfra Date: Wed, 30 Sep 2026 12:00:40 +0200 Subject: [PATCH 2/2] fix(costs): Opus 5.5, Sonnet 5.5, GPT-6.1 Sol y GPT-6 Luna en la comparativa Anthropic y OpenAI tienen modelos nuevos desde que se leyo la tabla el 2026-09-14. Releidas sus paginas de precios el 2026-09-30: - Claude Opus 5 ($5/$25) pasa a Opus 5.5 ($4/$20) - Claude Sonnet 5 pasa a Sonnet 5.5 (mismo $2/$10) - GPT-5.6 Sol ($4/$20) pasa a GPT-6.1 Sol ($2/$10) - GPT-5.6 Luna ($0.20/$1.20) pasa a GPT-6 Luna ($0.10/$0.50) Terra se queda en 5.6 porque no hay GPT-6 Terra. Las filas nuevas son mas baratas que las que sustituyen, y eso no es infravalorar a la competencia: es el precio del modelo que alguien usaria hoy. Como con Gemini 3.1 Pro, las filas de OpenAI van al precio de prompt corto (hasta 272k tokens). Co-Authored-By: Claude Opus 5.5 --- internal/tui/tui.go | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/internal/tui/tui.go b/internal/tui/tui.go index 5b1703d..d2e04ad 100644 --- a/internal/tui/tui.go +++ b/internal/tui/tui.go @@ -1305,12 +1305,18 @@ type providerPricing struct { outPer1M float64 // $ per 1M output tokens } -// Per 1M tokens, read off each vendor's own pricing page on 2026-09-14. +// Per 1M tokens, read off each vendor's own pricing page on 2026-09-14, and +// the Anthropic and OpenAI rows again on 2026-09-30 when both had newer +// models out: Opus 5.5 and Sonnet 5.5 for Opus 5 and Sonnet 5, GPT-6.1 Sol and +// GPT-6 Luna for their 5.6 namesakes. Terra stays at 5.6 because OpenAI has +// not shipped a GPT-6 one. The newer rows are cheaper than the ones they +// replace, and that is not the understating the paragraph below is about: +// each is the price of the model a member would reach for today. // // Ten rows rather than the six this started as, and the reason is the spread // rather than the count. The tab multiplies a member's NaN token usage by each // of these, so a table that carried only mid-range models answered only the -// mid-range question. From Luna at $0.20 in to Astra and Fable at $10, a +// mid-range question. From Luna at $0.10 in to Astra and Fable at $10, a // reader can find the row that matches what they would actually have reached // for instead of taking ours as the comparison. // @@ -1321,20 +1327,21 @@ type providerPricing struct { // whose whole claim is "this is what you would have paid elsewhere" // understating every competitor is the one direction it must not be wrong in. // -// Two rows carry a condition the table cannot express, so each is taken at its -// lowest published rate and the comparison stays conservative: Gemini 3.1 Pro +// Some rows carry a condition the table cannot express, so each is taken at its +// lowest published rate and the comparison stays conservative: the GPT-6 and +// GPT-5.6 rows cost more above a 272k-token prompt, Gemini 3.1 Pro // costs $4/$18 above a 200k-token prompt, and Gemini 3.8 Flash is on a // promotional rate that doubles on 2027-01-01. TestGeminiFlashPromoHasNotExpired // fails on that date so the number is changed rather than forgotten. var pricingTable = []providerPricing{ {"Claude Fable 5.1", "Anthropic", 10.00, 50.00}, - {"Claude Opus 5", "Anthropic", 5.00, 25.00}, - {"Claude Sonnet 5", "Anthropic", 2.00, 10.00}, + {"Claude Opus 5.5", "Anthropic", 4.00, 20.00}, + {"Claude Sonnet 5.5", "Anthropic", 2.00, 10.00}, {"Claude Haiku 4.5", "Anthropic", 1.00, 5.00}, {"GPT-6 Astra", "OpenAI", 10.00, 50.00}, - {"GPT-5.6 Sol", "OpenAI", 4.00, 20.00}, + {"GPT-6.1 Sol", "OpenAI", 2.00, 10.00}, {"GPT-5.6 Terra", "OpenAI", 2.00, 12.00}, - {"GPT-5.6 Luna", "OpenAI", 0.20, 1.20}, + {"GPT-6 Luna", "OpenAI", 0.10, 0.50}, {"Gemini 3.1 Pro", "Google", 2.00, 12.00}, {"Gemini 3.8 Flash", "Google", 0.75, 3.75}, }