Codex Mode Runtime Trace — Mode Inside the Desktop App
Live evidence of the Codex mode (top-level, no submodes) inside the ChatGPT desktop — webrun/alpha-search tool, model switch, task-title agent, and the differences vs the work submode
Codex Mode Runtime Trace — Mode Inside the Desktop App
Part of openai-research. Live capture 2026-08-26 20:57 (BRT 15:57): the user opened the Codex mode (top-level, no submodes), asked a search question ("Se a minha mãe tivesse bolas, ela seria o meu pai.\n\nPesquise online a quem se atribui essa frase.\n" — attribution found: Max Verstappen), then switched the model mid-conversation (gpt-5.6-sol → gpt-5.6-luna, effort low) and asked "que legal, e qual foi a repercussão?" (multi-turn). Evidence: app-server-stdio.jsonl, logs_2.sqlite, state_5.sqlite.
Thread metadata (state_5.sqlite — 01a03fdd-1e38-…)
model: gpt-5.6-luna(final;Configuring sessionshowsgpt-5.6-solat 20:57:30 thengpt-5.6-lunaat 20:57:42 — the in-conversation switch)reasoning_effort: low(the UI "light"),sandbox_policy: {"type":"disabled"},approval_mode: never,memory_mode: enabledthread_source: user(NOTchatgpt_handoff— direct user-initiated thread),source: vscodetokens_used: 626,727— same order of magnitude as the work submode (706,805 on the Taylor thread)- Transport: same app-server;
responses_websocket"connected" frames; model refreshGET /backend-api/codex/models?client_version=0.150.0every ~3min
The tool: webrun → POST /backend-api/codex/alpha/search
handle_tool_call_with_source: dispatch_tool_call_with_code_mode_result {
otel.name=webrun tool_name=webrun call_id="exec-a1889571-…" aborted=false }
: endpoint_session.execute_with { http.method=POST api.path="alpha/search" }:
Request completed POST https://chatgpt.com/backend-api/codex/alpha/search status=200 OK
- The Codex mode web search is the
webruntool, executed via the code-mode host (dispatch_tool_call_with_code_mode_result), hittingalpha/searchon the app-server backend — this is the/codex/alpha/searchendpoint documented as "Search on demand" in codex-flow-protocol, now confirmed as the tool path of the Codex mode. - Compared to the work submode: work used
web_search_call(standalone web searchaction.searchin the Responses stream) — different execution seams.
The task-title agent
A turn-mate system prompt ran for the task created from that prompt: "You are a helpful assistant. You will be presented with a user prompt, and your job is to provide a short title for a task that will be created from that prompt.\nThe tasks typically have to do wi…" — a lightweight title generator that annotates the task/thread from the user text (the longer state_5.threads.title shows the original prompt, not the generated title in this run — the agent result feeds the creation flow).
Events per tool call
app-server event: account/rateLimits/updated and thread/tokenUsage/updated after each tool call — the UI quota/usage updates ride the same app-server event stream (also seen in the work submode).
No x-codex-turn-state on the desktop (correction)
x-codex-turn-state appears 0 times in the recent desktop logs (Codex mode, 20:57–21:07). The sticky turn-state routing came from the self-contained probe of 2026-08-24 (API access), not from the desktop app — the desktop uses the responses_websocket session instead. See codex-flow-protocol note.
Codex mode vs work submode (what differs)
| Surface | Codex mode | Work submode |
|---|---|---|
| Thread source | user | chatgpt_handoff (from chat handoff) / user when direct |
| Web search tool | webrun → alpha/search | web_search_call (standalone) + web__run (CTR) |
| Scheduling tool | — | automation_update (MCP codex_app) |
| Initiation | top-level tab | chat handoff (card) or Work tab |
| Effort scale | same codex scale (low vs high) | same (thread reasoning_effort) |
| Quota | codex quota | codex quota (agent turns) |
Attestation provider (x-oai-attestation) — same mechanism as work
The app-server attaches x-oai-attestation (header constant X_OAI_ATTESTATION_HEADER, core client generate_attestation_header_for → AttestationProvider::header_for_request) when model_provider.supports_attestation(). The desktop provider is the DeviceCheck addon: each Configuring session is preceded by AttestationGenerate { request_id: 2 } (sol) and { request_id: 3 } (luna) — the token is generated per session/thread, exactly as in the work submode. So flow A (attestation) applies to the Codex mode too (and to work); the sentinel/PoW fallback is the chat-submode/main mechanism.
Standalone web search — same service, two seams (RESOLVED)
From codex-rs tests (core/tests/suite/code_mode.rs:489, core/src/tools/spec_plan_tests.rs):
- The Responses-native tool
web_search(server-side,ToolSpec::WebSearchwithexternal_web_access/indexed_web_access) is what the model sees when noweb.runexecutor is present (work submode:web_search_call). - With
Feature::StandaloneWebSearch+ awebnamespace tool executor (extension/MCP/CTR), the model seesweb.run(notweb_search) — and the tool runs as acustom_tool_call "exec"with the JS runtime:const result = await tools.web__run({ search_query: [{ q: "…" }] }); text(result); - That runtime (code mode host) performs
POST /v1/alpha/searchon the app-server → desktop:POST /backend-api/codex/alpha/search, with body:{"model": "<slug>", "commands": {"search_query": [{"q": "…"}]}, "settings": {"allowed_callers": ["direct"], "external_web_access": true}} - So the Codex mode
webrunand the work submodeweb_search_callare two seams of the same standalone web search service (the web search backend; the work submode runs it server-side inside the Responses stream — no clientalpha/search— while the Codex mode runs it client-side viawebrun→alpha/search).WebSearchMode: Live / Indexed / Cached / Disabled (per-turn config).
Task-title agent + onboarding strings
The turn-mate "You are a helpful assistant … provide a short title for a task" system prompt annotates the task being created (this ran in the handoff/creation turn). It is the "task title" service — evidence of a nested agent in the creation path, feeding the wham/tasks creation.
No "phantom tokens"
Searched the app (asar, app-server strings) and codex-rs: no phantom/token-swap endpoint exists. The only token exchange is the link-session (auth_token → __oailb 1h, see auth-and-link-session); no derived sidecar tokens beyond the OAuth family (id/access/refresh) + __oailb.
TypeScript types (code mode surface)
// ── the webrun tool call (tracing) ───────────────────────────────────────
export interface WebrunToolCallTrace {
otel_name: 'webrun'
tool_name: 'webrun'
call_id: string // 'exec-<uuid>' — the code-mode executor call id
aborted: boolean
}
// …dispatched through: dispatch_tool_call_with_code_mode_result (code mode host) →
// endpoint_session.execute_with { http.method: 'POST', api.path: 'alpha/search' } →
// POST https://chatgpt.com/backend-api/codex/alpha/search ← the ONLY client-side search seam
export interface AlphaSearchRequest {
model: string
commands: { search_query: Array<{ q: string }> }
settings: { allowed_callers: ['direct']; external_web_access: boolean | 'indexed' }
}
export type WebSearchMode = 'live' | 'indexed' | 'cached' | 'disabled'
export interface WebSearchModeTarget { // per-turn config
web_search_mode: WebSearchMode | null // null = server default
}
// ── the CTR (web namespace) shape ────────────────────────────────────────
export interface WebRunJs { // custom_tool_call 'exec' input (code mode)
// const result = await tools.web__run({ search_query: [{ q: '<query>' }] });
// text(result);
search_query: Array<{ q: string }>
}
// ── the task-title agent ─────────────────────────────────────────────────
export const TASK_TITLE_PROMPT: string =
"You are a helpful assistant. You will be presented with a user prompt, and your job is to provide a short title for a task that will be created from that prompt."
// (the run: the creation path of wham/tasks; the threads.title kept the original prompt in the observed run)
// ── code mode thread metadata (state_5) ──────────────────────────────────
export interface CodeModeThreadMeta {
id: string
model: 'gpt-5.6-sol' | 'gpt-5.6-luna' | string
reasoning_effort: 'low' | 'medium' | 'high' | string
sandbox_policy: { type: 'disabled' }
approval_mode: 'never'
memory_mode: 'enabled'
thread_source: 'user' | 'chatgpt_handoff' | 'realtime_voice' | string
source: 'vscode'
tokens_used: number // 626,727 observed
}
Cross-references
- codex-flow-protocol — backend endpoints, quota snapshot
- work-runtime-trace — the work submode loop (the analogous trace)
- chatgpt-desktop-architecture — the mode hierarchy
- bezetacil — how the capture happens