openclaw - ✅(Solved) Fix buildCliSessionHistoryPrompt cap is hardcoded; expose maxHistoryChars via per-backend config [1 pull requests, 1 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#83985Fetched 2026-05-20 03:45:33
View on GitHub
Comments
1
Participants
2
Timeline
17
Reactions
1
Timeline (top)
labeled ×9mentioned ×3subscribed ×3commented ×1

buildCliSessionHistoryPrompt in src/agents/cli-runner/session-history.ts:50-102 accepts a maxHistoryChars parameter, but the production call site at src/agents/cli-runner/prepare.ts:410 never passes one, so every Claude-CLI user is locked to the hardcoded MAX_CLI_SESSION_RESEED_HISTORY_CHARS = 12 * 1024 default (12288 characters / ~3000 tokens).

For users running long-context backends (Anthropic Claude Opus 4.7's 200K standard / 1M extended context, Sonnet 4.6's 200K), 12288 characters is ~1.5% of standard context and ~0.3% of extended context. Multi-topic Telegram or long-running coding sessions blow past the cap routinely. When a session_expired reseed fires, the rendered transcript still has to drop most of the conversation simply because the cap is so conservative.

Root Cause

For users running long-context backends (Anthropic Claude Opus 4.7's 200K standard / 1M extended context, Sonnet 4.6's 200K), 12288 characters is ~1.5% of standard context and ~0.3% of extended context. Multi-topic Telegram or long-running coding sessions blow past the cap routinely. When a session_expired reseed fires, the rendered transcript still has to drop most of the conversation simply because the cap is so conservative.

Fix Action

Fixed

PR fix notes

PR #83986: feat(cli-runner): expose maxReseedHistoryChars per-backend config [AI-assisted]

Description (problem / solution / changelog)

feat(cli-runner): expose maxReseedHistoryChars per-backend config

Summary

  • Plumbs agents.defaults.cliBackends.<id>.maxReseedHistoryChars through prepareCliRunContext()buildCliSessionHistoryPrompt({ ..., maxHistoryChars }). The function already accepts maxHistoryChars, but the production call site at src/agents/cli-runner/prepare.ts:410 never passed one — so every CLI-backed user has been locked to the hardcoded MAX_CLI_SESSION_RESEED_HISTORY_CHARS = 12 * 1024 default (~3000 tokens).
  • Operators on long-context backends (Anthropic Claude Opus 4.7 200K standard / 1M extended, Sonnet 4.6 200K) can now raise the cap so multi-topic sessions survive a session_expired reseed; operators on small-context local backends (Llama 3 8B, Phi-3) can leave the cap at the conservative default.
  • The in-source default (12288) is unchanged. Pure addition.

Closes #83985.

Motivation

When a long-running CLI-backed session reseeds (for example after a session_expired retry or a bundled-MCP rotation), buildCliSessionHistoryPrompt renders the prior transcript into a <conversation_history> block before the next user message. That block is capped at 12288 characters, which is ~1.5% of a Claude Opus 4.7 200K context window and ~0.3% of the 1M extended window. Multi-topic conversations routinely blow past it, and the recovered session immediately loses context.

The cap is reasonable as a floor (small-context local models would otherwise overflow on a fat reseed prompt) but bad as a hard ceiling for long-context operators. Per-backend config lets each CLI carry the cap that fits its model.

Changes

FileChange
src/config/types.agent-defaults.tsNew optional field CliBackendConfig.maxReseedHistoryChars?: number
src/config/zod-schema.core.tsStrict zod entry maxReseedHistoryChars: z.number().int().positive().optional()
src/agents/cli-runner/prepare.tsPass backendResolved.config.maxReseedHistoryChars into the existing buildCliSessionHistoryPrompt(...) call
src/agents/cli-runner/prepare.test.tsTwo new regression tests: override raises the cap; default fires when no override is set
docs/gateway/cli-backends.mdNew "Reseed history cap" section under CLI Backends
CHANGELOG.mdOne-line ### Changes entry

Test plan

pnpm install
pnpm build
node scripts/run-vitest.mjs run \
  src/agents/cli-runner/session-history.test.ts \
  src/agents/cli-runner/prepare.test.ts
pnpm tsgo:core
pnpm tsgo:core:test
pnpm exec oxfmt --check --threads=1 \
  src/config/types.agent-defaults.ts \
  src/config/zod-schema.core.ts \
  src/agents/cli-runner/prepare.ts \
  src/agents/cli-runner/prepare.test.ts \
  CHANGELOG.md \
  docs/gateway/cli-backends.md
node scripts/run-oxlint.mjs \
  src/config/types.agent-defaults.ts \
  src/config/zod-schema.core.ts \
  src/agents/cli-runner/prepare.ts \
  src/agents/cli-runner/prepare.test.ts

All green locally on feat/cli-runner/configurable-reseed-history-cap (rebased on origin/main at b34cf2f1a2).

Manual verification (operator-side):

  1. openclaw configure → set agents.defaults.cliBackends.claude-cli.maxReseedHistoryChars: 65536
  2. Trigger a session_expired reseed on a long Claude-CLI session
  3. Confirm the rendered <conversation_history> block can grow to ~64K characters before the truncation marker appears.
  4. Remove the override → confirm the marker now appears at the 12288-character boundary.

Backward compatibility

  • maxReseedHistoryChars is optional. Configs that don't set it pick up the in-source default (12288) unchanged — no user-visible default change.
  • No schema removals, no field renames, no deprecations. Pure addition.

Change Type (select all)

  • New feature

Scope (select all touched areas)

  • Memory / storage (CLI session-recovery transcript rendering cap)

Linked Issue/PR

  • Closes #83985
  • Related #79713 / #80934 — original opt-in + default-for-claude-cli for the reseed-from-raw-transcript path. The cap was introduced in the same lineage.
  • Related #83157 — separate issue about which characters get dropped when the cap fires (slice direction). Orthogonal: that one's about prefix/suffix selection, this PR is about making the cap itself configurable.
  • Closest precedent for shape: PR #79997 ("feat(tui,cli-runner): expose streamingWatchdogMs config + bump watchdog defaults") — same fix pattern: plumb a previously-hardcoded knob through config without changing defaults.

Real behavior proof (required for external PRs)

External-contributor real-environment proof, captured on macOS 15.x / Node 22 / 2026.5.12 brew install of OpenClaw with the Telegram channel + Claude CLI backend.

  • Behavior or issue addressed: the rendered <conversation_history> block produced by buildCliSessionHistoryPrompt is capped at 12288 characters (~3000 tokens) regardless of how much context the underlying model can absorb. Operators have no knob to raise it for long-context backends, so reseeds on Claude Opus 4.7 (200K standard / 1M extended), Sonnet 4.6 (200K), etc. silently drop most of the conversation even when the model could trivially carry it. With this PR, agents.defaults.cliBackends.<id>.maxReseedHistoryChars: N raises the cap to N characters for backend <id> while leaving the conservative 12288-character default in place for backends that don't set it.

  • Real environment tested: OpenClaw 2026.5.12 brew-installed on M5 Max / macOS 15.x / Node 22; Anthropic claude-cli backend over Telegram. Reproduced the underlying defect from a real ~/.claude/projects/.../<post-reseed-sessionId>.jsonl capture on 2026-05-18 23:25 EDT — a multi-topic Telegram session was reseeded after a session_expired event and the recovered context dropped several hours of prior keyboard-settings discussion because the cap fired. With the fix landed locally as a bundled-dist patch (~/.local/bin/openclaw-reseed-cap-raise-patch.py's upstream-fix detector for maxHistoryChars: backendResolved matches this PR's source change), reseeds on the same workload retain the fuller context.

  • Exact steps or command run after this patch:

    1. git checkout feat/cli-runner/configurable-reseed-history-cap
    2. pnpm install
    3. pnpm build — produced dist/session-history-Do3qpYPs.js and dist/prepare.runtime-nsC5XTOy.js
    4. node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts src/agents/cli-runner/prepare.test.ts — 36 tests pass, including the two new regressions in prepare.test.ts
    5. pnpm tsgo:core and pnpm tsgo:core:test — both clean
    6. pnpm exec oxfmt --check --threads=1 <touched files> — clean
    7. node scripts/run-oxlint.mjs <touched .ts files> — 0 warnings, 0 errors
    8. Built dist verified: grep "maxReseedHistoryChars" dist/zod-schema.core-*.js and grep "maxHistoryChars: backendResolved" dist/prepare.runtime-*.js both match — the field is in the published zod schema and the runtime call site reads it.
  • Evidence after fix (built dist + test results):

    $ grep -n "maxHistoryChars" dist/prepare.runtime-nsC5XTOy.js
    834:    maxHistoryChars: backendResolved.config.maxReseedHistoryChars
    
    $ grep -n "maxReseedHistoryChars" dist/zod-schema.core-Btm2IKuW.js
    453:    maxReseedHistoryChars: z.number().int().positive().optional(),
    
    $ node scripts/run-vitest.mjs run \
        src/agents/cli-runner/session-history.test.ts \
        src/agents/cli-runner/prepare.test.ts
     Test Files  4 passed (4)
          Tests  36 passed (36)
  • Observed result after fix: agents.defaults.cliBackends.<id>.maxReseedHistoryChars is parsed by the zod schema, surfaced on backendResolved.config, and threaded into buildCliSessionHistoryPrompt at the production call site. When set, the cap moves to the configured value; when unset, the existing 12288-character default fires unchanged.

  • What was not tested: a live OC instance running the freshly-built bundle in production-shape Telegram → Claude-CLI flow with a long enough conversation to trip the new override. The unit-level reproducer in prepare.test.ts plus the dist-grep proof that backendResolved.config.maxReseedHistoryChars flows into buildCliSessionHistoryPrompt at the only production call site cover the same surface as a live reseed test would.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: src/agents/cli-runner/prepare.test.ts — added two it(...) cases inside the existing describe("shouldSkipLocalCliCredentialEpoch", ...) block (where the other reseed-prompt assertions live):
    1. it("plumbs cliBackend.maxReseedHistoryChars into the rendered reseed prompt", ...) — given a cliBackends.<id>.maxReseedHistoryChars: 40000 config and a 20000-character compaction summary, the rendered openClawHistoryPrompt must contain the full summary marker and must NOT contain the truncation marker.
    2. it("falls back to the default reseed history cap when no override is configured", ...) — given the same 20000-character summary and no override, the rendered prompt MUST contain the truncation marker (the in-source default of 12288 fires unchanged).
  • Scenario the tests lock in: the per-backend maxReseedHistoryChars field is parsed, surfaced on backendResolved.config, and passed to buildCliSessionHistoryPrompt as maxHistoryChars; the default behavior is unchanged when the field is unset.
  • Why these are the smallest reliable guardrails: the bug was plumbing, not rendering — buildCliSessionHistoryPrompt was already correct, the call site just never passed the value. A test that exercises only buildCliSessionHistoryPrompt directly cannot catch this; the regression has to assert against prepareCliRunContext's output, which is what these tests do.
  • Existing test that already covers this (if any): none. The existing caps rendered reseed history before adding the next user message test in session-history.test.ts calls buildCliSessionHistoryPrompt directly with an explicit maxHistoryChars, so it bypasses the missing-plumbing path entirely.

User-visible / Behavior Changes

  • New optional config field agents.defaults.cliBackends.<id>.maxReseedHistoryChars.
  • No default change. Configs that don't set the field continue to cap reseed prompts at 12288 characters exactly as today.

Implementation notes

  • The field lives at the same level as reseedFromRawTranscriptWhenUncompacted and the reliability.* settings — per-backend, optional, positive integer. This keeps every CLI backend free to carry its own cap; long-context Claude can be 65536, a small-context local model on the same gateway can stay at 12288.
  • The backendResolved.config.maxReseedHistoryChars lookup at the call site is undefined when the field is unset, which buildCliSessionHistoryPrompt correctly normalizes via params.maxHistoryChars ?? MAX_CLI_SESSION_RESEED_HISTORY_CHARS — preserving the existing default exactly.
  • No defaults are bumped in this PR. Bumping the in-source MAX_CLI_SESSION_RESEED_HISTORY_CHARS = 12 * 1024 is a separate decision (it would affect every operator, including small-context local-model setups). Operators who want a higher cap can now opt in via config; the reverse-direction conversation about whether the default should also move is left for a follow-up.

Diagram

BEFORE: hardcoded cap
┌─────────────────────────────────────────────────────────────────┐
│ prepare.ts:410 → buildCliSessionHistoryPrompt({ messages, prompt }) │
│                              │                                   │
│                              ▼                                   │
│         maxHistoryChars = 12288 (always; no override path)       │
└─────────────────────────────────────────────────────────────────┘

AFTER: configurable per-backend cap
┌─────────────────────────────────────────────────────────────────┐
│ agents.defaults.cliBackends.<id>.maxReseedHistoryChars: 65536   │
│                              │                                   │
│                              ▼                                   │
│ prepare.ts:411 → buildCliSessionHistoryPrompt({                  │
│   messages, prompt,                                              │
│   maxHistoryChars: backendResolved.config.maxReseedHistoryChars  │
│ })                                                               │
│                              │                                   │
│                              ▼                                   │
│ maxHistoryChars = configured value, or 12288 default if unset    │
└─────────────────────────────────────────────────────────────────┘

AI-assisted

[AI-assisted] Built with Claude (claude-cli) under the contributor's openclaw-contributor skill. Issue scope and patch shape were derived from a real ~/.claude/projects/.../*.jsonl capture on 2026-05-18 where the 12288-char cap dropped several hours of prior conversation on a Claude Opus 4.7 reseed. Bot review conversations will be addressed promptly.

Changed files

  • CHANGELOG.md (modified, +1/-0)
  • docs/gateway/cli-backends.md (modified, +36/-0)
  • src/agents/cli-runner/prepare.test.ts (modified, +148/-0)
  • src/agents/cli-runner/prepare.ts (modified, +1/-0)
  • src/config/types.agent-defaults.ts (modified, +7/-0)
  • src/config/zod-schema.core.ts (modified, +1/-0)

Code Example

// src/agents/cli-runner/prepare.ts:410
buildCliSessionHistoryPrompt({
  messages: await loadCliSessionReseedMessages({ ... }),
  prompt: preparedPrompt,
  maxHistoryChars: backendResolved.config.maxReseedHistoryChars,  // <-- new
})

---

{
  agents: {
    defaults: {
      cliBackends: {
        "claude-cli": {
          maxReseedHistoryChars: 65536, // ~16K tokens; covers a multi-day conversation
        },
      },
    },
  },
}
RAW_BUFFERClick to expand / collapse

Summary

buildCliSessionHistoryPrompt in src/agents/cli-runner/session-history.ts:50-102 accepts a maxHistoryChars parameter, but the production call site at src/agents/cli-runner/prepare.ts:410 never passes one, so every Claude-CLI user is locked to the hardcoded MAX_CLI_SESSION_RESEED_HISTORY_CHARS = 12 * 1024 default (12288 characters / ~3000 tokens).

For users running long-context backends (Anthropic Claude Opus 4.7's 200K standard / 1M extended context, Sonnet 4.6's 200K), 12288 characters is ~1.5% of standard context and ~0.3% of extended context. Multi-topic Telegram or long-running coding sessions blow past the cap routinely. When a session_expired reseed fires, the rendered transcript still has to drop most of the conversation simply because the cap is so conservative.

Reproducer

A Claude-CLI Telegram session that covered two topics over an afternoon — one verified case from 2026-05-18:

  • ~14:00–22:30 EDT: keyboard discussion (Q6 Ultra HE actuation distance, rapid trigger settings; 0.3/0.3 recommendations with sourcing)
  • ~22:30–23:05 EDT: keymap JSON conversion via two Read tool calls + a Write (a 403-second turn rendering 4200 raw lines into the assistant transcript)
  • 23:25 EDT: session_expired reseed fires

Verified via direct read of the post-reseed ~/.claude/projects/.../<post-reseed-sessionId>.jsonl: the <conversation_history> block contains "keymap" + "keychron" (most recent content) but does NOT contain "rapid trigger" / "actuation" / "HE mode" / "0.3" — the older settings discussion got dropped to fit the 12288-char cap. The user's follow-up question "where did you get the numbers" was unanswerable from agent context because the prior recommendation chain was sliced off.

Why the current cap exists

MAX_CLI_SESSION_RESEED_HISTORY_CHARS = 12 * 1024 was added in commit f9c8a5107ce (2026-04-26, fix(cli): cap fresh session reseed size) as a stopgap to bound reseed prompt size. The commit message and surrounding code don't document why 12288 specifically — likely a lowest-common-denominator default for users running OC against small-context local models (Llama 3 8B with 8K context, Phi-3 with 4K, etc.) where stuffing 100K+ characters into a reseed prompt would overflow.

The cap is reasonable as a floor but bad as a hard ceiling for users on long-context backends.

Suggested fix

Plumb maxHistoryChars through per-backend config under agents.defaults.cliBackends.<id>. Same shape as the existing per-backend tunables (reliability.outputLimits.maxTurnRawChars, etc.) — every CLI backend can carry its own cap.

Proposed shape

Add maxReseedHistoryChars?: number to CliBackendConfig (in src/config/types.agent-defaults.ts) and to the matching zod schema in src/config/zod-schema.core.ts. Resolve at the call site:

// src/agents/cli-runner/prepare.ts:410
buildCliSessionHistoryPrompt({
  messages: await loadCliSessionReseedMessages({ ... }),
  prompt: preparedPrompt,
  maxHistoryChars: backendResolved.config.maxReseedHistoryChars,  // <-- new
})

Operator config:

{
  agents: {
    defaults: {
      cliBackends: {
        "claude-cli": {
          maxReseedHistoryChars: 65536, // ~16K tokens; covers a multi-day conversation
        },
      },
    },
  },
}

The MAX_CLI_SESSION_RESEED_HISTORY_CHARS = 12 * 1024 in-source default stays as the fallback when the field is unset — preserves current behavior for users who don't tune it, and keeps small-context local-model users safe.

Alternative (cheaper, but worse)

Just bump the in-source constant. Tradeoff: still hardcoded, still affects all users globally, still doesn't help operators with smaller local models who'd want to keep the smaller cap. The plumbing-via-config approach handles both directions — long-context backends tune up, small-model backends keep small.

Why no test caught this

The cap default is exercised in session-history.test.ts (the existing fixture sets maxHistoryChars directly on the param), but no test asserts that the prepare.ts call site actually plumbs a config-resolved value through to it. A regression test of the form "given agents.defaults.cliBackends.<id>.maxReseedHistoryChars: N, the prepared reseed prompt should be allowed to grow up to that limit before truncation" would catch any regression on the plumbing.

Related

  • #79713 / #80934 — original opt-in + default-for-claude-cli for the reseed-from-raw-transcript path. The cap was added in the same lineage.
  • #83157 — separate issue tracking the prefix/tail slice direction inside buildCliSessionHistoryPrompt. Orthogonal to this issue: that one is about which characters get dropped when the cap fires; this issue is about making when the cap fires configurable.
  • The closest precedent for this issue's shape is PR #79997 ("feat(tui,cli-runner): expose streamingWatchdogMs config + bump watchdog defaults"). Same fix shape: plumb a previously-hardcoded knob through config.

PR with the fix + a regression test will follow alongside this issue.

Stack

  • OpenClaw 2026.5.12
  • Claude CLI 2.1.143
  • Three claude-cli-backed agents (claude-cli/claude-opus-4-7)
  • macOS 15.x, M5 Max

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…

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 buildCliSessionHistoryPrompt cap is hardcoded; expose maxHistoryChars via per-backend config [1 pull requests, 1 comments, 2 participants]