Repository navigation
Desktop wait/status orchestration re-meters full cached context every 10-30s; stale 'running' subagents keep it looping (90% of weekly Pro in 15.5h) #37299
Description
Summary
Long-running Codex Desktop tasks that orchestrate subagents and background commands consume extreme weekly usage while effectively idle. The driver is that every small wait/status result triggers a model turn that re-meters the task's entire accumulated context (~140k tokens at 97–98% cached in our case), and Desktop's orchestration issues these waits on 10–30s timeouts against work that takes minutes to hours. A newly activated Pro account went from 0% to 90% of its weekly limit in ~15.5 hours this way; the account usage API reported ~290M tokens for that single day (Aug 5).
Related: #4764 (large cached contexts consuming limits). This report adds the Desktop wait/status orchestration angle plus a subagent lifecycle defect that keeps the pattern running.
Environment
- Codex Desktop (app-server
0.147.0-alpha.1.2), macOS 15 (Darwin 24.6.0) - Model
gpt-5.6-sol, reasoningxhigh/max, multi-agent + code-mode enabled
Measurements (local session rollouts, fork/replay events deduplicated)
Over Aug 4–6:
- 75% of all model-visible tool calls were
wait/wait_agent/list_agents(8,744 of 11,002). Observed timeout arguments:waitmostlyyield_time_ms10,000–30,000;wait_agentmostlytimeout_ms30,000–60,000. - 83% of
wait_agentcalls returnedtimed_out: true(1,765 of 2,122) — no-op turns whose only effect is re-metering the full cached context. - Average input per turn ~137–141k tokens, 97.4–98.1% cached, sustained across 15–25k turns/day (2.4–3.6B input tokens/day processed on one workstation).
- One long-lived thread issued 3,834 wait-family calls over its lifetime.
- Tool outputs returned through
waittotaled 28.2MB over three days (60% of all tool-result bytes), much of it unprojected upstream API JSON that then persists in context and is re-metered on every subsequent turn.
Subagent lifecycle defect (keeps the loop alive and inflates rosters)
- Completed subagents remain
running/"Working" in the roster and UI for days. In our local Codex state, 1,154 child-task relationships are marked open; 1,071 have no update for >24h; 815 for >72h; in a 200-item sample, 184 already contained a final answer. - Direct example: a subagent whose rollout file went silent at 06:42 still reported
agent_status: "running"inlist_agentsat 07:48. - Thread forks/continuations rewrite the full rollout history in bulk (e.g., an 86MB rollout written in under a minute, 8,879 of 8,908 token events being replayed history) and re-import the parent roster including stale "running" agents — so ghost agents survive across generations, and parents keep polling agents that will never change state.
list_agentsembeds each completed agent's entire final message in the roster payload, so every roster check re-injects those reports into context.
Impact
- Fresh Pro weekly limit 0% → 90% in ~15.5 hours, dominated by two long-lived tasks; ~290M account-reported tokens in one day.
- The workload pattern (agentic delivery with verifier subagents and CI waits) is exactly what Desktop multi-agent encourages, so this scales with adoption of the feature.
Suggested directions
- Server-side or client-side long-wait primitive: a bounded wait on subagents/commands should not cost one full-context model turn per 10–30s slice, and a
timed_outno-change result ideally should not trigger a model turn at all. - Fix child-task lifecycle: transition completed subagents out of
runningwithout requiring the parent to reap them; do not re-import stale rosters into forks/continuations. - Return roster/status summaries (name, state, timestamps) instead of embedding full final messages in
list_agents. - Clarify how cached context in orchestration turns is weighted against plan limits (follow-up to cache miss can cause higher usage towards limit than expected #4764) — the current behavior makes long-lived tasks pay near-max context for idle polling.
Happy to provide session IDs, rollout excerpts, timestamps, and the affected account privately through support (ticket references this issue).
cc @tibo-openai
Activity
John-Lin98 commentedon Aug 13, 2026
Follow-up measurement from one Windows Codex Desktop installation (anonymized; no session text, IDs, paths, account data, or rollout excerpts).
Method:
token_countcounters.functions.wait >= 180000 ms, with 300000 ms preferred when no intermediate output is needed.Observed local counts:
functions.waitcalls; 158 were <180 s and 82 were <30 s. Local counters totaled 78.2M tokens (74.8M cached input).functions.waitcalls; 80 were <180 s and 40 were <30 s. All 80 short waits came from one session; the other 9/10 sessions had zero shortfunctions.waitcalls. There were also 12wait_agentand 6list_agentscalls. Local counters totaled 863.5M tokens (834.7M cached input).Interpretation limits:
functions.waitpattern in most new sessions, but one post-policy session still looped at 1–30 s. That makes an enforced client/server no-change long-wait primitive important: guidance alone is not a reliable guardrail.This independently supports the request that no-change timeout/status results should avoid waking a full model turn, and that
wait_agent/list_agentsneed the same treatment as code-cell waits.GorangN commentedon Aug 21, 2026
I can reproduce a very similar pattern on Codex app-server 0.148.0-alpha.9 with GPT-5.6 Sol xhigh and multi-agent.
In my case, a single multi-agent session exhausted essentially 100% of my weekly Codex allowance in about two hours.
Observed timeline:
• ~0% weekly usage at 19:37 CEST
• ~50% at 20:56
• ~80% at 21:19
• ~95% at 21:34
• 100% at approximately 21:40
Parent thread:
01a0203f-10c5-7431-998e-c2643c4220ab
The parent spawned six subagents across two waves. All inherited GPT-5.6 Sol + xhigh.
After deduplicating forked/replayed token_count history, my local rollout analysis indicates approximately:
• 174M total processed tokens
• 169M cached input tokens
• ~97–98% cached input on many turns
• ~135k average input tokens per parent model turn
• 8 of 9 observed wait_agent calls timed out
I can also see cases where a wait_agent timeout is followed by another model turn processing 100k–190k+ input tokens, almost entirely cached.
This looks extremely similar to the re-metering behavior described in this issue.
I have retained the parent and all six subagent rollout JSONLs and can provide sanitized excerpts if useful.
lunara69-ctrl commentedon Aug 24, 2026
Independent data point from a Linux Codex CLI audit:
I observed the same qualitative pattern, although at a much smaller scale than reported here.
Across one audited workflow:
The larger session accumulated 55.9M total MAIN input tokens, with repeated context processing becoming a major component of usage.
One additional observation: cached input changes the economics, but does not remove the architectural amplification. A semantically empty status check can still cause a very large existing context to be processed again.
So I would strongly support exposing telemetry for:
Without separating those three, normal agent work and orchestration overhead are difficult to distinguish from account-level usage numbers.
jmtt89 commentedon Aug 27, 2026
Same pattern on plain CLI, and it removes the two variables your report is most likely to be dismissed on: no Desktop, no subagents, single session.
Codex CLI 0.149.0,
codex execinvoked non-interactively by an orchestrator. One agent, nowait_agent, no roster, no fork/replay. The polling iswrite_stdinagainst aunified_execsession. Your per-turn profile reproduces almost exactly:Session totals (175 turns): input 23,957,294 · cached 23,542,272 · new input 415,022 · output 52,694 · input:output 454:1.
Splitting by turn kind isolates the cost:
The polls re-metered more context than all the real work combined, for 12% of the output. Cache hit rate on the poll turns alone: 98.7% (13,327,360 of 13,507,179) — so this is your "re-meters full cached context" with the cache working perfectly, and it still cost 13.5M.
The mechanism, which I think your report is missing and which explains why this is structural rather than a Desktop bug. Checked against
102ae5e2e6ff:core/src/unified_exec/mod.rs:72—pub(crate) const MAX_YIELD_TIME_MS: u64 = 30_000;core/src/unified_exec/mod.rs:213—yield_time_ms.clamp(MIN_YIELD_TIME_MS, MAX_YIELD_TIME_MS)core/src/unified_exec/process_manager.rs:575—clamp_yield_timeis applied where the comment reads "For the initial exec_command call"So no single tool call can observe a process for more than 30s, including the first one, and it is a compile-time constant with no config override (
background_terminal_max_timeoutinconfig_toml.rs:308bounds how long the process may live, not how long a call may block). Any wait longer than 30s is therefore structurally N re-metered turns, where N = duration/30s. #38495 has the protocol-level framing of the same thing: resumption is pull, not push.That matters for triage: your stale-subagent defect is a real amplifier — it manufactures waits that never end — but it is not the cause. Remove Desktop, remove subagents, remove stale rosters, and a single
codex execwaiting on an ordinary 21.6-minute CI pipeline still burns 13.5M of metered context to learn nothing.Our wait was legitimate, incidentally: the agent had finished, committed, pushed and opened a PR, and was waiting on its own pipeline whose jobs run sequentially with multi-minute gaps. Nothing was hung and it had no signal telling it to stop.
Cross-linking, since these threads don't reference each other: #38495 is the mechanism-level report (pull-not-push, no completion push, no timer) and proposes escalating server-side yield + completion push as fixes. It leaves the caching question open — its author suspected a cache miss inflated their numbers. Your data answers it: the account usage API returning ~290M for one day at 97–98% cache is the evidence that cached context is metered regardless. Combined, the two give a conclusion neither has alone: a cache fix would not help, and the yield/push fixes pay for themselves in quota even when caching is perfect.
andgedeveloper commentedon Sep 13, 2026
Also reproducible on Codex CLI 0.154.0 on Linux, with quantified wait/poll token impact
I can independently reproduce the same class of issue on ChatGPT Pro ($200 / Pro20x) using Codex CLI with a heavily multi-agent software-development workflow.
Environment
Codex CLI: 0.154.0
OS: Arch Linux x86_64
Install: npm
Auth: ChatGPT
Primary model during the measured day: GPT-6 Astra Medium
Multi-agent, sleep tool, unified exec/code-mode functionality enabled
I performed a local forensic analysis of one full working day: Sep 12, 2026, America/New_York.
Day totals from unique response usage records, including compaction:
8,020 model calls
912,459,392 cached input tokens
26,666,608 ordinary input tokens
2,808,482 output tokens
The main finding is repeated parent-model re-entry while waiting.
Polling / wait / status:
1,574 model calls
19.63% of all model calls
198.90M cached input tokens
21.80% of the day's total cached-input volume
Of those, 1,474 pure wait/sleep calls alone consumed 186.94M cached tokens.
This was not primarily caused by unusually huge individual contexts:
median cached prefix: 114,176 tokens
p95: 202,368
p99: 221,952
max: 242,432
calls above 300k cached tokens: 0
So the high token volume is mainly caused by a very large number of model re-entries against an already substantial 100k–200k cached prefix, rather than a few 500k+ context calls.
One especially clear example:
A long-running release process was already being waited on inside the shell using a while kill -0 style wait. Despite that process-side wait, the parent/model was re-entered 109 additional times just to continue waiting.
For that wait loop:
109 calls
median interval: ~49.7 seconds
p90 interval: ~53.6 seconds
8.49M cached input tokens consumed
The observed control flow was effectively:
long-running process/subagent -> tool yield/timeout -> parent model inference -> decide to keep waiting -> wait again -> timeout/yield -> parent inference -> repeat
Two large working threads demonstrate the same behavior:
Thread A
1,364 total model calls
332 polling/status calls
43.31M cached tokens from polling/status
Thread B
580 total model calls
217 polling/status calls
25.02M cached tokens from polling/status
This workflow does use many engineering/architecture reviewers and subagents, but the reviewers themselves do not appear to inherit huge parent contexts.
I checked 22 engineering-reviewer threads: their initial input was only about 20.3k–20.9k tokens, with separate thread IDs and no previous parent tool outputs. The expensive behavior is primarily the parent/model repeatedly waking while waiting for those independent agents/processes.
There also appears to be an interaction with progress/heartbeat behavior: the environment encourages the agent not to block waiting for long periods and to provide periodic progress updates. In practice, this can couple a user-visible heartbeat to another full model inference even though no new reasoning decision is required.
Expected behavior
I would expect long-running waits to be runtime/event driven:
while a child/process is still running, keep waiting below the model layer;
child completion/failure should immediately wake the parent;
user steering/cancellation should immediately wake the parent;
an actual decision-required event should wake the parent;
progress/heartbeat UI should ideally not require a full parent-model inference;
timeout-only wait events should not repeatedly re-meter the complete parent context when nothing changed.
In other words, ideally:
parent -> start/wait for child -> runtime waits -> child completes -> wake parent model
rather than:
parent -> wait -> timeout -> full parent inference -> wait -> timeout -> full parent inference -> ...
I also verified that this is not a ccusage cumulative-counter double-counting artifact. Duplicate unchanged token-count records were excluded, unique response IDs reproduce the usage totals, and including otherwise omitted compaction requests actually increases the measured total slightly.
For context, this workload consumed approximately 80% of my Pro20x weekly allowance during that working day. I am not claiming that polling caused all 80%; the specific measurable finding is that wait/poll/status re-entry alone represented ~21.8% of cached-input volume.
This looks closely related to #35108, #35259 and the wait_agent timeout / parent polling behavior discussed in #41875.
I can provide additional sanitized per-call metrics, thread timelines, compaction data, or wait-loop aggregates if they would be useful to the Codex team.