silicon
omni.

one interface for claude code,
codex and antigravity

by unlikefraction
$ pip install silicon-omni

three clis.
one conversation.

Every vendor ships a good agentic CLI and a subscription that makes it cheap to run. None of them talk to each other. Pick one and you are married to its models, its rate limits and its outages.

omni owns the conversation instead. Claude Code, Codex and Antigravity become interchangeable engines underneath a single Python object, driven by the subscriptions you already pay for rather than API keys. Zero runtime dependencies. You bring the CLIs.

# the whole surface from omni import Inference, Event chat = Inference.load_or_create_session("nightly-triage") chat.intelligence(7) @chat.on_event def handle(event): if event.type == Event.TOOL.CALL: print(event.tool, event.args) chat.start() chat.send("what changed in this repo today?")
claude code

Flags do the work

Subagents, MCP and memory files switch off from the command line. Seeding is a file write. Model changes go over the stdin control channel, so re-tuning never restarts anything.

codex

App server, not exec

Runs against a CODEX_HOME of its own holding two things: a link to your real auth.json and a near-empty config. History arrives by thread/inject_items.

antigravity

The awkward one

No flag for MCP, none for subagents, and no way to seed. omni folds prior conversation into the front of the next message, so catching up costs one turn rather than two.

the conversation
moves.

Raise the dial mid-run and the next turn can land on a different vendor's model, with everything that came before already in its head. Coming back is cheaper still: the provider resumes its own session and is told only what it missed.

This is a real run, not a mock. Three facts, three vendors, one session id.

--- claude @ level 5 (claude-sonnet-5) --- Remember: fact one is RUBY-1. Reply with exactly: OK OK --- openai @ level 1 (gpt-5.4-mini) --- switch_provider claude → openai new_session 01a01d11-5ca8-7d02 Remember: fact two is JADE-2. Reply with exactly: OK OK --- google @ level 0 (gemini-3.5-flash-low) --- switch_provider openai → google Remember: fact three is ONYX-3. Reply with exactly: OK OK --- claude @ level 5 --- switch_provider google → claude List all three facts, comma separated, values only. RUBY-1, JADE-2, ONYX-3 3 switches · 3 native sessions · 0 errors

What actually crosses

~/.omni/sessions/{id}.jsonl is the source of truth, and it is the event log — the same objects your handlers see, appended in order. Alongside it, omni remembers each provider's own session and how far up the log it has already seen, so arriving somewhere replays only the part it missed.

Providers do not share tools, so a tool the destination does not have is rendered as text that reads as what happened. The structured original stays in the log, which is why going back to Gemini replays Gemini's own session and the brackets never happened. Preserved, not lossy.

[GoogleSearch: "kite festivals"] [GoogleSearch result: 12 results …]

Reasoning is the one thing that never travels. It is signed or encrypted per vendor and cannot be replayed anywhere else, so omni records that the model thought and throws the content away — including out of your logs.

one number.
not a model picker.

Every model the three CLIs can run is plotted by its GDPval-AA v2 Elo — blind pairwise judging of real economically valuable work, anchored so a human expert scores 1000 — against the dollars it measurably cost to earn that score.

Only the left edge of that graph becomes a dial. A model earns a level if nothing else is both better and cheaper. Level 10 is the top of the edge, and the dial walks down-left from there, so a step down is always a real saving and never a sideways move.

level
elo
$/task
model
10
1844.7
6.766
claude-opus-5 max
9
1813.8
4.963
claude-opus-5 xhigh
8
1732.8
3.0271
claude-opus-5 high
7
1678.9
2.1141
gpt-5.6-sol xhigh
6
1621.2
1.3708
gpt-5.6-sol high
5
1619.6
1.3641
claude-opus-5 medium
4
1578.3
0.1022
gpt-5.6-luna max
3
1525.7
0.0667
gpt-5.6-luna xhigh
2
1465.8
0.0412
gpt-5.6-luna high
1
1274.5
0.0133
gpt-5.6-luna medium
0
1155.6
0.0072
gpt-5.6-luna low

Level 4 is worth staring at. GPT-5.6 Luna at max effort scores within 15% of the top of the board for 66 times less money, which is why everything between it and Opus 5 falls off the edge entirely.

There is one dial per set of providers, because losing a vendor puts models back on the dial that another vendor's were shadowing. omni asks this site for the dial matching the providers it has and caches it for an hour. The dial is editable — add a model, and every install picks it up on its next refresh.

one vocabulary.

Everything omni has to say arrives as one Event. The same objects go to your handlers, to your logs, and onto disk — so the session file is the event log, and there is never a second schema to learn.

type
carries
START
the message that opened this turn
TEXT
one finished assistant message
THINKING
the model is reasoning. never what it thought
TOOL.CALL
tool, args, id
TOOL.RESULT
tool, id, result, ok
END
the turn is over
INJECTED
a message that landed mid-turn
ERROR
auth · limit · unavailable · crash
SWITCH_PROVIDER
the conversation moved
NEW_SESSION
a provider opened one of its own

Handlers run on one thread, in the order things actually happened. A handler that raises is reported and stepped over — it cannot take the run down with it.

nothing changes
mid turn.

This is the rule the whole design hangs off. Intelligence, providers, prompts and session swaps are recorded the moment you ask for them and applied at the next turn boundary, once the running tool has finished. A model never changes underneath itself.

chat.intelligence(9) # noted now, applied at the boundary chat.active_inference_providers(["claude", "openai"]) chat.system_prompt(...) chat.disable_subagents() chat.disable_mcp()

A message sent while a turn is running is injected rather than queued behind it: the provider picks it up at the next safe point. Either way send returns immediately and is safe from any thread, so whether you drive omni from a while loop, asyncio or a message bus is entirely your business.

what it will not do.

A short list, because the things a tool refuses to do tell you more than the things it claims.