anomalyco / anomalyco/opencode

compact silently lands reasoning-only (empty-body) summaries, causing irrecoverable context loss

Open
#44,080 2 comments 0 reactions 1 assignee View on GitHub

@rekram1-node is already working on this.

Since Aug 22, 2026.

Dominant language
TypeScript
Stars
209k
Forks
27.5k
PR merge metrics
PR metrics pending

Description

Summary

When the model used for /compact returns a response containing only reasoning parts and no text part, opencode lands that empty-body message as the session summary and reports compact as successful. The epoch replacement then permanently destroys the original conversation history — yet everything reports success, so the agent continues with an essentially empty (corrupted) context and no warning.

Reproduction — 100% reproducible (2/2 triggers)

  • opencode v1.18.21 (latest)
  • Model: opencode-go/glm-5.3 (reasoning model)
  • Triggered compaction twice on this model; both times compact reported success while the summary message had only a reasoning part and no text part, and the original history was lost.

Steps:

  1. Use opencode-go/glm-5.3 in a session.
  2. Trigger /compact.
  3. Compact reports success; the resulting summary message has no text body; the original history is gone and cannot be recovered.

Relationship to #41571

Shares the same core failure as #41571 (Kimi K3-256k): a reasoning model produces a reasoning-only summary (no text part), opencode marks it summary: true, lands it, and swaps in an empty summary — silently dropping history. #41571 traces this to the v1.18.15 serialize() flattening change in compaction.ts (history flattened to a wall of plain text → reasoning model analyzes instead of summarizing). opencode-go/glm-5.3 on v1.18.21 exhibits the identical symptom at a 100% rate, suggesting the serialize-format issue affects reasoning models broadly, not just k3-256k. (I have not yet read compaction.ts to confirm glm-5.3's exact trigger mechanism, but the symptom + 100% rate + reasoning-model + same serialize code path make a shared root cause highly likely.)

Additional proposals

  • L0 (stream layer): detect/reject reasoning-only (no-text-part) responses as incomplete at stream end — upstream defense before compaction even runs.
  • Structural detection basis: per the @opencode-ai/sdk Part union, TextPart (type:"text") and ReasoningPart (type:"reasoning") are independent union members — a reasoning-only response has no text part at all and is structurally detectable, not a length-threshold heuristic (negligible false-positive risk).
  • Irrecoverability: once the epoch is replaced, the source material is destroyed, so any retry afterward is useless — the failure is silent and permanent. The failure must be caught before epoch replacement.

Expected behavior

  • Compact should fail and retry the LLM while source material still exists when the produced summary has no non-empty TextPart — not land it and replace the epoch.
  • The success/"compacted" marker must not be set until the summary passes body validation.

Suggested fix

Add a hasNonEmptyTextBody(message) check (iterate message.parts; require at least one type === "text" with non-whitespace content) at:

  • L0 (stream layer): at stream end, reject reasoning-only responses as incomplete.
  • L1 (compact landing): before epoch replacement, validate the summary body; on failure, retry the LLM (source still intact) instead of landing.

The same detection function can live in the SDK so plugins/tools share one definition. This is the "at minimum detect empty summaries and fail" bottom-line from #41571, plus an upstream L0 defense.

Impact

  • Severity: high — irrecoverable, silent context loss; the agent continues on a broken session with no warning because the compacted flag is set despite no real content.

Environment

  • opencode v1.18.21 (latest)
  • Model: opencode-go/glm-5.3 (reasoning model)
  • Repro rate: 2/2 (100%)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.