We keep the conversation.
The three of them take turns.
We drive Claude Code, Codex and Antigravity through one session — from Python, Rust, or a terminal — on the subscriptions you already have. Say the work is worth more and the same chat finishes on another company's model, knowing everything said before it.
Artificial Analysis Intelligence Index v4.322 scored on it · 8 where nothing is both better and cheaper
Each of the three keeps its own memory.
Pick a good one and you have picked its models, its rate limits and its bad afternoons with it. Leaving later costs you the integration and the conversation you were in the middle of. The second one is the expensive part, and it is the one nobody quotes you for.
We keep the conversation in a file of our own. The three become engines, and you can change an engine while it runs.

claude -p --output-format stream-json --input-format stream-jsonModel and effort go down the same stdin control channel the turns do, so the process stays up while the dial moves.
codex app-server --stdioCODEX_HOME is pointed at a jail. Inside it: a symlink to your real auth.json, and one near-empty config. That is the whole of what it loads.
agy --output-format stream-json --input-format stream-json --print ""agy resumes its own conversation by id, so consecutive turns cost nothing extra. Only what it missed while another provider answered rides in the front of the next message.
chat.send("what changed in this repo today?")Three grammars, drawn here at three gauges. One takes flags and JSON lines on stdin. One takes JSON-RPC over a socket. One takes its history inside the front of the next message. Once one is active, we keep its process and native conversation open across consecutive turns. A switch may cost an activation; the arriving provider gets the same story in the dialect it accepts.

One session, and the logins already on your machine.
We offer the providers whose command line is installed and signed in. That is the list you get.
Python, Rust and terminal clients attach to the same daemon. send from any client, intelligence to change what answers, detach to leave it warm, and stop to end the shared session.
from omni import Inference, Event
chat = Inference.load_or_create_session("triage")
chat.intelligence(7)
@chat.on_event
def handle(event):
if event.type == Event.TEXT:
print(event.text)
chat.start()
chat.send("what changed in this repo today?")
Every line gets a number, and the number only goes up.
The session is a log we append to. Every line carries a number, and the number runs on through every provider the conversation has passed through. A second, smaller file holds how far up that log each one has read.
Two files, and the rest follows. A provider arriving hears what it missed. A provider coming back resumes its own session and hears the rest.
- 00configlaunch · level 10
- 01startWhich test in this repo is slowest?
- 02thinkingthe model is reasoning. we record that it did.
- 03tool.call
[Bash: pytest --durations=3] - 04tool.result
[Bash result: 3 slowest durations] - 05texttest_conversation_survives_a_switch — 41.2s.
- 06endturn complete
- 07configintelligence(4)
- 08switchthe conversation moves to claude-code-clihanded 0–7 · 8 events, each one once
- 09startWhy is that one slow?
- 10thinkingthe model is reasoning. we record that it did.
- 11textIt starts two CLIs and waits out both cold starts before the first token.
- 12endturn complete
- 13configintelligence(10)
- 14switchback to codex-app-serverhanded 7–13 · resumes its own session, already holds 0–6
- 15startFix it. Start them in parallel.
- 16tool.call
[Edit: tests/conftest.py] - 17textBoth runners start together now. 41.2s → 18.9s.
- 18endturn complete
Watch the second column fill. On the way out, the arriving provider is handed eight lines. Coming back, the first one is handed seven of the fourteen the file now holds, because the first six are already inside its own session and it is resuming that.
Benedict gave the kitchen a week at a time, and gave the changeover to both men at once: the server going off washes up and returns the utensils to the cellarer, who hands them to the server coming on, in order that he may know what he gives out and what he receives back. The second file is the cellarer's list.


We tell the next one what it missed.
A real run, transcribed. One fact each to three companies' models, then the question put back to the first. Each of them heard its own line once.
Remember: fact one is RUBY-1.
OK
Remember: fact two is JADE-2.
OK
Remember: fact three is ONYX-3.
OK
List all three facts.
RUBY-1,JADE-2,ONYX-3
one of those three was told to this model. the other two were told to somebody else's.

A tool that belongs to somebody else arrives as text that reads as what happened — [GoogleSearch: "kite festivals"]. The structured original stays in the log, so going back to Gemini resumes Gemini's own session, which holds it.
When a bob is called, the bells in fourth and fifth place ring on as if no calling had been made. Coming back, so does the provider.

Say what the work is. We say what runs it.
One word for the kind of work, and the answer comes back — a shortlist somebody chose, best first, and the first vendor on it you are signed into wins.
If you would rather say a number, say a number. If you already know the model, name it and we will pass it through untouched.
codex app-server --stdio · turn/start { model: "gpt-6-astra", effort: "high" }in order: codex-app-server › claude-code-cli › antigravity-cli
We tried to work this out arithmetically first — score, cost and time, normalised and weighted, one number out the other end. It kept producing answers that were defensible and wrong: three models within one percent of each other and a hundred Elo apart, and a winner that moved when a constant did. Ranking is not judgement, so the judgement is written down instead.
Ask for code and today you get gpt-6-astra high on codex-app-server. Sign out of codex-app-server and it walks one place down the list, which is the whole cost of losing a vendor, said plainly.
Two of the three run hot on request. Codex takes service_tier; Claude Code has a fast mode on its largest models. Antigravity has no such thing, so asking for it there is ignored rather than refused.
The number still works, and it is still the left edge of a board: a model earns a rung when nothing else is both better and cheaper. There are 10 of them on GDPval-AA v2 today. See the whole board and turn a provider off.
Other people's measurements decide the number. If a board mismeasures a model, or stops publishing, or scores it on work unlike yours, we are wrong in exactly that way and we carry on. They are named, with their methods and their error bars linked.
A bee hunter gets his straight line out of the eighth or tenth flight. The first few point somewhere else.
We wait for the turn to end.
Intelligence, providers, prompts and session swaps are written down the moment you ask for them, and applied at the next turn boundary, once the running tool has finished.
A message is the exception, and on purpose. Sent while a turn is running, it goes into that turn.
If, at any time, the officer in charge of the navigational watch is to be relieved when a manoeuvre or other action to avoid any hazard is taking place, the relief of that officer shall be deferred until such action has been completed.
STCW Code, Section A-VIII/2, Part 4-1, paragraph 23 · the Manila amendments, 2010 · International Maritime Organization
A ship under pilotage separates two things that software usually confuses. The pilot has the conduct of the vessel; the master stays in command. The provider has conduct of the turn — it decides what to say and which tool to reach for. We have command: the log, the counter, and the moment at which anything is allowed to change.
chat.send("also check the fixtures") injected. It lands where you sent it, inside the turn already running. send returns after the daemon has durably accepted it, without waiting for model output, and is safe from any thread, including from inside an event handler.
chat.intelligence(9) held. Written down the moment you ask, applied at the seam once the running tool has finished. Providers, prompts and session swaps wait in the same queue.

Five things we refuse.
Five, with the mechanism for each.
Carry reasoning across a switch.
A vendor signs its reasoning, so it replays only where it was made. We record that the model thought, and drop the thought — out of your logs as well.
Define tools.
We watch what the command line already exposes.
Silence a project's own rules.
agy reads a GEMINI.md or an AGENTS.md out of the directory it runs in and offers no way to stop it. We say so on the turn rather than call the chat quiet.
Guess a benchmark number.
A model a board has measured is a candidate on that board. One it has yet to measure stays on the list and out of the answer.
Ship a model list.
A list inside a release goes stale. With the registry unreachable and the cache empty, we raise.

The list is one text file.
It matters whose file it is. The shipowners kept one register of ships and the underwriters kept another: the Red Book and the Green Book, 1800 to 1833, grading the same hulls, each believed only by the party that had printed it. Both came to the verge of bankruptcy. Merged 1834, into one body neither of them owned. That is the arrangement that survived. A routing table maintained by any one of these three vendors would be the Red Book.
We ask this site for a finished map from level to model, and hand two strings to a command line. Every model name on your machine arrived over the wire this morning.
The map comes out of one JSON file in a public repository. Editing it and committing is the publish. Git does what a table would have done: history, blame, review, rollback.
Which also means somebody's commit is your routing table. That is the trade. What you get for it is a diff you can read, a name against every change, and OMNI_REGISTRY if you would rather point at your own.
models.jsonone file in a public repo. edit it, commit itraw.githubusercontentread at request time, held for a minute/choose.jsona word, a number, or a model you name~/.omni/cachekept an hour. a stale answer still runs offlineclaude -p --model … --effort maxtwo strings, handed over as they are

