Skip to content

fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes#87593

Merged
steipete merged 4 commits into
openclaw:mainfrom
Pluviobyte:fix/opencode-deepseek-reasoning-replay
May 28, 2026
Merged

fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes#87593
steipete merged 4 commits into
openclaw:mainfrom
Pluviobyte:fix/opencode-deepseek-reasoning-replay

Conversation

@Pluviobyte

Copy link
Copy Markdown
Contributor

Summary

  • Strip well-known tier suffixes (-free, -paid, -trial) when building the REASONING_CONTENT_REPLAY_MODEL_IDS allowlist candidates so OpenCode Zen's deepseek-v4-flash-free (and any other tier-suffixed DeepSeek/MiMo/Kimi route) matches the base model id and keeps its DeepSeek-style reasoning_content replay.
  • Out of scope: making isDeepSeek recognise OpenCode-native endpoints, adding an OpenCode Zen wrapStreamFn, or changing the replay sanitizer for non-DeepSeek-style providers. The minimal fix removes the 400 without expanding the replay contract.

Linked context

Closes #87575

Was this requested by a maintainer or owner?

No direct maintainer request; the issue is clawsweeper:fix-shape-clear + queueable-fix + source-repro, and the reporter included a 4-step root cause analysis that points at exactly this allowlist gap.

Real behavior proof (required for external PRs)

  • Behavior or issue addressed: OpenCode Zen exposes DeepSeek V4 Flash Free as deepseek-v4-flash-free. The shared OpenAI-compatible replay sanitizer used the bare allowlist (deepseek-v4-flash, etc.) so the tier-suffixed id never matched. The follow-up request to DeepSeek therefore had reasoning_content stripped from the assistant turn and DeepSeek rejected it with 400: \reasoning_content` in the thinking mode must be passed back to the API`, deadlocking the session.

  • Real environment tested: Local OpenClaw source checkout on Linux with Node v22.22.3, pnpm 11.2.2 via corepack.

  • Exact steps or command run after this patch: Ran a local OpenClaw source smoke against the real buildOpenAICompletionsParams with an OpenCode-routed deepseek-v4-flash-free model and a transcript that includes a thinking block carrying reasoning_content, then re-ran the same smoke after git stash-ing this patch on the same branch.

  • Evidence after fix: Copied console output from the smoke.

    Before-fix (patch stashed):

    === BEFORE-FIX BEHAVIOR ===
    assistant.reasoning_content present: false
    assistant keys: [ 'role', 'content' ]
    bug repro: DeepSeek API would reject follow-up since reasoning_content missing
    

    After-fix (patch restored):

    === AFTER-FIX BEHAVIOR ===
    assistant.reasoning_content present: true
    assistant.reasoning_content value: \"Need to answer politely.\"
    
  • Observed result after fix: The OpenCode-routed deepseek-v4-flash-free follow-up assistant payload now carries the original reasoning_content instead of being stripped down to bare role+content. The same matching path keeps preserving other tier-suffixed routes (MiMo -free, Kimi K2 -free).

  • What was not tested: A live OpenCode Zen call against https://opencode.ai/zen/v1. The fix is at the sanitizer boundary that consumes the same OpenAI-compatible request shape; the smoke exercises that boundary directly using the model+context the live provider produces.

  • Proof limitations or environment constraints: The after-fix proof uses the real shared sanitizer in a local checkout but does not call the OpenCode Zen endpoint because this environment is not configured with OpenCode credentials. The reporter's 400 capture in the issue documents the upstream side of the same boundary.

  • Before evidence (optional but encouraged): Captured above via git stash-ing this patch.

Tests and validation

Which commands did you run?

  • PATH=\"\$HOME/.nvm/versions/node/v22.22.3/bin:\$PATH\" node scripts/test-projects.mjs src/agents/openai-transport-stream.test.ts
  • PATH=\"\$HOME/.nvm/versions/node/v22.22.3/bin:\$PATH\" corepack pnpm exec oxfmt --check src/agents/openai-transport-stream.ts src/agents/openai-transport-stream.test.ts
  • git diff --check

Outputs:

  • openai-transport-stream.test.ts: Test Files 1 passed (1) / Tests 204 passed (204) — the existing reasoning replay coverage plus a new parameterised case for OpenCode Zen DeepSeek V4 Flash Free, OpenRouter MiMo V2 Pro Free, and OpenRouter Kimi K2 Thinking Free.
  • oxfmt --check: All matched files use the correct format.

What regression coverage was added or updated?

Added an it.each([...]) block under buildOpenAICompletionsParams sanitizes reasoning replay fields that asserts reasoning_content survives the replay sanitizer for three tier-suffixed model ids while still stripping reasoning_details, reasoning, and reasoning_text. The before-fix smoke above demonstrates the new assertions would not have passed without this patch.

What failed before this fix, if known?

The OpenCode-routed deepseek-v4-flash-free follow-up assistant payload had reasoning_content stripped, so any DeepSeek API call after the first turn returned HTTP 400 and the session deadlocked.

If no test was added, why not?

N/A.

Risk checklist

Did user-visible behavior change? (Yes/No)

Yes (positive). Tier-suffixed DeepSeek-style routes can now complete multi-turn sessions instead of failing the second request with HTTP 400.

Did config, environment, or migration behavior change? (Yes/No)

No.

Did security, auth, secrets, network, or tool execution behavior change? (Yes/No)

No.

What is the highest-risk area?

A provider that ships a model id ending in -free/-paid/-trial that does not honour the DeepSeek-style replay contract would now receive reasoning_content it did not expect.

How is that risk mitigated?

The base id (after suffix strip) still has to be in REASONING_CONTENT_REPLAY_MODEL_IDS, which only lists DeepSeek V4, Kimi/MiMo, and friends that all document the same replay contract. The suffix list is intentionally narrow (three known tier markers) and only applied to allowlist candidate matching — the wire-level sanitizer behavior is unchanged for any base id that is not on the allowlist.

Current review state

What is the next action?

Await maintainer / ClawSweeper review on (a) whether suffix-stripping is preferred over enumerating every -free variant in the allowlist, and (b) whether the OpenCode Zen plugin should also wire wrapStreamFn for parity with opencode-go.

What is still waiting on author, maintainer, CI, or external proof?

A live OpenCode Zen call producing the 400 → success transition is still not provided from this environment.

Which bot or reviewer comments were addressed?

Initial PR; no PR comments yet.

AI-assisted: yes. I reviewed the touched code paths, designed the candidate-stripping change, and ran the focused validation commands above.

Made with Cursor

@openclaw-barnacle openclaw-barnacle Bot added agents Agent runtime and tooling size: S proof: supplied External PR includes structured after-fix real behavior proof. labels May 28, 2026
@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge. Reviewed May 28, 2026, 11:25 AM ET / 15:25 UTC.

Summary
The branch strips -free, -paid, and -trial tier suffixes before matching reasoning_content replay allowlist candidates, and adds focused sanitizer plus optional OpenCode Zen live coverage.

PR surface: Source +28, Tests +176. Total +204 across 3 files.

Reproducibility: yes. source-reproducible: current main only matches bare or colon-split replay IDs, so deepseek-v4-flash-free misses the DeepSeek V4 replay allowlist and loses reasoning_content at the shared OpenAI-compatible sanitizer boundary. The linked issue supplies the upstream 400 error from that missing field.

Review metrics: 1 noteworthy metric.

  • Replay tier suffixes: 3 added. The PR makes -free, -paid, and -trial variants inherit allowlisted base-model replay behavior, which is a compatibility-sensitive provider payload change.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🐚 platinum hermit
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Mirror suffix matching in src/agents/transcript-policy.ts with focused regression coverage.
  • Run the optional credentialed OpenCode Zen live test if maintainers require endpoint proof.

Risk before merge

  • [P1] Merging expands reasoning_content preservation for -free, -paid, and -trial variants whose stripped base ID is already allowlisted; a provider that reuses one of those suffixes for a different wire contract would now receive reasoning_content.
  • [P1] The transport sanitizer and new tests handle tier-suffixed IDs, but the separate strict OpenAI-compatible transcript replay policy does not yet mirror that suffix matching for historical replay on unowned routes.
  • [P1] The supplied proof exercises the real sanitizer boundary with before/after terminal output, but it does not show a credentialed OpenCode Zen endpoint 400-to-success run.

Maintainer options:

  1. Mirror transcript replay matching (recommended)
    Apply the same tier-suffix candidate logic to src/agents/transcript-policy.ts and add transcript-policy or session-history coverage for tier-suffixed replay-required IDs before merge.
  2. Accept transport-only scope
    Maintainers may decide this PR intentionally fixes only OpenCode Zen transport payloads and leave unowned historical replay suffix handling for a later issue.
  3. Require live OpenCode Zen proof
    If endpoint proof is required, run the new optional OpenCode Zen live test with redacted credentials before merging.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Update strict OpenAI-compatible transcript replay matching to strip the same `-free`, `-paid`, and `-trial` tier suffixes used by the transport sanitizer, add focused transcript-policy/session-history regression coverage for tier-suffixed replay-required model IDs, and rerun the focused agent replay tests.

Next step before merge

  • [P2] A narrow repair can mirror the suffix matcher in the transcript replay policy and add regression coverage without changing the PR's product direction.

Security
Cleared: The diff only changes provider replay matching and tests; it does not add dependencies, secrets handling, CI permissions, release scripts, or new code-execution paths.

Review findings

  • [P2] Mirror suffix replay matching in transcript policy — src/agents/openai-transport-stream.ts:3430-3439
Review details

Best possible solution:

Land the suffix normalization after keeping the transcript replay policy in sync and making the compatibility semantics an explicit maintainer choice.

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

Yes, source-reproducible: current main only matches bare or colon-split replay IDs, so deepseek-v4-flash-free misses the DeepSeek V4 replay allowlist and loses reasoning_content at the shared OpenAI-compatible sanitizer boundary. The linked issue supplies the upstream 400 error from that missing field.

Is this the best way to solve the issue?

Mostly yes: normalizing narrow tier suffixes is a small fix at the right transport boundary. It should also update the sibling transcript replay policy so historical strict OpenAI-compatible replay does not keep a conflicting source of truth.

Full review comments:

  • [P2] Mirror suffix replay matching in transcript policy — src/agents/openai-transport-stream.ts:3430-3439
    This PR teaches the transport sanitizer that tier-suffixed MiMo/Kimi/DeepSeek IDs are replay-required, but src/agents/transcript-policy.ts still has its own candidate matcher without the tier-suffix strip. Strict OpenAI-compatible routes without a provider replay hook can still set dropReasoningFromHistory and remove reasoning_content from older replay turns before this transport code runs, leaving the new IDs only half fixed. Please share or mirror the suffix candidate logic and add transcript-policy/session-history coverage.
    Confidence: 0.86

Overall correctness: patch is incorrect
Overall confidence: 0.86

AGENTS.md: found and applied where relevant.

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

Label changes

Label changes:

  • add proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after terminal output from a local source smoke against the real buildOpenAICompletionsParams sanitizer, but no live OpenCode Zen endpoint run.
  • add rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🐚 platinum hermit and patch quality is 🦐 gold shrimp.
  • add status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The PR body includes before/after terminal output from a local source smoke against the real buildOpenAICompletionsParams sanitizer, but no live OpenCode Zen endpoint run.
  • remove rating: 🐚 platinum hermit: Current PR rating is rating: 🦐 gold shrimp, so this older rating label is no longer current.
  • remove status: 👀 ready for maintainer look: Current PR status label is status: ⏳ waiting on author.

Label justifications:

  • P2: This is a normal-priority provider replay bug fix with bounded blast radius but real multi-turn session impact.
  • merge-risk: 🚨 compatibility: The PR intentionally sends reasoning_content to additional tier-suffixed model IDs, which can affect providers whose suffix variants do not share the base model's replay contract.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🐚 platinum hermit and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The PR body includes before/after terminal output from a local source smoke against the real buildOpenAICompletionsParams sanitizer, but no live OpenCode Zen endpoint run.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes before/after terminal output from a local source smoke against the real buildOpenAICompletionsParams sanitizer, but no live OpenCode Zen endpoint run.
Evidence reviewed

PR surface:

Source +28, Tests +176. Total +204 across 3 files.

View PR surface stats
Area Files Added Removed Net
Source 1 28 0 +28
Tests 2 176 0 +176
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 3 204 0 +204

Acceptance criteria:

  • [P1] node scripts/run-vitest.mjs src/agents/openai-transport-stream.test.ts src/agents/transcript-policy.test.ts.
  • [P1] node scripts/run-vitest.mjs src/agents/embedded-agent-runner.sanitize-session-history.test.ts.
  • [P1] corepack pnpm exec oxfmt --check src/agents/openai-transport-stream.ts src/agents/openai-transport-stream.test.ts src/agents/transcript-policy.ts src/agents/transcript-policy.test.ts src/agents/embedded-agent-runner.sanitize-session-history.test.ts.
  • [P1] git diff --check.

What I checked:

  • PR diff adds suffix normalization: The PR diff adds REASONING_CONTENT_REPLAY_TIER_SUFFIXES, strips those suffixes while building transport allowlist candidates, adds sanitizer regression coverage for OpenCode Zen, MiMo, and Kimi tier-suffixed IDs, and adds an optional OpenCode Zen live test. (src/agents/openai-transport-stream.ts:3398, 7685a75fb681)
  • Current transport miss on main: Current main's transport allowlist includes bare replay-required model IDs and the candidate builder only checks the final path component plus colon parts, so deepseek-v4-flash-free does not match the deepseek-v4-flash allowlist entry. (src/agents/openai-transport-stream.ts:3386, 5216841a9e59)
  • DeepSeek replay contract: The DeepSeek provider docs state that V4 thinking sessions expect replayed assistant messages from tool turns to include reasoning_content on follow-up requests. Public docs: docs/providers/deepseek.md. (docs/providers/deepseek.md:88, 5216841a9e59)
  • Sibling transcript policy remains unsuffixed: The strict OpenAI-compatible transcript replay policy still has its own replay-required model-id candidate matcher without tier-suffix stripping, so unowned strict OpenAI-compatible historical replay can still drop reasoning for tier-suffixed IDs before transport shaping runs. (src/agents/transcript-policy.ts:169, 5216841a9e59)
  • Prior replay matching changed both surfaces: The prior Kimi reasoning replay fix extended model-id matching in both src/agents/openai-transport-stream.ts and src/agents/transcript-policy.ts, which is the relevant sibling-surface precedent for this suffix matcher. (f8b7008f7c53)
  • OpenCode provider context: The OpenCode Zen plugin registers the provider with passthrough replay hooks and no provider-local DeepSeek wrapper, so the shared OpenAI-compatible transport sanitizer remains the relevant boundary for the reported OpenCode Zen payload shape. (extensions/opencode/index.ts:35, 5216841a9e59)

Likely related people:

  • steipete: Recent OpenClaw agent-runtime and OpenCode provider history points to this account, and the current PR head includes follow-up live-test commits from the same person. (role: recent area contributor; confidence: high; commits: bb46b79d3c14, 827b0de0ce74, 9e977d159039; files: src/agents/openai-transport-stream.ts, src/agents/transcript-policy.ts, extensions/opencode/index.ts)
  • amknight: The Kimi reasoning replay fix in commit f8b7008f7c532838603faefd524935d53b8efb7b extended the same transport allowlist and transcript-policy matching surfaces used by this PR. (role: adjacent replay-fix contributor; confidence: medium; commits: f8b7008f7c53; files: src/agents/openai-transport-stream.ts, src/agents/transcript-policy.ts, extensions/kimi-coding/stream.ts)
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.

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.

@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. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. labels May 28, 2026
@clawsweeper

clawsweeper Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

ClawSweeper PR egg: ✨ hatched 💎 rare Neon Review Wisp. Rarity: 💎 rare. Trait: guards the happy path.

Details

Share on X: post this hatch
Copy: My PR egg hatched a 💎 rare Neon Review Wisp in ClawSweeper.
Hatchability:

  • 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.

About:

  • Eggs appear after real-behavior proof passes. They are collectible flavor only.
  • Review momentum changes the shell state: follow-up work warms it, re-review makes it wobble, and a clean final review lets it hatch.
  • 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.

@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@steipete steipete self-assigned this May 28, 2026
@openclaw-barnacle openclaw-barnacle Bot added extensions: opencode size: M and removed size: S proof: sufficient ClawSweeper judged the real behavior proof convincing. labels May 28, 2026
@clawsweeper clawsweeper Bot added the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
Pluviobyte and others added 4 commits May 28, 2026 16:15
…uffixes

OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.

Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.

Fixes openclaw#87575

Co-authored-by: Cursor <cursoragent@cursor.com>
oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.

Co-authored-by: Cursor <cursoragent@cursor.com>
@steipete steipete force-pushed the fix/opencode-deepseek-reasoning-replay branch from 8ac42f8 to 7685a75 Compare May 28, 2026 15:17
@openclaw-barnacle openclaw-barnacle Bot removed the proof: sufficient ClawSweeper judged the real behavior proof convincing. label May 28, 2026
@steipete

Copy link
Copy Markdown
Contributor

Verification before merge:

Behavior addressed: OpenCode Zen DeepSeek tier-suffixed model IDs now preserve replayed reasoning_content when thinking is enabled, instead of stripping it before the second turn.
Real environment tested: GitHub Actions repo secrets for OpenCode/OpenCode Zen, Blacksmith Testbox, local Node/Vitest.
Exact steps or command run after this patch:

  • node scripts/run-vitest.mjs src/agents/openai-transport-stream.test.ts extensions/opencode/opencode.live.test.ts --run
  • pnpm check:changed delegated to Blacksmith Testbox tbx_01ksqhwp5b75pqhb4abmf5dky2
  • .agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main
  • focused live workflow OpenClaw Live And E2E Checks (Reusable) run 26583837284 with live_suite_filter=native-live-extensions-o-z-other
    Evidence after fix:
  • Local focused Vitest: 2 files, 408 tests passed.
  • Testbox changed check: exit 0.
  • Autoreview: clean, no accepted/actionable findings.
  • Live shard: validate_live_provider_suites (native-live-extensions-o-z-other) passed on SHA 7685a75fb681e5117bbf7b01070fd996203c1140.
  • PR CI: required quality/security/build/type/lint checks green.
    Observed result after fix: extensions/opencode/opencode.live.test.ts passed live against deepseek-v4-flash-free, including replay after a tool call.
    What was not tested: no local OpenCode key was present, so live verification used GitHub repo secrets rather than a local OP-managed key.

@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 28, 2026
@steipete steipete merged commit ad3e3cb into openclaw:main May 28, 2026
126 of 132 checks passed
github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request May 29, 2026
…uffixes (openclaw#87593)

* fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes

OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.

Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.

Fixes openclaw#87575

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(agents): avoid spread-rebuild when iterating allowlist candidates

oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(opencode): add live DeepSeek replay probe

* test(opencode): avoid forced tool choice in live replay

---------

Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
eleboucher pushed a commit to eleboucher/homelab that referenced this pull request May 31, 2026
…026.5.28) (#759)

This PR contains the following updates:

| Package | Update | Change |
|---|---|---|
| [ghcr.io/openclaw/openclaw](https://openclaw.ai) ([source](https://github.com/openclaw/openclaw)) | patch | `2026.5.27` → `2026.5.28` |

---

### Release Notes

<details>
<summary>openclaw/openclaw (ghcr.io/openclaw/openclaw)</summary>

### [`v2026.5.28`](https://github.com/openclaw/openclaw/blob/HEAD/CHANGELOG.md#2026528)

[Compare Source](openclaw/openclaw@v2026.5.27...v2026.5.28)

##### Highlights

- Agent and Codex runtime recovery is steadier: subagents keep cwd/workspace separation, hook context stays prompt-local, session locks release on timeout abort while live OpenClaw locks survive cleanup, stale restart continuations are avoided, and Codex app-server/helper failures no longer tear down shared runtime state. ([#&#8203;87218](openclaw/openclaw#87218), [#&#8203;86875](openclaw/openclaw#86875), [#&#8203;87409](openclaw/openclaw#87409), [#&#8203;87399](openclaw/openclaw#87399), [#&#8203;87375](openclaw/openclaw#87375), [#&#8203;88129](openclaw/openclaw#88129))
- Channel delivery and session identity got safer across outbound plugin hooks, Matrix room ids, iMessage reactions/approvals, Slack final replies, Discord recovered tool warnings, runtime-config message actions, WhatsApp profile auth roots, Telegram polling, and Microsoft Teams service URL trust checks. ([#&#8203;73706](openclaw/openclaw#73706), [#&#8203;75670](openclaw/openclaw#75670), [#&#8203;87366](openclaw/openclaw#87366), [#&#8203;87451](openclaw/openclaw#87451), [#&#8203;87334](openclaw/openclaw#87334), [#&#8203;84535](openclaw/openclaw#84535), [#&#8203;82492](openclaw/openclaw#82492), [#&#8203;83304](openclaw/openclaw#83304), [#&#8203;87160](openclaw/openclaw#87160))
- Mobile and chat surfaces got a broader refresh: the iOS Pro UI, hosted push relay default, realtime Talk tab playback, Gateway chat transport, onboarding, Talk permissions, WebChat reconnect delivery, and session picker behavior now preserve more state across reconnects and empty searches. ([#&#8203;87367](openclaw/openclaw#87367), [#&#8203;87531](openclaw/openclaw#87531), [#&#8203;87682](openclaw/openclaw#87682), [#&#8203;88096](openclaw/openclaw#88096), [#&#8203;88105](openclaw/openclaw#88105)) Thanks [@&#8203;ngutman](https://github.com/ngutman) and [@&#8203;BunsDev](https://github.com/BunsDev).
- Browser, channel, and automation inputs are stricter: Browser tool timeouts, viewport/tab indices, Gateway ports, cron retry handling, Discord component ids, schema array refs, Telegram callback pages, and channel progress callbacks now reject malformed values earlier and preserve the intended delivery context. ([#&#8203;82887](openclaw/openclaw#82887))
- Provider, media, and document coverage expands with Claude Opus 4.8, Fal Krea image schemas, NVIDIA featured models, MiniMax streaming music responses, encrypted PDF extraction, voice model catalogs, GitHub Copilot agent runtime support, and a Codex Supervisor plugin path for delegated Codex workflows. ([#&#8203;87845](openclaw/openclaw#87845), [#&#8203;87890](openclaw/openclaw#87890), [#&#8203;80775](openclaw/openclaw#80775), [#&#8203;84764](openclaw/openclaw#84764), [#&#8203;87751](openclaw/openclaw#87751), [#&#8203;87794](openclaw/openclaw#87794))
- CLI, auth, doctor, and provider paths fail faster and recover more clearly: malformed numeric/version options are rejected, workspace dotenv provider credentials are ignored, heartbeat defaults, OAuth/token lifetimes, and local service startup requests are bounded, agent auth health labels are clearer, legacy `api_key` auth profiles migrate to canonical form, and restart guidance is actionable. ([#&#8203;87398](openclaw/openclaw#87398), [#&#8203;86281](openclaw/openclaw#86281), [#&#8203;87361](openclaw/openclaw#87361), [#&#8203;88133](openclaw/openclaw#88133), [#&#8203;83655](openclaw/openclaw#83655), [#&#8203;87559](openclaw/openclaw#87559), [#&#8203;88088](openclaw/openclaw#88088), [#&#8203;85924](openclaw/openclaw#85924)) Thanks [@&#8203;vincentkoc](https://github.com/vincentkoc) and [@&#8203;giodl73-repo](https://github.com/giodl73-repo).
- Plugin and Gateway hot paths do less repeated work while preserving cache correctness for install records, config JSON parsing, tool search catalogs, session stores, manifest model rows, auto-enabled plugin config, browser tokens, viewer assets, and release-split external plugin packages. ([#&#8203;86699](openclaw/openclaw#86699))
- Release, QA, and E2E validation now bound more log, artifact, harness, and cross-OS waits so failing lanes produce proof instead of hanging or false-greening.

##### Changes

- Status: show active subagent details in status output.
- Diffs: split the default language pack and expand default Diffs language coverage while keeping the host floor aligned. ([#&#8203;87370](openclaw/openclaw#87370), [#&#8203;87372](openclaw/openclaw#87372)) Thanks [@&#8203;RomneyDa](https://github.com/RomneyDa).
- ClawHub: add plugin display names plus skill verification and trust surfaces. ([#&#8203;87354](openclaw/openclaw#87354), [#&#8203;86699](openclaw/openclaw#86699)) Thanks [@&#8203;thewilloftheshadow](https://github.com/thewilloftheshadow) and [@&#8203;Patrick-Erichsen](https://github.com/Patrick-Erichsen).
- iOS: refresh the dev app with Pro Command, Chat, Agents, Settings, hosted push relay defaults, and realtime Talk playback wired to gateway sessions, diagnostics, chat, and realtime Talk. ([#&#8203;87367](openclaw/openclaw#87367), [#&#8203;88096](openclaw/openclaw#88096), [#&#8203;88105](openclaw/openclaw#88105)) Thanks [@&#8203;Solvely-Colin](https://github.com/Solvely-Colin) and [@&#8203;ngutman](https://github.com/ngutman).
- Docs: clarify Codex computer-use setup, paste-token stdin auth setup, macOS gateway sleep troubleshooting, native Codex hook relay recovery, container model auth, install deployment cards, device-token admin gating, CLI setup flow compatibility, Notte cloud browser CDP setup, and backport targets. ([#&#8203;87313](openclaw/openclaw#87313), [#&#8203;63050](openclaw/openclaw#63050), [#&#8203;87685](openclaw/openclaw#87685)) Thanks [@&#8203;bdjben](https://github.com/bdjben), [@&#8203;liaoandi](https://github.com/liaoandi), and [@&#8203;thewilloftheshadow](https://github.com/thewilloftheshadow).
- PDF/tools: use ClawPDF for PDF extraction, support encrypted PDF extraction, and surface MCP structured content in agent tool results. ([#&#8203;87670](openclaw/openclaw#87670), [#&#8203;87751](openclaw/openclaw#87751))
- Providers: add Claude Opus 4.8 support, Fal Krea image model schemas, NVIDIA featured model catalogs, MiniMax streaming music responses, and provider-backed voice model catalogs. ([#&#8203;87845](openclaw/openclaw#87845), [#&#8203;87890](openclaw/openclaw#87890), [#&#8203;80775](openclaw/openclaw#80775), [#&#8203;84764](openclaw/openclaw#84764), [#&#8203;87794](openclaw/openclaw#87794)) Thanks [@&#8203;eleqtrizit](https://github.com/eleqtrizit) and [@&#8203;vincentkoc](https://github.com/vincentkoc).
- Codex/GitHub: add the GitHub Copilot agent runtime and the Codex Supervisor plugin package.
- Plugins: externalize GitHub Copilot and Tokenjuice as official install-on-demand plugins with npm and ClawHub publish metadata.
- Workboard: add agent coordination tools for tracking and handing off active agent work.
- Discord: show commentary in progress drafts so live Discord runs expose useful in-progress context. ([#&#8203;85200](openclaw/openclaw#85200))
- Plugin SDK: add a reply payload sending hook for plugins that need to deliver channel-owned replies and flatten package types for SDK declarations. ([#&#8203;82823](openclaw/openclaw#82823), [#&#8203;87165](openclaw/openclaw#87165)) Thanks [@&#8203;piersonr](https://github.com/piersonr) and [@&#8203;RomneyDa](https://github.com/RomneyDa).
- Policy: add policy comparison, ingress-channel conformance, and sandbox-posture conformance checks. ([#&#8203;85572](openclaw/openclaw#85572), [#&#8203;85744](openclaw/openclaw#85744), [#&#8203;86768](openclaw/openclaw#86768))

##### Fixes

- Agents: fall back to local config pruning when the optional `agents delete` Gateway probe cannot authenticate, so offline installs can still delete agents without removing shared workspaces.
- Tighten phone-control mutation authorization \[AI]. ([#&#8203;87150](openclaw/openclaw#87150)) Thanks [@&#8203;pgondhi987](https://github.com/pgondhi987).
- Clarify directive persistence authorization policy \[AI]. ([#&#8203;86369](openclaw/openclaw#86369)) Thanks [@&#8203;pgondhi987](https://github.com/pgondhi987).
- Agents/Codex: keep spawned agent cwd/workspace state separated, forward ACP spawn attachments, keep hook context prompt-local, release session locks on timeout abort and runtime teardown without deleting live OpenClaw-owned locks during cleanup, avoid session event queue self-wait, clean up exec abort listeners, stream assistant deltas incrementally, recover raw missing-thread compaction failures, preserve rotated compaction session identity, keep compaction-timeout snapshots continuable, preserve shared app-server state across startup or helper failures, keep native hook relay alive across restarts and prune stale bridge files, close native hook relay replacement races, keep Claude live tool progress visible for watchdog recovery, suppress abandoned requester completion handoff, route workspace memory through tools, resolve Codex runtime models first, report quarantined dynamic tools, format `skills` command output, bind node auto-review to prepared plans, retry Claude CLI transcript probes, and bound compaction/steering retries. ([#&#8203;87218](openclaw/openclaw#87218), [#&#8203;86875](openclaw/openclaw#86875), [#&#8203;86123](openclaw/openclaw#86123), [#&#8203;88129](openclaw/openclaw#88129), [#&#8203;87399](openclaw/openclaw#87399), [#&#8203;87375](openclaw/openclaw#87375), [#&#8203;72574](openclaw/openclaw#72574), [#&#8203;87383](openclaw/openclaw#87383), [#&#8203;87400](openclaw/openclaw#87400), [#&#8203;83022](openclaw/openclaw#83022), [#&#8203;87671](openclaw/openclaw#87671), [#&#8203;87738](openclaw/openclaw#87738), [#&#8203;87747](openclaw/openclaw#87747), [#&#8203;87706](openclaw/openclaw#87706), [#&#8203;87546](openclaw/openclaw#87546), [#&#8203;87541](openclaw/openclaw#87541), [#&#8203;81048](openclaw/openclaw#81048)) Thanks [@&#8203;mbelinky](https://github.com/mbelinky), [@&#8203;Alix-007](https://github.com/Alix-007), [@&#8203;luoyanglang](https://github.com/luoyanglang), [@&#8203;yetval](https://github.com/yetval), [@&#8203;sjf](https://github.com/sjf), [@&#8203;joshavant](https://github.com/joshavant), [@&#8203;benjamin1492](https://github.com/benjamin1492), [@&#8203;c19354837](https://github.com/c19354837), [@&#8203;fuller-stack-dev](https://github.com/fuller-stack-dev), [@&#8203;pfrederiksen](https://github.com/pfrederiksen), and [@&#8203;dodge1218](https://github.com/dodge1218).
- Codex Supervisor: keep real-home app-server MCP session listing on the loaded state path, bound stored history scans, and close WebSocket probes cleanly.
- Channels: thread canonical session keys into outbound hooks, preserve Matrix room-id case, keep fallback tool warnings mention-inert, retain delivered Slack final replies during late cleanup, continue iMessage polling after denied reactions, suppress duplicate native exec approvals, resolve Gateway message actions against the active runtime config, preserve Telegram SecretRef prompt config and polling keepalives, preserve WhatsApp profile auth roots, QR display, document filenames, and plugin hook config, suppress Discord recovered tool warnings, preserve the Discord voice outbound helper, cap Discord/Signal/Zalo channel request and container timeouts, and block untrusted Teams service URLs while keeping TeamsSDK patterns aligned. ([#&#8203;73706](openclaw/openclaw#73706), [#&#8203;75670](openclaw/openclaw#75670), [#&#8203;87366](openclaw/openclaw#87366), [#&#8203;87451](openclaw/openclaw#87451), [#&#8203;87465](openclaw/openclaw#87465), [#&#8203;87334](openclaw/openclaw#87334), [#&#8203;84535](openclaw/openclaw#84535), [#&#8203;76262](openclaw/openclaw#76262), [#&#8203;83304](openclaw/openclaw#83304), [#&#8203;82492](openclaw/openclaw#82492), [#&#8203;87581](openclaw/openclaw#87581), [#&#8203;77114](openclaw/openclaw#77114), [#&#8203;86426](openclaw/openclaw#86426), [#&#8203;85529](openclaw/openclaw#85529), [#&#8203;87160](openclaw/openclaw#87160)) Thanks [@&#8203;zeroaltitude](https://github.com/zeroaltitude), [@&#8203;lukeboyett](https://github.com/lukeboyett), [@&#8203;jarvis-mns1](https://github.com/jarvis-mns1), [@&#8203;xiaotian](https://github.com/xiaotian), [@&#8203;funmerlin](https://github.com/funmerlin), [@&#8203;joshavant](https://github.com/joshavant), [@&#8203;eleqtrizit](https://github.com/eleqtrizit), [@&#8203;heyitsaamir](https://github.com/heyitsaamir), [@&#8203;amittell](https://github.com/amittell), [@&#8203;lidge-jun](https://github.com/lidge-jun), [@&#8203;liorb-mountapps](https://github.com/liorb-mountapps), [@&#8203;masatohoshino](https://github.com/masatohoshino), [@&#8203;bladin](https://github.com/bladin), and [@&#8203;giodl73-repo](https://github.com/giodl73-repo).
- CLI/auth/doctor/providers: reject malformed numeric/timeout/subcommand-version inputs, ignore workspace dotenv provider credentials, wait for respawn child shutdown, bound heartbeat defaults plus Codex, GitHub Copilot, OpenAI, Anthropic, Google, Feishu, LM Studio, MiniMax, Xiaomi TTS, and local-provider OAuth/token/model requests, harden Codex auth probes, label auth health by agent, preserve explicit agentRuntime pins during Codex model migration, warm provider auth off the main thread, honor Codex response timeouts, stop migrating current Claude Haiku 4.5 profiles to Sonnet, bound local service startup, resolve GPT-5.5 without cached catalog, migrate legacy memory auto-provider config, rewrite non-canonical `api_key` auth profiles, and make doctor restart follow-ups actionable. ([#&#8203;87398](openclaw/openclaw#87398), [#&#8203;86281](openclaw/openclaw#86281), [#&#8203;87361](openclaw/openclaw#87361), [#&#8203;88133](openclaw/openclaw#88133), [#&#8203;83655](openclaw/openclaw#83655), [#&#8203;87559](openclaw/openclaw#87559), [#&#8203;87719](openclaw/openclaw#87719), [#&#8203;88088](openclaw/openclaw#88088), [#&#8203;85924](openclaw/openclaw#85924), [#&#8203;84362](openclaw/openclaw#84362)) Thanks [@&#8203;Patrick-Erichsen](https://github.com/Patrick-Erichsen), [@&#8203;samzong](https://github.com/samzong), [@&#8203;giodl73-repo](https://github.com/giodl73-repo), [@&#8203;alkor2000](https://github.com/alkor2000), [@&#8203;mmaps](https://github.com/mmaps), [@&#8203;nxmxbbd](https://github.com/nxmxbbd), and [@&#8203;vincentkoc](https://github.com/vincentkoc).
- Gateway/security/session state: expire browser tokens after auth rotation, scope assistant idempotency dedupe, drain probe client closes, avoid stale restart continuation reuse, preserve retry-after fallbacks and stale rate-limit cooldown probes, bound webchat image and artifact transcript scans, include seconds in inbound metadata timestamps, clear completed session active runs, clear stale chat stream buffers, and evict current plugin-state namespaces at row caps. ([#&#8203;87810](openclaw/openclaw#87810), [#&#8203;87833](openclaw/openclaw#87833), [#&#8203;75089](openclaw/openclaw#75089)) Thanks [@&#8203;joshavant](https://github.com/joshavant) and [@&#8203;litang9](https://github.com/litang9).
- Config/parsing/network: reject partial numeric parsing, parse provider/Discord retry headers and dates strictly, honor IPv6 and bare IPv6 `no_proxy` entries, preserve empty plugin allowlists, canonicalize secret target array indexes, and reject malformed media content lengths, inspected TCP ports, marketplace content lengths, cron epochs, sandbox stat fields, unsafe duration values, empty config path segments, noncanonical schema array refs, unsafe Telegram callback pages, and invalid Teams attachment-fetch DNS targets. ([#&#8203;87883](openclaw/openclaw#87883)) Thanks [@&#8203;zhangguiping-xydt](https://github.com/zhangguiping-xydt).
- Browser/input hardening: reject invalid tab indexes, excessive viewport resizes, explicit zero CDP ports, malformed geolocation options, unsafe screenshot or permission-grant timeouts, loose response-body limits, invalid cookie expiries, and non-finite Browser tool delays/timeouts.
- Cron/automation: retry recurring jobs after transient model rate limits before waiting for the next scheduled slot, and preflight model fallbacks before skipping scheduled work. ([#&#8203;82887](openclaw/openclaw#82887)) Thanks [@&#8203;chen-zhang-cs-code](https://github.com/chen-zhang-cs-code).
- Auto-reply/directives: respect provider and relayed channel metadata during directive persistence so channel-originated decisions keep their intended context. ([#&#8203;87683](openclaw/openclaw#87683))
- WhatsApp: resolve the auth directory from the active profile so profile-scoped WhatsApp installs do not drift to the wrong credential root. ([#&#8203;82492](openclaw/openclaw#82492)) Thanks [@&#8203;lidge-jun](https://github.com/lidge-jun).
- Gateway/session state: clear completed session active runs, avoid cold-loading providers for MCP inventory, cache single-session child indexes, cap handshake timers, and bound preauth, auth-guard, media, transcript, readiness, and port options.
- Channels/replies: preserve channel-owned progress callbacks when verbose output is off, keep group-room progress suppression intact, prefer external session delivery context, escape Discord component id delimiters, force final TUI chat repaints, show Slack reasoning previews, and normalize Discord/Matrix/Mattermost channel numeric options. ([#&#8203;87476](openclaw/openclaw#87476), [#&#8203;87423](openclaw/openclaw#87423))
- Agents/tool args: harden smart-quoted argument repair for edit arrays and exact escaped arguments so model-produced tool calls recover without corrupting valid input. ([#&#8203;86611](openclaw/openclaw#86611)) Thanks [@&#8203;ferminquant](https://github.com/ferminquant).
- Providers/agents: preserve seeded Anthropic signatures, preserve signed thinking payloads, concatenate signature-delta chunks, preserve DeepSeek `reasoning_content` replay across tier suffixes, apply OpenRouter strict9 ids to Mistral routes, promote Ollama plain-text tool calls, load NVIDIA featured model catalogs, stream MiniMax music generation responses, and recover empty preflight compaction. ([#&#8203;87593](openclaw/openclaw#87593), [#&#8203;87493](openclaw/openclaw#87493), [#&#8203;80775](openclaw/openclaw#80775), [#&#8203;84764](openclaw/openclaw#84764)) Thanks [@&#8203;Pluviobyte](https://github.com/Pluviobyte) and [@&#8203;eleqtrizit](https://github.com/eleqtrizit).
- Media/images: skip CLI image cache refs when resolving generated images, allow trusted generated HTML attachments, and bound generated video downloads so stale refs and slow providers fail cleanly. ([#&#8203;87523](openclaw/openclaw#87523), [#&#8203;87982](openclaw/openclaw#87982))
- File transfer: handle late tar stdin pipe errors after archive validation or unpacking has already settled.
- Performance: trust install-record caches between reloads, prefer native JSON parsing, reuse unchanged tool-search catalogs, reuse gateway session and plugin metadata paths, skip unchanged store serialization, patch single-entry session writes, add precomputed session patch writers, reduce store clone allocations, cache manifest model catalog rows and auto-enabled plugin config, avoid full session snapshots for entry reads, defer configured Slack full startup, prefer bundled plugin dist entries, and slim current metadata identity caches. ([#&#8203;87760](openclaw/openclaw#87760))
- Docker/release/QA: package runtime workspace templates, stream cross-OS served artifacts, preserve sparse Crabbox run artifacts, isolate npm plugin installs per package, reject incompatible package plugin API installs, drop the leftover root Sharp dependency from package manifests after the Rastermill migration, bound OpenClaw instance logs, plugin gauntlet relay logs, MCP channel buffers, kitchen-sink scans, agent-turn assertions, QA-Lab credential broker calls, QA Matrix substrate requests, and release scenario logs, and keep release/google live guards current. ([#&#8203;87647](openclaw/openclaw#87647), [#&#8203;87477](openclaw/openclaw#87477)) Thanks [@&#8203;rohitjavvadi](https://github.com/rohitjavvadi) and [@&#8203;vincentkoc](https://github.com/vincentkoc).
- Release/CI: bound manual git fetches, ClawHub verifier responses, ClawHub owner metadata, dependency-guard error bodies, Parallels limits, startup/test/memory budget parsing, and diffs viewer build warnings so release lanes fail with useful proof instead of hanging. ([#&#8203;87839](openclaw/openclaw#87839))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xMDEuMSIsInVwZGF0ZWRJblZlciI6IjQzLjEwMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJyZW5vdmF0ZS9jb250YWluZXIiLCJ0eXBlL3BhdGNoIl19-->

Reviewed-on: https://git.erwanleboucher.dev/eleboucher/homelab/pulls/759
SYU8384 pushed a commit to SYU8384/openclaw that referenced this pull request Jun 3, 2026
…uffixes (openclaw#87593)

* fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes

OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.

Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.

Fixes openclaw#87575

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(agents): avoid spread-rebuild when iterating allowlist candidates

oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(opencode): add live DeepSeek replay probe

* test(opencode): avoid forced tool choice in live replay

---------

Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
sablehead pushed a commit to sablehead/openclaw that referenced this pull request Jun 10, 2026
…uffixes (openclaw#87593)

* fix(agents): preserve reasoning_content replay across DeepSeek tier suffixes

OpenCode Zen exposes DeepSeek V4 as `deepseek-v4-flash-free`, which keeps the upstream DeepSeek thinking-mode contract that requires `reasoning_content` to be passed back on follow-up requests. The existing replay allowlist only matched the bare ids (`deepseek-v4-flash`, `kimi-k2-thinking`, ...), so the tier-suffixed id missed every candidate and the sanitizer stripped `reasoning_content` from the assistant turn. DeepSeek then rejected the second API call with HTTP 400 and the session deadlocked.

Strip the well-known tier suffixes (`-free`, `-paid`, `-trial`) when generating allowlist candidates so the base model id matches and the reasoning replay survives. Existing matching for prefixed / colon-suffixed routes is unchanged.

Fixes openclaw#87575

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(agents): avoid spread-rebuild when iterating allowlist candidates

oxlint flagged the [...candidates] spread as an unnecessary array copy. Use an explicit baseCount loop bound instead so we still iterate the original entries while pushing tier-stripped variants onto the same array.

Co-authored-by: Cursor <cursoragent@cursor.com>

* test(opencode): add live DeepSeek replay probe

* test(opencode): avoid forced tool choice in live replay

---------

Co-authored-by: Pluviobyte <Pluviobyte@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
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 extensions: opencode merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. 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.

Bug: DeepSeek V4 Flash Free via OpenCode Zen provider fails with 400 on follow-up API calls (reasoning_content stripped)

2 participants