Automatic compaction drops unchanged additionalContext

Open 💬 1 comment Opened Aug 12, 2026 by davidfant

What issue are you seeing?

Unchanged client-supplied additionalContext disappears after automatic compaction.

Codex retains additionalContext in AdditionalContextStore and normally emits only entries that changed since the prior turn. That deduplication works until compaction replaces model history: the replacement history is built without re-rendering the store's retained values. A subsequent model request therefore lacks still-active application context even though the client did not remove or change it.

In our app-server-backed integration, this caused connected browser-profile context to vanish after compaction. The model then reported that no browser profile was active despite the client having supplied one earlier in the same session.

What steps can reproduce the bug?

The included integration test reproduces this through the real Responses API loop:

  1. Submit a user turn with application additionalContext, such as browser_access = "profile id: browser-profile-1".
  2. Return a shell tool call with usage high enough to trigger automatic mid-turn compaction.
  3. Return the compaction summary.
  4. Inspect the continuation request sent to the model.

Before the fix, the continuation request contains no <browser_access> developer context. The regression assertion fails with an empty list instead of:

<browser_access>profile id: browser-profile-1</browser_access>

The same underlying omission exists when a token-budget rollover starts a replacement context window.

What is the expected behavior?

Every replacement context window should include the current values retained by AdditionalContextStore. Ordinary turns should continue deduplicating unchanged values, but compaction must rebuild active context because it discards the history that previously carried those values.

Additional information

A focused fix and regression test are ready on a branch based directly on current openai/codex main:

The change renders the store's current values into standard local/remote compaction replacement history and token-budget context-window replacement history. It touches four files and preserves normal per-turn deduplication.

Focused validation on current upstream:

  • env -u CODEX_SANDBOX_NETWORK_DISABLED NEXTEST_RETRIES=0 just test -p codex-core suite::additional_context — 7 passed
  • env -u CODEX_SANDBOX_NETWORK_DISABLED NEXTEST_RETRIES=0 just test -p codex-core multiple_auto_compact_per_task_runs_after_token_limit_hit token_budget_context_uses_new_window_after_compaction — 2 passed
  • cargo fmt --all
  • git diff --check

The new E2E failed at the intended post-compaction assertion before the implementation and passed afterward. If a maintainer agrees with the approach and invites the contribution, the clean branch is ready to open as a PR.

View original on GitHub ↗

1 Comment

c3mb0 · 14 days ago

Verified this remains reproducible on current openai/codex main (9d012ca4) after the recent compaction-hook changes.

The failure boundary is replacement history: AdditionalContextStore::merge intentionally emits only changed values, but both build_compaction_initial_context and token-budget start_new_context_window replace the history without rendering the store's current values. The next model request therefore cannot recover unchanged application context from either the old history or the deduplicating store.

I adapted David Fant's prior implementation onto current main, preserved its provenance, and added current regression coverage for both user-visible paths:

  • automatic mid-turn compaction retains active application context in the continuation request;
  • token-budget rollover retains it both with and without client-developer-message retention enabled.

Local proof:

  • just fmt
  • NEXTEST_RETRIES=0 just test -p codex-core --test all suite::additional_context — 7 passed
  • NEXTEST_RETRIES=0 just test -p codex-core --test all token_budget_context_uses_new_window_after_compaction — 2 passed
  • git diff --check

Reviewable hunk: https://github.com/c3mb0/codex/pull/1
Prior implementation credited here: https://github.com/davidfant/codex/commit/a77285a05

I have not opened an upstream PR because external contributions are invitation-only. If this direction fits the owner lab's current architecture and priority, I would be glad to submit the focused branch by invitation.