A look into how Codex spends your extra tokens.
I was recently made aware of how
[Claude Code spends 15-20% of the tokens on updating a three-word status line](https://gist.github.com/flamerix-ai/c3905705cc4205e5de3011fa2e74efce)
. Since I work almost entirely within [Codex](https://github.com/openai/codex), I
decided to take a peek at the code (Codex is open source and written in Rust). If you are
working with an open source harness, be that Codex, pi, OpenCode, or something else, I encourage you
to explore how it actually works.
Giving threads a name
Whenever an unnamed thread receives a completed, nonempty user text message, an automatic
thread naming procedure is invoked behind the scenes.
<sup>[1](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/app/thread_routing.rs#L2070),
[2](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/app/thread_title.rs#L180)</sup>
This happens for example when you start a fresh Codex session and enter the first instructions
into the text box. In this case the full prompt, including the title naming instructions,
labels and user's text, are capped at 960 bytes and sent to an LLM.
<sup>[3](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/app/thread_title.rs#L288),
[4](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/app/thread_title.rs#L275)</sup>
The returned response is a structured output, resulting in a thread title that is preferably
under five words, or 36 characters. The preferred model is `gpt-5.6-luna` with low
reasoning effort, when signed into ChatGPT and that model exists in the catalog; otherwise it
uses the current selected model.
<sup>[5](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/app/thread_title.rs#L32)</sup>
You can also explicitly rename the thread by evoking `/rename <TITLE>` and
this doesn't cost you anything. However, if you run `/rename` with no arguments,
Codex immediately makes an LLM call requesting a title suggestion. That request uses up to
eight recent substantive user/assistant messages, excluding assistant commentary.
<sup>[6](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/chatwidget/interaction.rs#L476)</sup>
Knowing what's happening
Next I took a look at the live activity indicator, to see if it's similar to how Claude Code
does it. Turns out it's not. When using a model that supports it, which is any reasoning model,
reasoning summaries are always requested along with the actual response.
<sup>[7](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/core/src/client.rs#L998)</sup>
So Codex piggy-backs the live activity indicator with standard requests. The TUI receives
`ResoningSummaryTextDelta`, accumulates its text, extracts the latest usable line,
and sets the status header; there is no separate status-generation request.
Codex TUI has a catch-up recap feature that can be invoked explicitly via `/recap`,
but is also automatically enabled by default via `tui.auto_recap = true`. Automatic
recaps use the current model, together with the most recent visible changes and recap
instructions, resulting in a structured `summary` and nullable
`next_action` fields. The full recap prompt has a byte
ceiling corresponding to 8 192 estimated tokens and the currently selected model is the one
used for recap.
<sup>[8](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/tui/src/app/recap.rs)</sup>
Remembering what matters
Codex memories are persistent notes carried between sessions - useful knowledge, previous
decisions, your stated or implied preferences. Memories can be enabled via
`/memories` option. There are two background stages, and both make additional LLM
requests.
During the extraction stage the LLM processes an eligible past thread and records useful
context, i.e. what you requested, findings, changes, corrections and unfinished work. The v2
prompt explicitly tells is to preserve uncertainty and avoid turning a single task instruction
into a general user preference.
<sup>[9](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/memories/write/templates/memories/stage_one_system_v2.md)</sup>
During the consolidation stage an internal agent combines those records into a compact index,
updates preferences and project pointers, and removes unsupported or superseded claims.
<sup>[10](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/memories/write/templates/memories/consolidation_v2.md)</sup>
Extraction generally makes on request per selected thread; consolidation is an agent and can
make several. By default `gpt-6-luna` is used for extraction and
`gpt-6.1-terra` for consolidation, with configurable overrides. You can see your
stored memories in `~/.codex/memories_v2/`.
Keeping watch
Finally we have guardian tasks. Guardian is Codex's automatic security and permission reviewer.
It evaluates whether an action could cause harm and whether the user authorised its target and
side effects. It implements a background classifier, which Codex can launch when an eligible
action starts, in order to determine whether the current workflow is high or low risk. It uses
`gpt-5.6-luna` and is just a prompt
<sup>[11](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/prompts/templates/guardian/classifier_instructions.md)</sup>
that returns a high/low classification in the response, and converts the result into a score of
1.0 or 0.0.
Additionally there is a seperate internal agent turn that can hold execution while it evaluates
the action about to be executed. For example, it makes a judgment whether the action could
destroy data or disclose sensitive information, or whether it follows the necessary policies
<sup>[12](https://github.com/openai/codex/blob/rust-v0.166.0/codex-rs/prompts/templates/guardian/policy_template.md)</sup>
The result is a structured output with an allow/deny outcome, optionally with risk level,
authorisation level and rationale. The reviewer can actually involve multiple LLM requests, as
it reviews, inspects files, and reasons across these.
I intentionally left out more explicit features, which include actual response handling (the thing that actually builds your code), as well as context compaction. Note that with compaction if you have background compaction enabled, compaction will start ahead of the foreground limit and generate candidates in parallel.
Understanding where the tokens go is important both for better understanding of the billing and why you are hitting rate limits, but also in terms of being in control, wherever possible, of your agentic harness.