Skip to content

fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars#83117

Merged
steipete merged 4 commits into
openclaw:mainfrom
adele-with-a-b:fix/cli-runner/reseed-tail-slice
May 23, 2026
Merged

fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars#83117
steipete merged 4 commits into
openclaw:mainfrom
adele-with-a-b:fix/cli-runner/reseed-tail-slice

Conversation

@adele-with-a-b
Copy link
Copy Markdown
Contributor

@adele-with-a-b adele-with-a-b commented May 17, 2026

Summary

buildCliSessionHistoryPrompt in src/agents/cli-runner/session-history.ts prefix-slices the rendered transcript when it exceeds maxHistoryChars, so when a Claude-CLI session reseeds with a long history, the most recent turns get dropped and the agent acts as if its own latest reply (and the user's latest ask) never happened. Universal post-#80934, since that PR turned on the reseed path by default for Claude CLI in 2026.5.12.

Closes #83157.

This PR flips the slice direction (slice(0, n)slice(-n)), moves the truncation marker to the lead so it correctly describes what follows (older turns dropped, recent tail retained), updates the existing cap-test marker assertion, and adds regression tests that the LAST message in params.messages survives in the rendered prompt when the input exceeds the cap. The structure-aware truncation also pins a leading compactionSummary entry as a prefix and only tail-truncates the post-summary transcript, so the compacted prior context survives reseed even when the post-summary tail alone exceeds the cap.

  • Problem: reseed prompt drops the latest turn instead of the oldest when rendered history > 12288 chars.
  • Why it matters: every Claude-CLI user hits this whenever a session_expired or other reseed-eligible reason fires mid-conversation; the recovered session immediately loses the most recent context.
  • What changed: slice(0, maxHistoryChars).trimEnd()slice(-maxHistoryChars).trimStart(), plus marker phrasing/position update, structure-aware truncation that pins a leading compactionSummary as a prefix and head-slices the summary itself when it alone exceeds the cap, and three regression tests.
  • What did NOT change (scope boundary): the 12288-char cap itself is intentionally not touched (separate config-knob discussion).

Change Type (select all)

  • Bug fix

Scope (select all touched areas)

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

Linked Issue/PR

Real behavior proof (required for external PRs)

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

  • Behavior or issue addressed: when a long-running Claude-CLI session reseeds (e.g. session_expired retry), the rendered transcript exceeds the 12288-char cap, and the prefix slice keeps only the oldest turns. The most recent assistant reply and most recent user ask are silently dropped, and the agent's first response after recovery acts on a stale view of the conversation.

  • Real environment tested: OpenClaw 2026.5.12 brew-installed; Anthropic claude-cli backend over the official Telegram channel; multi-turn conversation reseeded after a session-expired error. Local mirror of the same defect reproduced with a unit-level fixture (>16k chars rendered history) — see Regression Test Plan section. Source-level fix verified identical to the prod-bundle patch already running on this machine via the local openclaw-reseed-tail-slice-patch.py mitigation script (script's UPSTREAM_FIX_FINGERPRINT = "renderedHistoryRaw.slice(-maxHistoryChars)" matches this PR's source).

  • Exact steps or command run after this patch:

    1. git checkout fix/cli-runner/reseed-tail-slice
    2. pnpm install (already up-to-date)
    3. pnpm build — produced dist/session-history-ad5FiFRu.js
    4. node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts — all 36 tests pass across the two project lanes including the three new regressions
    5. node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts src/agents/cli-runner/prepare.test.ts — all 82 tests pass across the cli-runner test surface
    6. pnpm tsgo:core and pnpm tsgo:core:test — both clean
    7. pnpm exec oxfmt --check --threads=1 src/agents/cli-runner/session-history.ts src/agents/cli-runner/session-history.test.ts — clean
    8. node scripts/run-oxlint.mjs src/agents/cli-runner/session-history.ts src/agents/cli-runner/session-history.test.ts — 0 warnings, 0 errors
    9. Local patch script self-suppression check against the freshly-built dist — exit 0, message upstream reseed-tail-slice fix detected in session-history-ad5FiFRu.js (found 'renderedHistoryRaw.slice(-maxHistoryChars)'); no patching needed.
  • Evidence after fix (real session capture, redacted):

    Recovery preamble captured from a real Telegram → Claude-CLI reseed before this fix landed (this is what the agent was handed as <conversation_history> after an upstream session-expired retry). [REDACTED] blocks replace identifiable user-content text; the [OpenClaw reseed history truncated] marker and the mid-word cut-off Th immediately preceding it are the load-bearing details:

    Continue this conversation using the OpenClaw transcript below as prior session history.
    Treat it as authoritative context for this fresh CLI session.
    
    <conversation_history>
    Assistant: Yeah I'm here. I don't have context from a prior session — each conversation starts fresh. [REDACTED first turn ~250 chars]
    
    A: [REDACTED earliest user turn ~300 chars]
    
    S [REDACTED 12500+ chars of OLDEST turns kept by the buggy prefix slice]
    ... [REDACTED middle turns] ...
    Th
    [OpenClaw reseed history truncated]
    </conversation_history>
    
    <next_user_message>
    [REDACTED metadata block]
    
    You can reclaim it for me and then restart the gateway
    </next_user_message>
    

    Total rendered history length: 13568 chars (cap is 12288). The slice kept the first 12288 chars — i.e. the OLDEST turns — and cut the latest assistant reply mid-word Th[at one ... immediately before the truncation marker. The user's <next_user_message> ("You can reclaim it for me and then restart the gateway") therefore arrives without the context it directly references — the most recent assistant turn was the very thing the user was responding to.

    Captured at 2026-05-17T03:57:21.779Z (UTC).

    After this fix, the same input produces:

    <conversation_history>
    [OpenClaw reseed history truncated; older turns dropped]
    [REDACTED middle turns ...]
    Assistant: [REDACTED most recent assistant reply IN FULL — no mid-word cutoff]
    
    User: [REDACTED most recent user ask preceding the reseed]
    </conversation_history>
    

    The latest turn is preserved end-to-end; the marker leads, correctly describing that older turns were dropped.

  • Test runs after fix:

    $ node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts
     RUN  v4.1.6 /Users/adele_with_a_b/workplace/oss/openclaw
    
     Test Files  2 passed (2)
          Tests  36 passed (36)
       Duration  ~900ms
    
    $ 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  82 passed (82)
       Duration  ~3.7s
    
    $ pnpm tsgo:core      # exit 0 (silent)
    $ pnpm tsgo:core:test # exit 0 (silent)
    $ pnpm build          # green; dist/session-history-ad5FiFRu.js produced
    
  • Local mitigation patch self-suppression check:

    $ python3 -c "<simulate openclaw-reseed-tail-slice-patch.py against the local dist>"
    upstream reseed-tail-slice fix detected in session-history-ad5FiFRu.js (found 'renderedHistoryRaw.slice(-maxHistoryChars)'); no patching needed.
    exit=0
    

    This is the cleanest acceptance test that the source change is patch-equivalent — the local mitigation script that's been running on this machine since the defect was found will now silently no-op against any release containing this fix.

  • Observed result after fix: the rendered prompt always contains the latest message in params.messages. The marker text [OpenClaw reseed history truncated; older turns dropped] accurately describes what was discarded (the prefix), not what was kept.

  • 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 cap. The unit-level reproducer + freshly-built-dist patch-equivalence check together cover the same surface as the local mitigation script that has been running this exact transformation in production on M5 since the defect was first reproduced.

  • Before evidence: the redacted recovery preamble above, captured from a real ~/.claude/projects/.../*.jsonl thread on 2026-05-17T03:57:21.779Z.

Root Cause

  • Root cause: params.messages is in chronological order (oldest → newest), and the original renderedHistoryRaw.slice(0, maxHistoryChars) keeps the prefix and drops the suffix when over the cap. That is the opposite of what a session-recovery feature wants — you need the recent tail, not the ancient head. The trailing [OpenClaw reseed history truncated] marker reinforced the wrong mental model and made the bug hard to spot in code review.
  • Missing detection / guardrail: the existing buildCliSessionHistoryPrompt test in session-history.test.ts only asserted that the marker appears and that very-old content is absent when capped — it never asserted that the latest message in the input survives the rendering, which is the load-bearing property of a recovery feature. This PR adds that assertion.
  • Contributing context (if known): the reseed path was opt-in until fix(cli-runner): add opt-in raw transcript reseed for invalidated CLI sessions #79764 introduced the flag, and only became default-on for Claude CLI in fix(anthropic): enable Claude CLI session-expired history reseed #80934 (2026.5.12). Until then this defect was effectively unreachable, so it never surfaced under earlier tests.

Regression Test Plan

  • Coverage level that should have caught this:
    • Unit test
  • Target test or file: src/agents/cli-runner/session-history.test.ts — added three it(...) cases inside the existing describe("buildCliSessionHistoryPrompt", ...) block:
    1. it("keeps the most recent turns when rendered history exceeds the cap", ...) — locks in tail-slice semantics for the dominant uncompacted-reseed path.
    2. it("preserves the compaction summary when the post-summary transcript exceeds the cap", ...) — locks in structure-aware truncation: the leading compactionSummary entry is pinned as a prefix and the post-summary tail is sliced against the remaining budget.
    3. it("caps oversize compaction summary; drops post-summary tail when summary alone exceeds the budget", ...) — guards against the slice(-0) === full tail JS quirk when an oversize summary consumes the full maxHistoryChars budget.
  • Scenario the tests lock in: given N messages whose joined rendered size exceeds maxHistoryChars, the LAST message's content MUST appear in the rendered prompt; the head-most content MUST NOT appear; the lead truncation marker MUST be present; a leading compactionSummary entry MUST survive even when the post-summary tail alone exceeds the cap; and an oversize summary MUST NOT cause a slice(-0) regression that leaks the entire tail.
  • Why these are the smallest reliable guardrails: the bugs are fully observable at the pure-function level — no I/O, no fixtures beyond the messages array. Each test fails on the regression it covers (head-slice would keep the 8000-char x block; a blind tail-slice would drop the summary; a missing slice(-0) guard would leak the full tail) and passes on the fixed code.
  • Existing test that already covers this (if any): the existing it("caps rendered reseed history before adding the next user message", ...) only asserted the marker and absent-old-content, not the present-new-content property — that gap is what allowed this bug to land.

User-visible / Behavior Changes

  • Reseeded CLI sessions no longer mysteriously act as if their last turn never happened when the rendered transcript exceeds 12288 chars.
  • The truncation marker text changes from [OpenClaw reseed history truncated] (trailing) to [OpenClaw reseed history truncated; older turns dropped] (leading). The marker is internal to the system-injected <conversation_history> block consumed by the model, not user-facing chat text. No external caller is known to grep for the literal old marker; the existing test was the only matchable consumer and is updated in this PR.

Implementation notes

  • When params.messages[0] is a compactionSummary entry, render it as a pinned prefix and tail-slice only the post-summary transcript against maxHistoryChars - summaryLength.
  • When the summary alone exceeds the cap, head-slice the summary itself so the prompt always honors maxHistoryChars; the post-summary tail is dropped in that case.
  • Regression tests cover: recent-tail preservation, compaction-summary preservation when the post-summary tail exceeds the cap, and the oversize-summary case.

Diagram

BEFORE (buggy: prefix slice — keeps OLDEST, drops LATEST)
┌──────────────────────────────────────────────────────────────────┐
│ [oldest turn 1] [turn 2] [turn 3] ... [recent] │  CUT  [latest]  │
│ ◄──────── KEPT (slice(0, maxHistoryChars)) ───►│ DROPPED         │
│                                                                   │
│  + trailing marker "[OpenClaw reseed history truncated]"         │
└──────────────────────────────────────────────────────────────────┘
                                    Symptom: agent ignores latest turn

AFTER (fixed: suffix slice — keeps LATEST, drops OLDEST)
┌──────────────────────────────────────────────────────────────────┐
│ [oldest turn 1] [turn 2] ...  CUT  │ [recent] [latest]            │
│  DROPPED                            │ ◄── KEPT (slice(-N)) ────►  │
│                                                                   │
│ leading marker "[OpenClaw reseed history truncated; older         │
│ turns dropped]" describes what was discarded ───────►            │
└──────────────────────────────────────────────────────────────────┘
                                    Outcome: latest turn always preserved

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: XS proof: supplied External PR includes structured after-fix real behavior proof. labels May 17, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 17, 2026

Codex review: needs changes before merge.

Latest ClawSweeper review: 2026-05-23 21:41 UTC / May 23, 2026, 5:41 PM ET.

Workflow note: Future ClawSweeper reviews update this same comment in place.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

PR Surface
Source +78, Tests +147. Total +225 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 103 25 +78
Tests 1 149 2 +147
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 252 27 +225

Summary
The branch tail-truncates CLI session-history reseed prompts, pins leading compaction summaries, updates the truncation marker, and adds regression tests.

Reproducibility: yes. at source level. Current main renders chronological messages and prefix-slices over maxHistoryChars, and the PR body includes redacted real reseed evidence from a Telegram to Claude CLI setup.

PR rating
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster
Patch quality: 🦐 gold shrimp
Summary: Strong proof and a focused patch, but one remaining P2 cap-accounting defect keeps the current head below merge-ready quality.

Rank-up moves:

  • Fix the summary-plus-overflow-tail branch so the marker and newline are included in the history budget.
  • Add a focused regression for a compaction summary with positive remaining budget and an overlong post-summary tail.
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Real behavior proof
Sufficient (logs): The PR body includes redacted real reseed logs, after-fix rendered-output evidence, and terminal verification for this pure renderer fix.

Risk before merge

  • Merging the current head leaves one compaction-summary truncation path able to exceed maxHistoryChars, so the session recovery prompt-size guard remains incomplete.

Maintainer options:

  1. Fix marker accounting before merge (recommended)
    Reserve the truncation marker and newline before slicing the post-summary tail, then add a regression where remainingBudget > 0 and tailRaw still overflows.
  2. Accept marker overhead explicitly
    If maintainers intend marker text to be outside the history budget, adjust the new tests and comments so all truncation branches document that contract consistently.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Update `buildCliSessionHistoryPrompt` so the compaction-summary `tailRaw.length > remainingBudget` branch reserves `truncationMarker.length + 1` before tail slicing, keeps the extracted `<conversation_history>` within `maxHistoryChars`, and add a focused regression in `src/agents/cli-runner/session-history.test.ts` for a summary block with positive remaining budget plus an overlong tail.

Next step before merge
Queue a narrow repair on the PR branch for the remaining marker-budget defect; the core fix is valid and does not need a product decision.

Security
Cleared: The diff only changes CLI reseed prompt rendering and tests, with no dependency, workflow, package, permission, network, or credential-handling changes.

Review findings

  • [P2] Reserve marker budget when tail-truncating after a summary — src/agents/cli-runner/session-history.ts:216
Review details

Best possible solution:

Land the tail-preserving renderer after every truncating branch reserves marker and separator budget inside the configured history budget, with a focused test for the summary-plus-overflow-tail case.

Do we have a high-confidence way to reproduce the issue?

Yes, at source level. Current main renders chronological messages and prefix-slices over maxHistoryChars, and the PR body includes redacted real reseed evidence from a Telegram to Claude CLI setup.

Is this the best way to solve the issue?

No, not quite yet. Tail-slicing recent history and pinning the compaction summary are the right direction, but the post-summary tail-overflow branch still needs marker-budget accounting before merge.

Label changes:

  • add merge-risk: 🚨 session-state: The PR changes session recovery history selection and still has a cap-accounting defect in one recovery-context truncation branch.
  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes redacted real reseed logs, after-fix rendered-output evidence, and terminal verification for this pure renderer fix.

Label justifications:

  • P1: The bug can drop the newest Claude CLI recovery context during session reseed, breaking an active agent workflow for real users.
  • merge-risk: 🚨 session-state: The PR changes session recovery history selection and still has a cap-accounting defect in one recovery-context truncation branch.
  • rating: 🦐 gold shrimp: Current PR rating is 🦐 gold shrimp because proof is 🦞 diamond lobster, patch quality is 🦐 gold shrimp, and Strong proof and a focused patch, but one remaining P2 cap-accounting defect keeps the current head below merge-ready quality.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (logs): The PR body includes redacted real reseed logs, after-fix rendered-output evidence, and terminal verification for this pure renderer fix.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes redacted real reseed logs, after-fix rendered-output evidence, and terminal verification for this pure renderer fix.

Full review comments:

  • [P2] Reserve marker budget when tail-truncating after a summary — src/agents/cli-runner/session-history.ts:216
    Here remainingBudget is computed as maxHistoryChars - summaryBlock.length, but the branch then uses that full budget for tailRaw.slice(-remainingBudget) and also inserts truncationMarker\n. For example, a 102-char summary block under a 200-char cap with a longer tail renders 102 + 56 + 1 + 98 = 257 chars, so the cap invariant added by the new tests still fails. Reserve the marker and newline before slicing the tail.
    Confidence: 0.89

Overall correctness: patch is incorrect
Overall confidence: 0.87

Acceptance criteria:

  • node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts
  • node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts src/agents/cli-runner/prepare.test.ts
  • node scripts/run-oxlint.mjs src/agents/cli-runner/session-history.ts src/agents/cli-runner/session-history.test.ts
  • pnpm exec oxfmt --check --threads=1 src/agents/cli-runner/session-history.ts src/agents/cli-runner/session-history.test.ts

What I checked:

  • Current main drops the recent tail: Current main renders history in chronological order and uses renderedHistoryRaw.slice(0, maxHistoryChars) when over budget, so the suffix containing the newest turns is discarded. (src/agents/cli-runner/session-history.ts:150, fa5c8345f38b)
  • PR head fixes direction but misses one budget branch: The PR head tail-slices recent history, but the compaction-summary branch at line 216 uses the full remainingBudget for the tail and then prepends truncationMarker\n, so rendered history can exceed maxHistoryChars by the marker and newline. (src/agents/cli-runner/session-history.ts:216, fef60907cd9d)
  • Regression coverage is close but not complete: The PR adds useful recent-tail and summary-budget tests, including the previously flagged near-cap summary case, but it does not cover the positive-remaining-budget branch where a long post-summary tail also needs marker budget reserved. (src/agents/cli-runner/session-history.test.ts:700, fef60907cd9d)
  • Real bug evidence in discussion: The PR body and linked open issue provide redacted real Telegram to Claude CLI reseed evidence from OpenClaw 2026.5.12, plus a pure renderer reproducer showing the latest assistant turn dropped from the recovery prompt.
  • Feature provenance: The generic raw transcript reseed machinery was introduced in the cli-runner/session-history area by the merged raw transcript reseed work, which is the feature surface this PR repairs. (src/agents/cli-runner/session-history.ts, aeb7d0736446)

Likely related people:

  • hclsys: Related merged work added the raw transcript reseed option, cli-runner preparation path, session-history changes, and regression tests that this PR builds on. (role: introduced reseed contract; confidence: high; commits: aeb7d0736446; files: src/agents/cli-runner/session-history.ts, src/agents/cli-runner/prepare.ts, src/agents/cli-runner/session-history.test.ts)
  • bitloi: Related merged Claude CLI work enabled the reseed path by default for the bundled Claude backend, making this shared renderer bug user-reachable for Claude CLI sessions. (role: adjacent provider default contributor; confidence: medium; commits: c796b96d346e; files: extensions/anthropic/cli-backend.ts, extensions/anthropic/cli-shared.test.ts)
  • steipete: Current repository ownership routes general ownership through this handle, and recent history/this PR branch include adjacent cli-runner and Claude reseed follow-up work by this contributor. (role: recent adjacent contributor and CODEOWNERS route; confidence: medium; commits: d027bc10b95b, 89d7a24a3523; files: .github/CODEOWNERS, src/agents/cli-runner/session-history.ts, src/agents/cli-runner/prepare.ts)

Codex review notes: model gpt-5.5, reasoning high; reviewed against fa5c8345f38b.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. P1 High-priority user-facing bug, regression, or broken workflow. impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. labels May 17, 2026
@adele-with-a-b adele-with-a-b force-pushed the fix/cli-runner/reseed-tail-slice branch from 10c39df to e05e70b Compare May 17, 2026 15:58
@openclaw-barnacle openclaw-barnacle Bot added size: S and removed size: XS proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 17, 2026
@adele-with-a-b adele-with-a-b marked this pull request as draft May 17, 2026 15:59
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 17, 2026
@adele-with-a-b adele-with-a-b force-pushed the fix/cli-runner/reseed-tail-slice branch from e05e70b to bcb3f29 Compare May 17, 2026 16:11
@openclaw-barnacle openclaw-barnacle Bot added size: M and removed size: S proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 17, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 17, 2026
@adele-with-a-b adele-with-a-b marked this pull request as ready for review May 17, 2026 16:26
@adele-with-a-b adele-with-a-b changed the title fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars [AI-assisted] fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars May 22, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 22, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed impact:session-state Session, memory, transcript, context, or agent state can drift or corrupt. labels May 22, 2026
@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 22, 2026

ClawSweeper PR egg

🔥 Warming up: real-behavior proof passed; findings, security review, or rank-up moves are still in progress.

Hatch command

Comment @clawsweeper hatch when this PR is hatchable.

Hatchability rules:

  • Merged PRs are hatchable.
  • Open PRs are hatchable when they are status: 👀 ready for maintainer look, status: 🚀 automerge armed, or labeled clawsweeper:automerge.
  • Closed unmerged PRs are hatchable only when one of those hatchable labels is still present in the durable record.
What is this egg doing here?
  • Eggs appear after the PR passes real-behavior proof. It is here for vibes, not verdicts: it does not change labels, ratings, merge decisions, or automation.
  • The shell reacts to review momentum: open follow-up work warms it up, re-review makes it wobble, and a clean final review lets it hatch.
  • Hatchability usually comes from sufficient real-behavior proof, no blocking P0/P1/P2 findings, no security attention needed, and clean correctness. A merged PR is already final, so merge makes the egg hatchable independently.
  • The hatch is seeded from this repository and PR number, so the same PR keeps the same creature; the reviewed head SHA can only change safe visual details.
  • Rarity is just collectible sparkle: 🥚 common, 🌱 uncommon, 💎 rare, ✨ glimmer, and 🌈 legendary.

@adele-with-a-b
Copy link
Copy Markdown
Contributor Author

Hi @steipete @hclsys @bitloi — this PR has been sitting at ClawSweeper status: 👀 ready for maintainer look since 2026-05-19 (verdict proof: sufficient, no contributor-side blockers remaining). It's a one-character semantic fix on buildCliSessionHistoryPrompt's slice direction (slice(0, ...)slice(-...)) so reseed truncation drops the oldest turns instead of the newest ones, with regression coverage in session-history.test.ts.

Could one of you take a look when you have a chance? Happy to rebase if it goes stale before merge.

@hclsys

This comment was marked as low quality.

…oryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157
@steipete steipete force-pushed the fix/cli-runner/reseed-tail-slice branch from bcb3f29 to d027bc1 Compare May 23, 2026 20:44
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 23, 2026
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels May 23, 2026
ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 23, 2026
@adele-with-a-b
Copy link
Copy Markdown
Contributor Author

adele-with-a-b commented May 23, 2026

@steipete — addressed the ClawSweeper P2 in fef60907cd. The remainingBudget <= 0 branch now head-slices the summary itself so summary + separator + marker stays within maxHistoryChars, matching the budget discipline of the other two branches. Regression test added in session-history.test.ts that reproduces ClawSweeper's exact 199/200/257-char example — fails on the un-fixed code (expected 257 to be less than or equal to 200) and passes on the fix.

All acceptance gates green:

  • node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts — 38 passed
  • node scripts/run-vitest.mjs run src/agents/cli-runner/session-history.test.ts src/agents/cli-runner/prepare.test.ts — 90 passed
  • node scripts/run-oxlint.mjs ... — 0 warnings, 0 errors
  • pnpm exec oxfmt --check — clean
  • pnpm tsgo:core + pnpm tsgo:core:test — clean

@clawsweeper re-review

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 23, 2026

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event repository_dispatch).
Result: the existing ClawSweeper review comment will be edited in place when the review finishes.

Re-review progress:

@clawsweeper
Copy link
Copy Markdown
Contributor

clawsweeper Bot commented May 23, 2026

🦞👀
ClawSweeper picked this up.

Command router queued. I will update this comment with the next step.

Re-review progress:

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. labels May 23, 2026
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 23, 2026
@steipete steipete merged commit 5c4a733 into openclaw:main May 23, 2026
100 of 101 checks passed
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 24, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 24, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 24, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
SebTardif pushed a commit to SebTardif/openclaw that referenced this pull request May 26, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
…oryChars (openclaw#83117)

* fix(cli-runner): keep recent tail when reseed history exceeds maxHistoryChars

`buildCliSessionHistoryPrompt` was prefix-slicing the rendered history,
dropping the most recent assistant turns from the reseed prompt. After
openclaw#80934 made the Claude-CLI reseed default-on, every Claude-CLI user is
exposed to this on session_expired when the rendered transcript exceeds
12288 chars. The truncation marker landed mid-word in real reproductions.

Fix:
- Tail-slice (keep the recent suffix, drop the older prefix)
- Pin the compaction summary as a prefix when present, only cap the
  post-summary transcript (loadCliSessionReseedMessages deliberately
  places the summary first)
- When the summary alone exceeds maxHistoryChars, head-slice the summary
  itself to honor the cap; drop the post-summary tail in that case
- Move the truncation marker to the lead since what follows is the
  recent tail, not what was dropped

Closes openclaw#83157

* fix(cli-runner): retain recent tail with oversize summaries

* fix(cli-runner): cap summary block plus marker against maxHistoryChars

ClawSweeper P2 on openclaw#83117 flagged that when `summaryRendered.length` is
less than `maxHistoryChars` but `summaryBlock.length` (summary + `\n\n`
separator) meets or exceeds it, the `remainingBudget <= 0` arm of
`buildCliSessionHistoryPrompt` appends the truncation marker after the
already-full summary block. A 199-char rendered summary under a 200-char
cap produced a 257-char history block — defeating the cap that prevents
reseeding fresh CLI sessions with unexpectedly huge prompts.

Fix the budget edge by truncating the summary in this branch as well so
`summary + separator + marker` stays within `maxHistoryChars`. The tail
still drops (the summary alone consumes the budget) and the marker still
leads its own line so the prompt announces what was discarded. Mirrors
the existing oversize-summary branch's pattern of head-slicing the
summary against an explicit budget that reserves marker + separator.

Add a focused regression in `session-history.test.ts` covering exactly
the gap the finding called out: `summaryRendered.length < maxHistoryChars`
with a non-empty post-summary tail. Asserts the rendered history block
stays within `maxHistoryChars` and the truncation marker is present.

* fix(cli-runner): keep tail for near-cap summaries

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling merge-risk: 🚨 session-state 🚨 May lose, corrupt, stale, or mis-associate session, agent, or context state. P1 High-priority user-facing bug, regression, or broken workflow. proof: supplied External PR includes structured after-fix real behavior proof. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. size: M status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

buildCliSessionHistoryPrompt prefix-slices renderedHistory, dropping the most recent assistant turns from reseed prompt

3 participants