Multi-agent V2 full-history forks duplicate historical compaction snapshots and inline images, causing >100 GiB session storage growth

Open 💬 1 comment Opened Jul 20, 2026 by gonzalolarralde

What issue are you seeing?

A long-running Codex Desktop conversation using Ultra reasoning and multi-agent V2 produced approximately 110 GiB of local session data under $CODEX_HOME/sessions.

The growth appears multiplicative rather than being caused by subagents producing unusually large outputs:

  1. Context compaction persists Compacted rollout records containing a complete replacement_history.
  2. These replacement histories can contain inline base64-encoded screenshots.
  3. Older compaction records remain in the append-only parent rollout.
  4. A full-history subagent fork loads the parent’s persisted rollout items.
  5. Those inherited items—including all historical compaction snapshots—are serialized into the child rollout.
  6. Ultra reasoning proactively creates many subagents, multiplying the retained history across many files.

Observed impact from one session tree:

| Measurement | Result |
|---|---:|
| Total rollout files | 300 |
| Total reported size | approximately 110.09 GiB |
| Subagent rollout files | 294 |
| Size attributed to subagent rollouts | approximately 106.56 GiB |
| Files sharing the same root session ID | 295 |
| Files at least 1 GB | 51 |
| Files at least 500 MB | 86 |
| Files at least 100 MB | 155 |

Of those files, 159 were recorded by 0.145.0-alpha.18 with multi_agent_version: "v2" and accounted for approximately 88.18 GiB.

Many individual subagent rollouts were between 1.3 GB and 1.9 GB.

A representative completed child rollout was 1,706,557,797 bytes and contained 23,564 JSONL records. Its composition was:

  • 141 compacted records
  • Approximately 1.55 GiB in compacted records
  • 97.6% of the entire file attributable to compacted records
  • 125 individual JSONL records larger than 10 MB
  • Largest JSONL record approximately 12.93 MiB

One inspected replacement_history contained:

  • 118 history items
  • 19 input_image entries
  • 13,316,621 characters of inline data:image/png;base64,... data
  • Approximately 197,716 characters of input text

The child files are not small records that merely reference their parent. A representative child begins with its own session_meta, followed by a copy of the parent’s original session_meta and inherited parent events. Sampled parent payloads were identical, while outer timestamps were rewritten to the child creation time and some response items received new IDs.

The files have separate inodes and link counts of 1, and their allocated sizes are close to their logical sizes. This appears to be independently serialized history rather than sparse files or parent references.

This can consume enough storage to exhaust a local disk during a single long-running Ultra session.

What steps can reproduce the bug?

The severity depends on having a sufficiently large parent history, multiple compactions, inline images, and multiple full-history forks.

  1. Start a persisted local Codex thread using Codex Desktop or another local surface backed by the shared Codex runtime.
  2. Use the same thread for an extended period.
  3. Add several screenshots or other image inputs to the conversation.
  4. Continue the conversation long enough to trigger multiple context compactions.
  5. Select Ultra reasoning with multi-agent V2 enabled.
  6. Give Codex work that causes it to proactively spawn multiple subagents.
  7. Allow the generated spawn_agent calls to omit fork_turns. In multi-agent V2, the omitted value defaults to "all".
  8. Inspect the newly created child rollouts under $CODEX_HOME/sessions.
  9. Compare their sizes and initial JSONL records with the parent rollout.
  10. Repeat with additional subagents and observe that each full-history child can approach the persisted historical size of the parent before doing significant child-specific work.

Example read-only inspection commands:

du -sh "$CODEX_HOME/sessions"

find "$CODEX_HOME/sessions" -type f -name '*.jsonl' -print0 \
| xargs -0 stat -f '%z %N' \
| sort -nr \
| head -50

For a suspected child file:

head -n 1 "$CHILD_ROLLOUT" \
| jq '.payload | {
id,
session_id,
thread_source,
forked_from_id,
parent_thread_id,
multi_agent_version,
source
}'

The child metadata should identify it as a subagent and point to the parent through parent_thread_id and/or forked_from_id.

Inspecting the first several records should show the child’s new session_meta followed by inherited parent rollout records.

The problem becomes increasingly visible as these factors increase:

  • Number of retained compaction records
  • Number and size of inline images
  • Length of the parent thread
  • Number of spawned subagents
  • Use of full-history/default fork_turns="all" behavior

What is the expected behavior?

Spawning subagents should not cause local persisted session storage to grow in proportion to the parent’s complete append-only rollout history for every child, particularly when the history contains multiple historical compaction snapshots representing earlier versions of substantially the same model context.

A newly spawned child should not routinely approach 1–2 GB before performing meaningful child-specific work.

Disk growth should remain bounded enough that proactive multi-agent behavior cannot unexpectedly create more than 100 GiB of local session data from one conversation tree.

This expectation applies regardless of whether the triggering surface is Codex Desktop, the CLI, or codex exec, since the relevant multi-agent and rollout-persistence behavior appears to be shared.

Additional information

Environment and metadata from the affected session:

  • Platform: macOS
  • Surface: Codex Desktop
  • Reasoning mode: Ultra
  • Root metadata:
  • originator: "Codex Desktop"
  • source: "vscode"
  • history_mode: "legacy"
  • Child metadata:
  • thread_source: "subagent"
  • multi_agent_version: "v2"
  • Codex versions present:
  • 0.144.2
  • 0.144.5
  • 0.145.0-alpha.18

Relevant source locations:

  • codex-rs/core/src/session/multi_agents.rs
  • effective_multi_agent_mode maps Ultra reasoning to proactive multi-agent behavior.
  • The behavior applies to SessionSource::Cli, SessionSource::VSCode, SessionSource::Exec, and other sources.
  • codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs
  • SpawnAgentArgs::fork_mode defaults omitted or empty fork_turns to "all".
  • "all" becomes SpawnAgentForkMode::FullHistory.
  • codex-rs/core/src/agent/control/spawn.rs
  • AgentControl::spawn_forked_thread flushes the parent and reads the stored thread with include_history: true.
  • It uses parent_history.items as the forked rollout items.
  • The fork filter retains RolloutItem::Compacted.
  • The inspected Compacted records contain almost all of the affected bytes.
  • codex-rs/core/src/session/mod.rs
  • The InitialHistory::Forked branch in record_initial_history persists the inherited rollout items.
  • The code comment says: If persisting, persist all rollout items as-is (the store filters).
  • codex-rs/core/src/compact.rs
  • Compaction creates a CompactedItem with replacement_history: Some(new_history.clone()).
  • codex-rs/core/src/session/mod.rs
  • replace_compacted_history also persists a CompactedItem containing a cloned replacement history.
  • codex-rs/rollout/src/recorder.rs
  • JsonlWriter::write_rollout_item assigns a new timestamp, serializes the item with serde_json::to_string, and writes it to the child JSONL.
  • codex-rs/app-server/README.md
  • thread/fork is documented as creating a new thread by copying stored history.

Questions that may help confirm whether the observed persistence is intentional:

  • Is a subagent fork expected to persist every historical Compacted record from the source rollout?
  • Is each earlier replacement_history expected to remain independently serialized after later compactions supersede it?
  • Is inline base64 image data expected to be repeated across every compaction snapshot and every full-history child fork?
  • Is there an intended upper bound for inherited persisted bytes when spawning a subagent?
  • Should proactive Ultra subagents have the same persistence behavior as an explicit user-created full thread fork?

I can provide additional sanitized structural evidence if needed, including record-size distributions, parent/child metadata relationships, payload hash comparisons, compaction counts, and encoded image lengths.

I cannot attach the complete affected rollouts because they are individually up to approximately 1.9 GB and contain private conversation and image data.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗