Skip to content

fix(agents): seed claude-cli fallback prompts with prior-session context (#69973)#72069

Merged
obviyus merged 6 commits into
openclaw:mainfrom
stainlu:fix/issue-69973-cli-fallback-context-seed
Apr 28, 2026
Merged

fix(agents): seed claude-cli fallback prompts with prior-session context (#69973)#72069
obviyus merged 6 commits into
openclaw:mainfrom
stainlu:fix/issue-69973-cli-fallback-context-seed

Conversation

@stainlu

@stainlu stainlu commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Closes #69973.

Why

When a claude-cli attempt fails with a fallbackable error (e.g. 402 billing limit), the next candidate runs cold. Claude Code keeps full session history in ~/.claude/projects/<id>/<sessionId>.jsonl, but the fallback runner only sees what OpenClaw assembles from its own transcript — and that transcript is empty for claude-cli sessions because OpenClaw doesn't mirror Claude's local JSONL turns into its own session file.

@steipete's triage: "the next candidate only sees what OpenClaw assembles from its own transcript/context. If the OpenClaw transcript is truncated or missing the prior CLI-backed turns, the fallback candidate starts cold while Claude later resumes fine. Concrete fix: when a claude-cli attempt fails with a fallbackable error, seed the fallback prompt from the OpenClaw transcript/compacted summary before trying the non-CLI candidate."

Pattern

Mirrors what Claude Code itself does on resume after /compact: prefer the explicit summary, then append the most recent post-boundary turns up to a char budget. Inferred from the upstream JSONL shape (type: "summary", type: "system" subtype: "compact_boundary" with compactMetadata) and confirmed against the published session-management behavior. References:

Last-N raw replay was rejected as the wrong shape: it diverges from how Claude Code itself replays after compaction, misses context if /compact already happened (the older turns are gone, only the summary remains), and chews more prompt budget than the equivalent summary.

What changed

File Role
src/gateway/cli-session-history.claude.ts New readClaudeCliFallbackSeed walks the JSONL with awareness of type: "summary" and compact_boundary entries. Pre-boundary turns are dropped (they're now in the summary). Multiple compactions: latest summary wins.
src/gateway/cli-session-history.ts Re-export the new reader + type.
src/agents/command/attempt-execution.helpers.ts New formatClaudeCliFallbackPrelude / buildClaudeCliFallbackContextPrelude. Tool blocks coalesce to compact (tool call: …) / (tool result: …) hints. Newest turns kept when truncating. Summary clearly labeled (truncated) if it overflows. resolveFallbackRetryPrompt gains an optional priorContextPrelude.
src/agents/command/attempt-execution.ts runAgentAttempt builds the prelude when isFallbackRetry && providerOverride !== claude-cli && cliSessionBindings.claude-cli.sessionId is set. Same-provider fallbacks (claude-cli → claude-cli) are unaffected — Claude's own --resume already works there.

Verified

  • 33 tests in src/agents/command/attempt-execution.test.ts (12 added)
  • 12 tests in src/gateway/cli-session-history.test.ts (7 added)
  • pnpm tsgo:core + pnpm tsgo:core:test clean
  • pnpm format + pnpm lint clean on touched files
  • Regression-catching verified: removing the prelude prepend in resolveFallbackRetryPrompt fails both new prelude cases, restoring the cold-start behavior — confirming the test pins the behavior (not just the helper signature).

Risks flagged

  • Privacy: same surface as the existing augmentChatHistoryWithCliSessionImports — we already read user's Claude JSONL for display. No new ground.
  • Path safety: reuses existing resolveClaudeCliSessionFilePath validation, which already rejects ..-style escapes.
  • Summary quality: if Claude's own /compact produced a malformed summary (see #46602), we inherit that. Mitigation: the prelude is clearly labeled "Prior session context (from claude-cli)" so the fallback model treats it as a hint, not authoritative state.
  • Char budget: defaults to 8k chars; truncates at word boundaries with so the model never sees a mid-summary fragment.

Out of scope

  • Codex CLI sessions (openai-codex as primary). Claude is the driver of the user-pain reports here. The same mechanism could be extended provider-by-provider as those CLIs expose their own structured transcripts.
  • Two-way: when a non-CLI session falls back to claude-cli. Claude has --resume and a session id of its own; cross-pollination there is a separate problem.

Files

  • src/gateway/cli-session-history.claude.ts:302+ — parser extension
  • src/gateway/cli-session-history.ts:11+ — re-export
  • src/agents/command/attempt-execution.helpers.ts:104+ — formatter + builder + extended resolveFallbackRetryPrompt
  • src/agents/command/attempt-execution.ts:265+ — integration in runAgentAttempt

@openclaw-barnacle openclaw-barnacle Bot added gateway Gateway runtime agents Agent runtime and tooling size: L labels Apr 26, 2026
@greptile-apps

greptile-apps Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR seeds fallback retry prompts with prior claude-cli session context by parsing Claude Code's local JSONL (summary + post-boundary turns) and prepending a labeled prelude to the fallback candidate's prompt, mirroring the resume-after-compaction shape Claude Code uses itself. The implementation is clean, well-tested, and the integration guard (isFallbackRetry && !isClaudeCliProvider(providerOverride)) correctly scopes the change to cross-provider fallbacks only.

Confidence Score: 4/5

Safe to merge; only P2 edge-case findings, no critical logic or security issues.

All findings are P2. The main concern is that a summary JSONL entry without a following compact_boundary (e.g. crash mid-compaction) leaves pre-summary turns in windowedTurns, causing the fallback model to see redundant context. This is a degenerate edge case and the impact is mild (extra context, not wrong context). The break-on-first-oversized-turn behavior is intentional by design. Test coverage is comprehensive (7 new cases in the gateway suite, 12 in the helpers suite) and the regression-catching verification was explicitly confirmed by the author.

src/gateway/cli-session-history.claude.ts — the summary/compact_boundary interaction in readClaudeCliFallbackSeed

Comments Outside Diff (1)

  1. src/gateway/cli-session-history.claude.ts, line 567-574 (link)

    P2 summary entry without a following compact_boundary surfaces pre-summary turns as recentTurns

    When a type: "summary" entry appears in the JSONL but is not followed by a compact_boundary (e.g., a crash mid-compaction, or a future Claude Code variant), processing the summary only updates summaryText — it does not reset windowedTurns. All turns accumulated before the summary remain in the window and end up in recentTurns, so the fallback model receives both the summary and the turns it is based on, doubling that portion of the context.

    The compact_boundary handler does reset the window, so the normal flow (summary → compact_boundary) is correct. The gap is the orphaned-summary case. A simple guard would close it:

    const explicitSummary = extractSummaryText(parsed);
    if (explicitSummary) {
      summaryText = explicitSummary;
      // Drop turns that are now represented by this summary so they are not
      // double-counted if no compact_boundary follows.
      windowedTurns = [];
      toolNameRegistry.clear();
      continue;
    }
    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: src/gateway/cli-session-history.claude.ts
    Line: 567-574
    
    Comment:
    **`summary` entry without a following `compact_boundary` surfaces pre-summary turns as `recentTurns`**
    
    When a `type: "summary"` entry appears in the JSONL but is not followed by a `compact_boundary` (e.g., a crash mid-compaction, or a future Claude Code variant), processing the `summary` only updates `summaryText` — it does **not** reset `windowedTurns`. All turns accumulated before the summary remain in the window and end up in `recentTurns`, so the fallback model receives both the summary and the turns it is based on, doubling that portion of the context.
    
    The `compact_boundary` handler does reset the window, so the normal flow (`summary → compact_boundary`) is correct. The gap is the orphaned-summary case. A simple guard would close it:
    
    ```typescript
    const explicitSummary = extractSummaryText(parsed);
    if (explicitSummary) {
      summaryText = explicitSummary;
      // Drop turns that are now represented by this summary so they are not
      // double-counted if no compact_boundary follows.
      windowedTurns = [];
      toolNameRegistry.clear();
      continue;
    }
    ```
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: src/gateway/cli-session-history.claude.ts
Line: 567-574

Comment:
**`summary` entry without a following `compact_boundary` surfaces pre-summary turns as `recentTurns`**

When a `type: "summary"` entry appears in the JSONL but is not followed by a `compact_boundary` (e.g., a crash mid-compaction, or a future Claude Code variant), processing the `summary` only updates `summaryText` — it does **not** reset `windowedTurns`. All turns accumulated before the summary remain in the window and end up in `recentTurns`, so the fallback model receives both the summary and the turns it is based on, doubling that portion of the context.

The `compact_boundary` handler does reset the window, so the normal flow (`summary → compact_boundary`) is correct. The gap is the orphaned-summary case. A simple guard would close it:

```typescript
const explicitSummary = extractSummaryText(parsed);
if (explicitSummary) {
  summaryText = explicitSummary;
  // Drop turns that are now represented by this summary so they are not
  // double-counted if no compact_boundary follows.
  windowedTurns = [];
  toolNameRegistry.clear();
  continue;
}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: src/agents/command/attempt-execution.helpers.ts
Line: 120-128

Comment:
**`break` on first oversized turn silently drops all older turns**

The loop walks newest → oldest and breaks as soon as one turn does not fit the remaining budget. This means that if the second-newest turn is large (e.g. a long tool result), any older — possibly much shorter — turns are silently omitted, even though they would fit individually.

For a "most-recent contiguous window" semantic this is intentional and the comment describes it as such. If a sparse/non-contiguous window would give better context coverage, a `continue` instead of `break` would achieve that. Since the PR explicitly chose contiguous semantics this is flagged as an observation rather than a blocking issue, but it's worth a conscious decision given the char budget is already quite generous at 8 k.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(agents): seed claude-cli fallback pr..." | Re-trigger Greptile

Comment thread src/agents/command/attempt-execution.helpers.ts

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: add644115b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/agents/command/attempt-execution.ts Outdated
Comment thread src/gateway/cli-session-history.claude.ts Outdated
@stainlu

stainlu commented Apr 26, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the review on 9987e7797f:

  • Codex P1 (provider gating): plumb originalProvider (the user-requested provider for the run) into runAgentAttempt. The seed now requires isClaudeCliProvider(originalProvider) so a stale cliSessionBindings["claude-cli"] from an unrelated past run cannot bleed into a fallback chain that started on another provider.
  • Codex P2 (summary/boundary pairing): restructured readClaudeCliFallbackSeed to queue summaries into a pendingSummary slot and flush each compact_boundary's pair atomically. A boundary with no preceding summary now correctly falls back to the boundary's own content rather than serving an older summary alongside fresh post-boundary turns. New regression test in cli-session-history.test.ts pins this; trailing summary without a boundary is also covered.
  • Greptile P2 (break behavior): kept the newest-first break semantic (a contiguous "what was happening just before the failure" window beats sparse coverage with gaps) and documented the design choice inline so a future maintainer doesn't reflexively flip it to skip-and-continue.

Verified: 33 attempt-execution + 14 cli-session-history tests; broader src/agents/command suite green (63/63); pnpm tsgo:core + type-aware oxlint clean on touched files.

@clawsweeper

clawsweeper Bot commented Apr 26, 2026

Copy link
Copy Markdown
Contributor

Codex automated review: keeping this open.

Keep this PR open. Current main still does not implement the PR’s intended cross-provider fallback seed from Claude CLI session history, while the live PR remains open, unmerged, focused on the linked fallback handoff bug, and now includes review-followup commits for source-provider gating and compact-boundary pairing.

Best possible solution:

Keep this PR open for maintainer review. The best path is to land this PR or an equivalent implementation that seeds a bounded Claude CLI summary/recent-turn prelude only when the fallback chain started on claude-cli and retries on another provider, with the regression coverage already proposed in src/agents/command and src/gateway.

What I checked:

  • current main retry helper lacks prior context seed: resolveFallbackRetryPrompt only accepts body, isFallbackRetry, and sessionHasHistory; when sessionHasHistory is false it returns the original body, which is the cold fallback case this PR targets. (src/agents/command/attempt-execution.helpers.ts:104, fb40ed99a7f0)
  • current main attempt construction does not read Claude session history: runAgentAttempt builds effectivePrompt only from the request body, fallback flag, and OpenClaw session-history boolean. There is no Claude CLI fallback seed reader call or prelude passed to the prompt helper. (src/agents/command/attempt-execution.ts:262, fb40ed99a7f0)
  • current main fallback caller lacks source-provider plumbing: The fallback loop passes candidate provider/model and isFallbackRetry into runAgentAttempt, but does not pass the original requested provider. The PR adds that field so Claude-only seeding can be gated by the failed source provider rather than stale session bindings. (src/agents/agent-command.ts:954, fb40ed99a7f0)
  • current Claude JSONL support is display/import only: Current main exports readClaudeCliSessionMessages and uses augmentChatHistoryWithCliSessionImports; there is no readClaudeCliFallbackSeed, ClaudeCliFallbackSeed, or summary/compact-boundary fallback seed API. (src/gateway/cli-session-history.claude.ts:302, fb40ed99a7f0)
  • current tests still pin no-history retry as cold: Current helper tests assert fallback retries with no OpenClaw session history preserve the original body, and the gateway history tests cover chat-history imports rather than fallback seed extraction from Claude summaries/recent turns. (src/agents/command/attempt-execution.test.ts:34, fb40ed99a7f0)
  • linked issue discussion supports the PR’s problem statement: The linked bug discussion includes triage identifying this as a fallback handoff bug and recommending that a failed claude-cli attempt seed the non-CLI fallback candidate with prior context. The issue was later closed based on broader CLI transcript persistence, but that did not add this PR’s Claude JSONL fallback seed path to current main.

Remaining risk / open question:

  • Closing this PR now would drop an active, focused implementation candidate for a fallback continuity gap that current main still lacks.
  • Before landing, maintainers should still review the privacy/context-transfer boundary: the PR intentionally reads Claude Code’s local JSONL and sends a bounded summary/recent-turn prelude to a different fallback provider.
  • The PR’s JSONL parsing relies on Claude Code’s observed summary and compact-boundary record shape; the added tests cover known edge cases, but maintainers should decide whether that dependency contract is acceptable.

Codex Review notes: model gpt-5.5, reasoning high; reviewed against fb40ed99a7f0.

@stainlu stainlu force-pushed the fix/issue-69973-cli-fallback-context-seed branch from 5510d33 to 12d8e44 Compare April 28, 2026 04:39
@stainlu stainlu requested a review from a team as a code owner April 28, 2026 04:39
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation cli CLI command changes scripts Repository scripts labels Apr 28, 2026
@stainlu stainlu force-pushed the fix/issue-69973-cli-fallback-context-seed branch from 26dc4ad to e06f042 Compare April 28, 2026 06:46
@openclaw-barnacle openclaw-barnacle Bot added size: L and removed docs Improvements or additions to documentation cli CLI command changes scripts Repository scripts size: XL labels Apr 28, 2026
@obviyus obviyus force-pushed the fix/issue-69973-cli-fallback-context-seed branch from e06f042 to 23d1e50 Compare April 28, 2026 07:57
@obviyus obviyus requested a review from a team as a code owner April 28, 2026 07:57
@obviyus obviyus self-assigned this Apr 28, 2026
stainlu added 2 commits April 28, 2026 13:31
…ext (openclaw#69973)

When a claude-cli attempt failed with a fallbackable error (e.g. a 402
billing limit), the next candidate -- typically a non-CLI provider --
ran with no prior conversation context. Claude Code keeps its own
JSONL session under ~/.claude/projects/, but the fallback runner only
sees what OpenClaw assembles from its own transcript, which is empty
for claude-cli sessions. The fallback model therefore behaved as if
the conversation just started, even though Claude later resumed fine.

Resolution mirrors what Claude Code itself does on resume after
compaction: prefer the explicit `/compact` summary, then append the
most recent post-boundary turns up to a char budget. Concretely:

- `readClaudeCliFallbackSeed` (gateway): walks the Claude JSONL with
  awareness of `type: "summary"` and `type: "system",
  subtype: "compact_boundary"` entries. Pre-boundary turns are dropped
  (they are represented by the summary); post-boundary turns become
  the recent-window. Multiple compactions are handled by preferring
  the latest summary. Path safety reuses the existing
  `resolveClaudeCliSessionFilePath` validation.

- `formatClaudeCliFallbackPrelude` / `buildClaudeCliFallbackContext\
Prelude` (agents helpers): format the harvested seed into a labeled
  prelude. Tool blocks are coalesced to compact "(tool call: name)" /
  "(tool result: …)" hints to keep the prompt budget honest. Newest
  turns are kept first when truncating; the summary is clearly
  labeled "(truncated)" if it overflows.

- `resolveFallbackRetryPrompt`: gains an optional
  `priorContextPrelude` that prepends before the existing retry
  marker. Empty/whitespace preludes are ignored; first-attempt prompts
  are unchanged.

- `runAgentAttempt`: builds the prelude when `isFallbackRetry === true`
  AND the new candidate is non-claude-cli AND a Claude-cli session
  binding is present. Same-provider fallbacks (claude-cli to
  claude-cli) are unaffected because Claude's own --resume still works.

Verified the new tests (12 in cli-session-history, 12 added to
attempt-execution) catch the regression: removing the prelude prepend
in resolveFallbackRetryPrompt makes both new prelude cases fail,
restoring the original cold-start behavior.

References:
- https://code.claude.com/docs/en/how-claude-code-works
- "Inside Claude Code: The Session File Format"
  https://databunny.medium.com/inside-claude-code-the-session-file-format-and-how-to-inspect-it-b9998e66d56b
…formatter

Local default oxlint did not run --type-aware so the warning was missed
on the initial commit; CI surfaced it via check-lint. Hoist the heading
into a named const so its length is read directly without the assertion.
stainlu and others added 4 commits April 28, 2026 13:31
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
The required-typed param introduced in 9987e7797f broke
attempt-execution.cli.test.ts and auth-profile-runtime-contract.test.ts
which construct runAgentAttempt params without an originalProvider field.
Make it optional and explicitly require the typeof check before passing
to isClaudeCliProvider so a missing field correctly skips the seed
(defensive default for fallback paths that didn't plumb the original
provider through, no-op for non-fallback paths).
@obviyus obviyus force-pushed the fix/issue-69973-cli-fallback-context-seed branch from 23d1e50 to aac8e06 Compare April 28, 2026 08:04

@obviyus obviyus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified the claude-cli fallback handoff: when the primary claude-cli attempt fails, the fallback prompt now carries Claude Code session context instead of starting cold.

Maintainer follow-up: made the source provider explicit, trimmed review comments, added contiguous-window coverage, and added the Unreleased changelog entry.

Local gate: pnpm test src/agents/command/attempt-execution.test.ts src/agents/command/attempt-execution.cli.test.ts src/gateway/cli-session-history.test.ts src/agents/auth-profile-runtime-contract.test.ts; pnpm tsgo:core; pnpm tsgo:test:src.

@obviyus obviyus merged commit 8da2fb1 into openclaw:main Apr 28, 2026
58 of 61 checks passed
obviyus pushed a commit that referenced this pull request Apr 28, 2026
…ndary

Addresses review on #72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
@obviyus

obviyus commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Landed on main.

Thanks @stainlu.

ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
ogt-redknie pushed a commit to ogt-redknie/OPENX that referenced this pull request May 2, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 9, 2026
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
globalcaos pushed a commit to globalcaos/tinkerclaw that referenced this pull request May 13, 2026
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 24, 2026
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
jameslcowan pushed a commit to jameslcowan/openclaw that referenced this pull request Jun 2, 2026
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
…ndary

Addresses review on openclaw#72069:

- Codex P1 ("Gate Claude prelude seeding by source provider"): the
  guard checked the *current* fallback candidate but not the failed
  attempt. A session that still carried a stale
  cliSessionBindings["claude-cli"] from an unrelated past run would
  inject Claude transcript context into a fallback chain that started
  on a different provider (e.g. openai -> openai-codex), leaking
  irrelevant prior conversation. Plumb `originalProvider` (the
  user-requested provider for the chain) through to runAgentAttempt
  and require `isClaudeCliProvider(originalProvider)` before reading
  Claude history.

- Codex P2 ("Prefer latest compact boundary when summary is missing"):
  the resolver always preferred the most recent explicit summary, so
  a later compaction without its own summary entry (rare crash case)
  paired stale summary text with post-latest-boundary turns. Restructure
  readClaudeCliFallbackSeed to queue summaries into pendingSummary and
  flush each boundary's pair atomically. A boundary with no preceding
  summary now correctly falls back to the boundary's own content
  rather than serving an older summary alongside fresh turns.

- Greptile P2 (newest-first break vs sparse coverage): the
  formatFallbackTurns walk intentionally stops on the first oversized
  turn so the prelude stays a contiguous "what was happening just
  before the failure" window. Document the design choice inline so a
  future maintainer doesn't reflexively change it to skip-and-continue.

Tests:
- New gateway cases for the boundary-without-summary edge case and
  for trailing summaries written without a paired boundary.
- existing 33 attempt-execution + 14 cli-session-history tests still
  pass; broader src/agents/command suite stays green (63/63).
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agents Agent runtime and tooling gateway Gateway runtime size: L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: claude-cli fallback turn loses prior session context even though Claude session file still has full history

2 participants