openclaw - ✅(Solved) Fix [Bug]: Codex legacy mirrored-history fallback ignores contextTokenBudget and caps high-window sessions at ~24k rendered chars [1 pull requests, 2 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
openclaw/openclaw#84084Fetched 2026-05-20 03:44:14
View on GitHub
Comments
2
Participants
2
Timeline
4
Reactions
1
Author
Timeline (top)
commented ×2cross-referenced ×2

In OpenClaw v2026.5.18, the legacy mirrored-history fallback in extensions/codex/src/app-server/run-attempt.ts:1088-1092 calls:

projectContextEngineAssemblyForCodex({
  assembledMessages,
  originalHistoryMessages,
  prompt,
})

without passing maxRenderedContextChars.

When omitted, projectContextEngineAssemblyForCodex normalizes the optional arg back to DEFAULT_RENDERED_CONTEXT_CHARS = 24_000 per extensions/codex/src/app-server/context-engine-projection.ts:17 and :362-369.

The active-context-engine branch already passes a budget-scaled cap at extensions/codex/src/app-server/run-attempt.ts:1024-1036:

maxRenderedContextChars: resolveCodexContextEngineProjectionMaxChars({
  contextTokenBudget: params.contextTokenBudget,
  reserveTokens: resolveCodexContextEngineProjectionReserveTokens({
    config: params.config,
  }),
})

That parity fix landed in PR #80761, closing issue #80760. It does not cover the legacy mirrored-history fallback call site.

Root Cause

Steps to reproduce

  1. Use OpenClaw v2026.5.18.
  2. Configure an agent using:
    provider=openai-codex
    modelApi=openai-codex-responses
    modelId=gpt-5.5
    contextTokenBudget=272000
    plugins.slots.contextEngine unset
  3. Because plugins.slots.contextEngine is unset, the context engine defaults to legacy per src/plugins/slots.ts.
  4. Continue a session until a new Codex thread is established while prior user turns already exist.
  5. Trigger a mid-session Codex thread rebuild, for example by clearing/losing the startup binding or by hitting a dynamic-tool fingerprint mismatch so shouldProjectMirroredHistoryForCodexStart returns true.
  6. Observe that the fallback rebuild uses the 24,000-character default projection cap rather than a cap derived from the 272,000-token budget.

Fix Action

Fixed

PR fix notes

PR #84093: fix(codex): budget legacy mirrored history projection (#84084)

Description (problem / solution / changelog)

Fixes #84084.

Summary

  • Pass the existing context-token-budget-derived projection cap into the legacy mirrored-history Codex startup path.
  • Add a regression test proving high-window legacy mirrored history keeps a >30k context marker instead of falling back to the old 24k rendered cap.

Audits

  • A existing-helper audit: reused resolveCodexContextEngineProjectionMaxChars and resolveCodexContextEngineProjectionReserveTokens; no new projection sizing helper.
  • B shared-caller audit: only changes the legacy fallback caller; projectContextEngineAssemblyForCodex contract stays unchanged.
  • C rival audit: exact open PR searches for 84084, legacy mirrored-history, and maxRenderedContextChars returned no issue-specific rival PR.

Real behavior proof

  • Behavior addressed: Legacy mirrored-history Codex startup now sizes projected context from the runtime contextTokenBudget instead of using the old 24k rendered-character default.
  • Real environment tested: Local OpenClaw checkout on Linux, branch fix-codex-legacy-mirrored-budget-84084, commit bd91823ef2, Node-based extension app-server path.
  • Exact steps or command run after this patch: node scripts/run-vitest.mjs run --config test/vitest/vitest.extensions.config.ts extensions/codex/src/app-server/run-attempt.test.ts
  • Evidence after fix: terminal output from the local run:
RUN  v4.1.6 /home/chenglunhu/code/openclaw

Test Files  1 passed (1)
Tests       183 passed (183)
Start at    18:46:02
Duration    22.07s (transform 2.54s, setup 113ms, import 4.29s, tests 17.59s, environment 0ms)
  • Observed result after fix: The new legacy mirrored-history startup case preserved a LEGACY_CONTEXT_END marker after more than 30k characters of projected context and did not emit [truncated ...]; before this patch, that fallback path did not pass maxRenderedContextChars, so the same projection used the default 24k cap.
  • What was not tested: A live hosted Codex provider turn was not run; this is an app-server startup/projection behavior exercised in the local OpenClaw runtime test harness.

Validation

  • node scripts/run-vitest.mjs run --config test/vitest/vitest.extensions.config.ts extensions/codex/src/app-server/run-attempt.test.ts

Changed files

  • extensions/codex/src/app-server/run-attempt.test.ts (modified, +27/-0)
  • extensions/codex/src/app-server/run-attempt.ts (modified, +6/-0)

Code Example

projectContextEngineAssemblyForCodex({
  assembledMessages,
  originalHistoryMessages,
  prompt,
})

---

maxRenderedContextChars: resolveCodexContextEngineProjectionMaxChars({
  contextTokenBudget: params.contextTokenBudget,
  reserveTokens: resolveCodexContextEngineProjectionReserveTokens({
    config: params.config,
  }),
})

---

provider=openai-codex
   modelApi=openai-codex-responses
   modelId=gpt-5.5
   contextTokenBudget=272000
   plugins.slots.contextEngine unset

---

(contextTokenBudget - reserveTokens) * 4

---

[DEFAULT_RENDERED_CONTEXT_CHARS=24000, MAX_RENDERED_CONTEXT_CHARS=1000000]

---

DEFAULT_RENDERED_CONTEXT_CHARS = 24000

---

Snapshot pair A (earlier):
                  Rebuild-heavy channel      Stable channel
Context           36k / 272k (13%)            118k / 272k (43%)
Compactions       0                            0

Snapshot pair B (later):
                  Rebuild-heavy channel      Stable channel
Context           36k / 272k (13%)            90k / 272k (33%)
Compactions       0                            0

---

24,000 rendered chars ~= 6,000 tokens of projected history
+ ~25k-30k tokens of system prompt, developer instructions, tool schemas, and current user message
= ~31k tokens fresh input per fallback turn

---

openai-codex/gpt-5.5 via Codex app-server
modelApi=openai-codex-responses
contextTokenBudget=272000

---

openclaw -> openai-codex (Codex OAuth) -> OpenAI Codex Responses API

---

effective plugins.slots.contextEngine = "legacy"

---

#80760 / PR #80761:
Active context-engine branch was changed to pass a context-budget-scaled
maxRenderedContextChars. The same fix was not applied to the legacy fallback branch.

#83127:
Fixed truncation direction inside projectContextEngineAssemblyForCodex via the
new truncateOlderContext helper (context-engine-projection.ts:385-402).
Applies to both branches because the helper is inside the projection function.

#83466 (issue) / PR #83516 / commit 491ce8b753:
Fixed inbound image attachment delivery on provider=openai modelApi=openai-responses.
Does not address this legacy-fallback projection-budget gap.

---

extensions/codex/src/app-server/run-attempt.ts:1088-1092
Legacy mirrored-history fallback calls projectContextEngineAssemblyForCodex without maxRenderedContextChars.

extensions/codex/src/app-server/run-attempt.ts:1024-1036
Active-context-engine branch passes maxRenderedContextChars derived from contextTokenBudget and reserveTokens.

extensions/codex/src/app-server/context-engine-projection.ts:17
Defines DEFAULT_RENDERED_CONTEXT_CHARS = 24_000.

extensions/codex/src/app-server/context-engine-projection.ts:29-40, :362-369
Normalizes missing/optional maxRenderedContextChars to DEFAULT_RENDERED_CONTEXT_CHARS.

extensions/codex/src/app-server/context-engine-projection.ts:385-402
Tail-preserving truncateOlderContext helper from #83127, applied to both branches.

src/plugins/slots.ts:17-20
Default `contextEngine: "legacy"` when no slot or plugin selection is configured.

---

Configuration (both channels):
provider=openai-codex
modelApi=openai-codex-responses
modelId=gpt-5.5
contextTokenBudget=272000
effective plugins.slots.contextEngine=legacy

Channel A (rebuild-heavy):
Snapshot 1: Context 36k/272k (13%), Compactions 0
Snapshot 2: Context 36k/272k (13%), Compactions 0

Channel B (stable thread):
Snapshot 1: Context 118k/272k (43%), Compactions 0
Snapshot 2: Context 90k/272k (33%), Compactions 0

---

Copy the existing maxRenderedContextChars kwarg from run-attempt.ts:1024-1036
into the legacy fallback call at run-attempt.ts:1088-1092.

No new helper appears necessary because the active branch already has the required expression.

---

Context retained held at 36k / 272k (13%) on the rebuild-heavy channel
across two snapshots taken on the same day.

The same agent on a different channel without projection rebuilds reached
90-118k / 272k (33-43%) in the same period.

No compactions occurred, so the observed reduction was not caused by compaction.
RAW_BUFFERClick to expand / collapse

Bug type

Behavior bug (incorrect output/state without crash)

Beta release blocker

No

Summary

In OpenClaw v2026.5.18, the legacy mirrored-history fallback in extensions/codex/src/app-server/run-attempt.ts:1088-1092 calls:

projectContextEngineAssemblyForCodex({
  assembledMessages,
  originalHistoryMessages,
  prompt,
})

without passing maxRenderedContextChars.

When omitted, projectContextEngineAssemblyForCodex normalizes the optional arg back to DEFAULT_RENDERED_CONTEXT_CHARS = 24_000 per extensions/codex/src/app-server/context-engine-projection.ts:17 and :362-369.

The active-context-engine branch already passes a budget-scaled cap at extensions/codex/src/app-server/run-attempt.ts:1024-1036:

maxRenderedContextChars: resolveCodexContextEngineProjectionMaxChars({
  contextTokenBudget: params.contextTokenBudget,
  reserveTokens: resolveCodexContextEngineProjectionReserveTokens({
    config: params.config,
  }),
})

That parity fix landed in PR #80761, closing issue #80760. It does not cover the legacy mirrored-history fallback call site.

Steps to reproduce

  1. Use OpenClaw v2026.5.18.
  2. Configure an agent using:
    provider=openai-codex
    modelApi=openai-codex-responses
    modelId=gpt-5.5
    contextTokenBudget=272000
    plugins.slots.contextEngine unset
  3. Because plugins.slots.contextEngine is unset, the context engine defaults to legacy per src/plugins/slots.ts.
  4. Continue a session until a new Codex thread is established while prior user turns already exist.
  5. Trigger a mid-session Codex thread rebuild, for example by clearing/losing the startup binding or by hitting a dynamic-tool fingerprint mismatch so shouldProjectMirroredHistoryForCodexStart returns true.
  6. Observe that the fallback rebuild uses the 24,000-character default projection cap rather than a cap derived from the 272,000-token budget.

Expected behavior

The legacy mirrored-history fallback should pass maxRenderedContextChars derived from contextTokenBudget, matching the active-context-engine branch behavior added in PR #80761.

For a 272,000-token budget, resolveCodexContextEngineProjectionMaxChars computes:

(contextTokenBudget - reserveTokens) * 4

clamped to:

[DEFAULT_RENDERED_CONTEXT_CHARS=24000, MAX_RENDERED_CONTEXT_CHARS=1000000]

With the default reserve, a 272,000-token budget reaches the MAX_RENDERED_CONTEXT_CHARS = 1,000,000 clamp, or roughly 250,000 tokens of rendered history capacity.

Actual behavior

The legacy mirrored-history fallback omits maxRenderedContextChars, so projectContextEngineAssemblyForCodex falls back to:

DEFAULT_RENDERED_CONTEXT_CHARS = 24000

That is roughly 6,000 tokens of rendered history. In production observations on v2026.5.18 with provider=openai-codex, modelApi=openai-codex-responses, modelId=gpt-5.5, and contextTokenBudget=272000, a channel whose behavior is consistent with legacy fallback rebuilds shows much lower retained context than a stable channel. Two snapshots from the same agent at different times, both on v2026.5.18:

Snapshot pair A (earlier):
                  Rebuild-heavy channel      Stable channel
Context           36k / 272k (13%)            118k / 272k (43%)
Compactions       0                            0

Snapshot pair B (later):
                  Rebuild-heavy channel      Stable channel
Context           36k / 272k (13%)            90k / 272k (33%)
Compactions       0                            0

The retained-context ratio is consistently in the 13% band on the rebuild-heavy channel and in the 33-43% band on the stable channel. The rebuild-heavy channel was observed not to grow past ~36k of total context usage, which is consistent with each new-thread turn re-rendering projected history at the 24,000-character cap plus normal system/tool/current-message overhead.

The fresh-input size is consistent with the code path:

24,000 rendered chars ~= 6,000 tokens of projected history
+ ~25k-30k tokens of system prompt, developer instructions, tool schemas, and current user message
= ~31k tokens fresh input per fallback turn

OpenClaw version

v2026.5.18

Operating system

Darwin 25.4.0

Install method

npm global

Model

openai-codex/gpt-5.5 via Codex app-server
modelApi=openai-codex-responses
contextTokenBudget=272000

Provider / routing chain

openclaw -> openai-codex (Codex OAuth) -> OpenAI Codex Responses API

Additional provider/model setup details

effective plugins.slots.contextEngine = "legacy"

plugins.slots.contextEngine is unset in the user openclaw.json. legacy is the resolved default per src/plugins/slots.ts:17-20 (default contextEngine: "legacy"). No third-party context-engine plugin installed.

Relevant prior merged work:

#80760 / PR #80761:
Active context-engine branch was changed to pass a context-budget-scaled
maxRenderedContextChars. The same fix was not applied to the legacy fallback branch.

#83127:
Fixed truncation direction inside projectContextEngineAssemblyForCodex via the
new truncateOlderContext helper (context-engine-projection.ts:385-402).
Applies to both branches because the helper is inside the projection function.

#83466 (issue) / PR #83516 / commit 491ce8b753:
Fixed inbound image attachment delivery on provider=openai modelApi=openai-responses.
Does not address this legacy-fallback projection-budget gap.

Logs, screenshots, and evidence

v2026.5.18 source references:

extensions/codex/src/app-server/run-attempt.ts:1088-1092
Legacy mirrored-history fallback calls projectContextEngineAssemblyForCodex without maxRenderedContextChars.

extensions/codex/src/app-server/run-attempt.ts:1024-1036
Active-context-engine branch passes maxRenderedContextChars derived from contextTokenBudget and reserveTokens.

extensions/codex/src/app-server/context-engine-projection.ts:17
Defines DEFAULT_RENDERED_CONTEXT_CHARS = 24_000.

extensions/codex/src/app-server/context-engine-projection.ts:29-40, :362-369
Normalizes missing/optional maxRenderedContextChars to DEFAULT_RENDERED_CONTEXT_CHARS.

extensions/codex/src/app-server/context-engine-projection.ts:385-402
Tail-preserving truncateOlderContext helper from #83127, applied to both branches.

src/plugins/slots.ts:17-20
Default `contextEngine: "legacy"` when no slot or plugin selection is configured.

Empirical production observation on v2026.5.18 (two consecutive snapshots across two channels under the same agent, same workspace, same OpenAI Codex OAuth profile):

Configuration (both channels):
provider=openai-codex
modelApi=openai-codex-responses
modelId=gpt-5.5
contextTokenBudget=272000
effective plugins.slots.contextEngine=legacy

Channel A (rebuild-heavy):
Snapshot 1: Context 36k/272k (13%), Compactions 0
Snapshot 2: Context 36k/272k (13%), Compactions 0

Channel B (stable thread):
Snapshot 1: Context 118k/272k (43%), Compactions 0
Snapshot 2: Context 90k/272k (33%), Compactions 0

Suggested fix shape, not normative:

Copy the existing maxRenderedContextChars kwarg from run-attempt.ts:1024-1036
into the legacy fallback call at run-attempt.ts:1088-1092.

No new helper appears necessary because the active branch already has the required expression.

Impact and severity

The default legacy context-engine path can rebuild Codex mirrored history using a 24,000-character projection cap even when the model has a 272,000-token context budget. Affected: any agent on the Codex app-server runtime with default legacy context-engine config (no third-party context-engine plugin installed) on long-running channels that re-establish Codex threads mid-session.

Observed impact in v2026.5.18 production:

Context retained held at 36k / 272k (13%) on the rebuild-heavy channel
across two snapshots taken on the same day.

The same agent on a different channel without projection rebuilds reached
90-118k / 272k (33-43%) in the same period.

No compactions occurred, so the observed reduction was not caused by compaction.

Active-context-engine users already receive the budget-scaled cap from PR #80761. The gap is limited to the legacy mirrored-history fallback call site.

Additional information

Scope guard: this issue is only about the projection budget used by the legacy mirrored-history fallback.

The separate root cause for why some channels frequently re-establish Codex threads is not part of this issue and should be filed separately after this lands. One observed trigger shape is dynamic-tool fingerprint churn when room_event inbound forces message_tool_only and changes the message tool direct/deferred spec, but that is not part of this issue and would benefit from its own investigation.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

The legacy mirrored-history fallback should pass maxRenderedContextChars derived from contextTokenBudget, matching the active-context-engine branch behavior added in PR #80761.

For a 272,000-token budget, resolveCodexContextEngineProjectionMaxChars computes:

(contextTokenBudget - reserveTokens) * 4

clamped to:

[DEFAULT_RENDERED_CONTEXT_CHARS=24000, MAX_RENDERED_CONTEXT_CHARS=1000000]

With the default reserve, a 272,000-token budget reaches the MAX_RENDERED_CONTEXT_CHARS = 1,000,000 clamp, or roughly 250,000 tokens of rendered history capacity.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

openclaw - ✅(Solved) Fix [Bug]: Codex legacy mirrored-history fallback ignores contextTokenBudget and caps high-window sessions at ~24k rendered chars [1 pull requests, 2 comments, 2 participants]