[Codex Desktop] Nested functions.exec wait_agent polling causes repeated parent turns and token usage
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
- Issue type
- Bug
- Clarity
- Mostly clear
- Activity status
- Active
- Tech stack
- javascript, rust
- Domain
- ai-infra-agents, performance, tooling
Research direction
Start at the multi_agent_v1__wait_agent entry point and trace how it is routed through functions.exec and functions.wait. Reproduce the nested wait with timeout_ms: 60000 and inspect the parent/model resumptions and wrapper metadata. Done means the runtime keeps the parent suspended until completion, failure, or timeout without repeated model inference for internal polling.
Written by the indexing model from the issue text.
Description
Summary
When multi_agent_v1__wait_agent is invoked inside Codex Desktop's generic functions.exec code-mode wrapper, one logical wait can be split into multiple parent-visible tool boundaries:
- the outer
functions.execyields withScript running with cell ID ...; - the parent then invokes
functions.wait; - the wait returns
timed_out: true; - the parent model is sampled again after each boundary.
This appears to consume additional model turns and append low-value wrapper metadata while the child agent is still simply running. The issue is not that the model continuously generates tokens during the blocked interval; the problem is that each yield/timeout returns control to the parent model and may trigger another full-context inference.
This is related to, but distinct from, the 30-second default timeout reported in #18394: the reproduction below explicitly passes timeout_ms: 60000.
Environment
- Surface: Codex Desktop
- Platform: Windows 11 x64
- Observed package version: 26.721.3404.0
- Tool path: nested
multi_agent_v1__wait_agentthrough genericfunctions.exec - Multi-agent version: v1
Reproduction
- Spawn a subagent and keep its work running longer than the outer code-mode yield interval.
- In the parent, invoke the wait through a generic
functions.execwrapper:
const result = await tools.multi_agent_v1__wait_agent({
targets: [agentId],
timeout_ms: 60000,
});
text(result);
- In the observed run:
- the nested
wait_agentstarted withtimeout_ms: 60000; - after approximately 11 seconds, the outer call returned
Script running with cell ID 29; - the parent then called
functions.waitfor the cell; - approximately 50 seconds later,
functions.waitreturned{"timed_out":true}.
- the nested
Observed usage evidence
The local rollout recorded two consecutive usage events around this single logical wait:
- first event: input 163,952; cached input 162,560; output 126; total 164,078;
- second event: input 164,106; cached input 163,584; output 31; total 164,137.
These raw totals are mostly cached input and should not automatically be interpreted as billable tokens. They do show that the parent turn resumed twice and that the second resume reprocessed an almost equally large context.
The important symptom is the repeated parent/model boundary:
multi_agent_v1__wait_agent(timeout_ms=60000)
-> functions.exec: Script running with cell ID ...
-> functions.wait(...)
-> timed_out: true
-> another parent/model continuation
Expected behavior
- A parent waiting for a subagent should remain in a runtime-managed wait until the child completes, fails, or the requested timeout expires.
- An intermediate wait yield should not require a new full parent-model inference.
- If an internal cap exists, the runtime should re-arm the wait internally rather than return to the model for every interval.
- Status/heartbeat metadata should not be repeatedly appended to the model-visible conversation history.
timeout_msshould bound the complete multi-agent wait operation, including the outer tool wrapper.
Actual behavior
- One logical
wait_agentoperation is exposed as multiple code-mode/tool polling steps. - Each step returns control to the parent model.
- The parent receives verbose wrapper output such as
Script running with cell ID ...and then a separate timeout result. - With a large parent context, this can create substantial repeated input processing and token/quota consumption.
- The behavior remains present even when
wait_agentreceives an explicit 60-second timeout.
Related issues
- #18394 — default
wait_agenttimeout is hardcoded to approximately 30 seconds and causes polling loops. - #32640 — the built-in
waittool is capped at approximately 50 seconds and re-samples the full context on each expiry. - #29122 — long-running nested tools routed through
functions.execconsume tokens while the model repeatedly waits. - #13733 — background polling sends a full API turn with the complete history for each poll.
- #24951 —
wait_agent.timeout_msmay not bound the complete tool call.
Those issues cover adjacent generic wait, code-mode, or multi-agent behavior. I did not find a report specifically covering multi_agent_v1__wait_agent nested inside Codex Desktop's functions.exec wrapper and producing multiple parent usage events for one logical wait.
Suggested fix direction
- Keep multi-agent waits as direct runtime-managed operations instead of wrapping them in a model-polled code-mode cell.
- If a wrapper is unavoidable, keep the parent turn suspended until the child reaches a terminal state or the full timeout expires.
- Re-arm internal timers without a new model request.
- Avoid adding repeated cell IDs, wall times, and unchanged wait metadata to the model-visible history.
- Add telemetry that distinguishes one logical wait from internal runtime polling and reports the number of parent model resumptions.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.5k
- Avg merge
- 1m
- Merged PRs (30d)
- 1k
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from openai/codex
-
enhancement remote
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
-
bug CLI windows-os
Difficulty 2/5 1-3 hours Newbie friendliness 76/100
-
macOS sandbox blocks hw.optional.arm64 sysctl, causing Flutter to misdetect Apple Silicon as x64 Openbug CLI sandbox
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
bug CLI TUI
Difficulty 2/5 1-3 hours Newbie friendliness 90/100
-
CLI config enhancement skills
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 86/100
kwakseongjae/auto-hwp#319 ·
-
area:cli bug filter-quality good first issue priority:medium
Difficulty 2/5 1-3 hours Newbie friendliness 84/100
-
Difficulty 1/5 Under an hour Newbie friendliness 72/100
bevyengine/bevy#25861 ·
-
comp-datalake
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/ClickHouse#121222 ·
-
A-linter
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
oxc-project/oxc#26863 ·