Second batch from the ten read-only audits; 126 bridge tests. Among them: a spoken number
("opus beş beş") resolves, a used route no longer expires after 7 days, the local reply says how to
go back, an unrouted 9r/ name gets a local 503 instead of going to chatgpt.com, a combo request is not
resent after the upstream closed it, the last model list is served when chatgpt.com is unreachable,
trimming keeps the real last user message, Claude-member combos use the Claude estimate, CRLF SSE is
handled, log lines carry the date and the status endpoint reports a code fingerprint.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7.7 KiB
AITURK IDE Codex bridge
Local HTTP proxy that AITURK IDE's Settings → Codex Bağlama runs for OpenAI Codex
(desktop or CLI) on the same computer. Standard-library Python only; it runs on the
agent venv's interpreter and keeps running after the IDE window closes, because
Codex depends on it while openai_base_url points here.
bridge.py: the proxy. Subscription models go to chatgpt.com unchanged;ide/<id>models go to the AITURK gateway configured in the agent'sconfig.yaml, with the key read from the agent's.envon every catalog refresh (never written elsewhere).ide_catalog.py: which IDE models and combos are added to Codex's model list and how they are named (provider/model, combos by bare name). The public/modelscatalog supplies every OmniRoute chat model; a member does not need a private local model allowlist./router/catalogsupplies only thekindmetadata used for sorting, never additional model IDs. Direct 9Router entries and internal 9Router combos are excluded. Codex's own subscription models stay first, in their own order and under their own names; added entries follow them: combos A–Z, then individual models by provider and name. A saved model selection remains unchanged. A failed or empty refresh keeps the last successful catalog.ozet_kopru.py: conversation summaries are produced as plain text, so a summary made by one provider stays readable after the user switches to another.gemini_context.py,resim_context.py: request fixes for routed models.
Run: python bridge.py --home <data dir> --hermes-home <agent home>. The IDE writes
<data dir>/bridge.json and reads GET /aiturk/codex-baglama/status;
POST /aiturk/codex-baglama/stop ends the process.
Tests: python -m unittest discover in this directory.
Version 8 (IDE beta.27): changing the model by saying so
Measured on a live voice chat (11 Oct 2026): a model picked on the phone reaches this machine only with a typed
message that STARTS a turn; a message that joins a running turn (including the lone .) does not carry it.
model_gecis.py therefore handles the change in the bridge. A short user message such as "modeli gemini flash low
yap" (spoken through the voice assistant or typed) is matched against the model list; the chat's requests are then
routed to that model (model-yonlendirme.json, keyed by prompt_cache_key) until Codex sends a different model
for the chat, i.e. until the user picks another one in the list.
- Idle chat: the bridge answers itself ("Sohbetin modeli artık X ..."), which also tells the voice assistant.
- Running turn: the command is replaced by a handover note and the SAME turn continues on the new model.
- "seçili model hangisi" / "ana model kim" is answered by the bridge with the model actually in use.
- An ambiguous name changes nothing and lists the closest names; a name matching no model is not a command.
- A lone
.that joins a running turn is dropped instead of answered (answering it cut the work short). - Handled commands left in the history are turned into notes so a later model does not answer them again.
Setting: sesle_model (default on).
Audit follow-up (11 Oct 2026, same version number; GET .../status now also returns kod, the first 8 hex digits
of bridge.py's SHA-256, to tell builds apart):
- Numbers said as words ("opus beş beş", "üç nokta sekiz") and a trailing "ve devam et" no longer drop the command.
- Picking the same model again in the list does not clear a spoken route (Codex sends the same name), so the bridge's answer says how to go back. A route that is in use no longer expires after 7 days.
- A command is turned into a "model was changed" note only if it took effect; a mid-turn note also reaches a subscription model when hook messages follow it.
- Combos: a Claude member selects the stricter token estimate; one unknown member no longer switches the overflow guard off for a known small member; a combo dropped by the peer after the request was sent is not resent.
- An old
9r/…name without a route gets a local 503 instead of going to chatgpt.com; upstream error bodies reach Codex whole, withRetry-After. - The last model list is also served when chatgpt.com cannot be reached at all, and is cached when the answer
was gzip-compressed. The old
display_mapsetting is no longer read (subscription models keep their names). - Log lines carry the date; the access-token tail is no longer logged.
Work handoff instruction (prepared, off by default)
is_hatti.py: when aiturk_hat is enabled and has a komut, the bridge prepends a developer message ("small work
yourself; larger work goes to AITURK via this command") to requests whose originator header is Codex Desktop.
The Codex CLI (codex-tui, codex_exec) never receives it and nothing is written to any file it reads. Each new
originator value is logged once (istemci goruldu).
Version 7 (IDE beta.26): external models behave like subscription models
Measured on 10 Oct 2026 with the unmodified Codex binary (codex exec, same conversation, model switched per turn).
| Area | Before | Now |
|---|---|---|
| Context window | every added model declared 272 000 | pencere.py: NanoGPT's own list for nano/*, a local table for cc/cx/ag, a combo declares its smallest member, unknown = 128 000, nothing above 272 000 (the summarizer's window). Codex then compacts by itself when switching to a smaller model. |
| Overflow | the target answered with an empty stream; Codex retried five times | pencere.sigdir trims the middle of oversized old parts in the outgoing copy; if it still does not fit Codex gets context_length_exceeded. A limit reported by the target is learned (pencere-ogrenilen.json). |
| Tools | clones inherited code_mode_only + Responses Lite; GLM and Qwen failed 0/3 on simple tasks (empty answers, reasoning leaked into the answer) |
dis_arac_modu: "direct" (default): classic tools. GLM 3/3, Qwen 2/3; Claude, cx and Gemini unchanged (6/6). "kalip" restores the old behaviour. |
| Intermediate messages | external assistant messages had no phase; Codex treats those as final answers |
SseRewriter stamps commentary / final_answer. Deltas are never delayed; only the item's done event waits for what follows. |
| Interrupted answer | text written before a stop or an app exit was lost | yarim.py keeps it (memory only) when the client closes the stream and adds it as a note to the next user message of the same conversation, once. |
| Images | removed from every old message for all external models | kept for vision-capable targets (user messages, newest first, 1 MB total). |
| Summary brake | the 4th summary in 10 minutes failed the turn | three summaries in a row with no normal request in between, or eight in 10 minutes. |
| Gateway errors | event: error (e.g. upstream 402) looked like a cut stream |
permanent ones become response.failed with a readable message; short upstream bodies are logged. |
| Start-up | the model list was fetched before listening; a slow gateway made the IDE give up after 10 s | window data comes from the cache at start and is refreshed in the background; the IDE waits 45 s. |
Not changed: subscription models, their order and names, and everything on the voice path (/realtime/* passes through untouched).
Settings in bridge.json (all optional): dis_arac_modu, tasma_bekcisi, yarim_cevap, ozet_kopru.dongu_siniri, ozet_kopru.dongu_art_arda.
A lone . sent as the user's message (used to apply a model change during a voice session) is answered by the bridge itself with " seçildi." (the target is not probed, so it does not say "ready") and never reaches a model (nokta_karsila, default on). Targets that reject tool calls are remembered (aracsiz-modeller.json) and marked "araçsız" in the picker.