anomalyco / anomalyco/opencode

compaction.prune never trims context in single-turn `opencode run` sessions, so long headless runs compact every ~10 minutes

Open
#47,485 2 comments 0 reactions 1 assignee View on GitHub

@neriousy is already working on this.

Since Sep 5, 2026.

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

Description

Description

Long headless runs (opencode run --format json, one user turn, several task subagents, OpenAI gpt-5.6 via ChatGPT OAuth, variant max) hit auto-compaction roughly every 10 minutes for hours. Each compaction is a 45-85k token summary call, the post-compaction context is only 22-42k, and the agent then re-reads the same files it just lost (one .cpp file was read 66 times in one session). It is not a hang, but it burns the account limit about 10x faster than it should and looks like a loop from the outside.

Root cause, in packages/opencode/src/session/compaction.ts and prompt.ts (checked on 1.18.27 and current dev):

  1. prune skips every message until it has seen two user messages (if (turns < 2) continue, compaction.ts ~line 286-292). A headless run has exactly one user message, so nothing is ever eligible.
  2. Even when it is eligible, prune is only called after the prompt loop exits (prompt.ts ~line 1338, forked into scope). In a long single-turn run the loop does not exit for hours, so the live context is never trimmed while it fills up.
  3. The compaction summary message inherits the user's variant (compaction.ts ~line 400: variant: userMessage.model.variant). With variant max the summary carries 26-94 encrypted reasoning items that are replayed on every later request at roughly 170 tokens each, which is a big part of why the post-compaction base is so large. There is no way to run the compaction agent at a cheaper variant; agent.compaction.variant in config is ignored.

Setting compaction.prune: true in config therefore has no effect for opencode run, and preserve_recent_tokens only changes what survives a compaction, not how fast the window fills.

I have a fix running locally (two commits on top of dev, tests in test/session/compaction.test.ts pass, typecheck clean):

  • call prune at the top of each loop iteration after the first step, so it runs while the session is live
  • protect by token budget only (newest PRUNE_PROTECT tokens of tool output kept verbatim, older cleared once more than PRUNE_MINIMUM is reclaimable), drop the two-turn guard
  • let the compaction agent use its own configured variant if the model has it, otherwise fall back to the user's

Measured on the same workload with the patched build: tool output in the prompt stays in a 50-59k band, ingestion dropped from about 22k to 3.8k tokens per minute, zero compactions in the window where the stock build did two, none of the 30 cleared outputs had to be re-fetched. The one cost is that the step right after a clearing loses its prompt-cache prefix, which is why the 20k minimum matters.

Plugins

@slkiser/opencode-quota (display only). OpenAI provider via the built-in ChatGPT OAuth login.

OpenCode version

1.18.27 release binary; same code on dev at e2894562f8

Steps to reproduce
  1. Config: "compaction": { "prune": true }, provider openai with ChatGPT OAuth, model gpt-5.6 with variant max.
  2. Give a large repo a task that needs a lot of reading, e.g. "audit every file under src/ and write a report", and run it headless: opencode run --format json "" (optionally with subagents via the task tool).
  3. Watch the log: message=pruning never appears while the run is going, only message=found pruned=0 total=0 after it ends. In the DB no tool part ever gets state.time.compacted set.
  4. Watch step-finish tokens.total in the JSON output: it climbs to the threshold (272k minus 20k reserve for this model) and a compaction part appears about every 10 minutes for the whole run, with the same files being read again after each one.
Screenshot and/or share link

No share link (private code). Can attach log excerpts or the DB query I used on request.

Operating System

Debian 13 (trixie), Linux 6.12

Terminal

tmux inside a Linux terminal; not terminal related, the run is headless

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.