Skip to content

Repository files navigation

opencode-context-guard

Keep long OpenCode sessions alive. When an assembled model request would overflow the model's context window, this trims it before dispatch.

The problem

OpenCode already compacts long sessions. But a request can be too large for reasons compaction does not cover — a huge pasted file, an imported transcript, a dozen inline screenshots.

When that happens the provider rejects the call:

the model's provider rejected the request.
check the model id, request fields, and context length

The part that makes this fatal: the same oversized transcript is what compaction would summarize. So compaction fails too, and the session is stuck. There is no prompt you can send that recovers it, because every request carries the same oversized history.

This plugin breaks the deadlock by shrinking the outgoing request.

What it does

Registers on two hooks, context (the agent loop) and compaction (checkpoint summaries) — both, or a stuck session stays stuck.

When a request exceeds a budget derived from the model's context window, it shrinks the largest offenders first:

  • Inline attachments (file parts holding data: URIs, base64 image parts) are replaced by a short text placeholder.
  • Long strings are truncated to a prefix, with a note recording how much was dropped.

It rewrites the outgoing request only. Persisted history is never touched, so nothing is lost — the session keeps working and the full data stays on disk.

Install

opencode plugin add github:mimu-sh/opencode-context-guard

Or add it to ~/.config/opencode/opencode.json:

{
  "plugins": ["github:mimu-sh/opencode-context-guard"]
}

To configure options, use the object form:

{
  "plugins": [
    {
      "package": "github:mimu-sh/opencode-context-guard",
      "options": {
        "targetRatio": 0.75,
        "verbose": true
      }
    }
  ]
}

Reload after changing configuration:

opencode reload

Options

Option Default Meaning
enabled true Master switch.
targetRatio 0.75 Aim for at most this fraction of the context window.
contextLimit 1000000 Fallback window when the model reports none.
contextLimitOverride — Force a window for every model, skipping discovery.
minString 8000 Only strings longer than this are trimmed.
keepString 2500 How many leading characters of a trimmed string survive.
charsPerToken 2.2 Conservative chars-per-token estimate.
logFile — Append JSON-lines diagnostics here.
verbose false Also write diagnostics to stderr.

What it does not do

  • It does not raise your context window. It makes oversized requests fit; it cannot make the model remember more.
  • It is lossy by design. Trimmed content is gone from that request. The original stays in the session history, so a later compaction or a fresh session can still recover it — but the current turn will not see it.
  • It does not replace compaction. Compaction summarizes; this only shrinks. Run with compaction enabled.
  • It guesses token counts. Providers do not agree on tokenization, so the estimate is deliberately conservative. A request under budget can still be rejected by a provider with a stricter limit; raise targetRatio safety by lowering it.

Why this is a plugin and not a fix upstream

The bug that motivated it is in how a request gets assembled, not in OpenCode's compaction policy. A plugin can rewrite the outgoing request without changing persisted state, which is the right blast radius for a workaround. If OpenCode grows a first-class answer, this should be deleted.

Development

npm install
npm test        # unit tests for the pruning logic

No build step. index.js and prune.js are the shipped artifacts, in plain ESM, so what you test is what runs. prune.js has no OpenCode imports, so the logic is tested directly; index.js is the plugin shell.

Why no build step

An earlier revision compiled from TypeScript. That was removed deliberately: a build step adds a way for an install to half-work (a stale or missing dist/), and a plugin that silently fails to load is worse than one that is slightly plainer to read. The JSDoc annotations in prune.js carry the same type information for editors without a compiler in the install path.

License

MIT

About

Keep long OpenCode sessions alive: trim oversized model requests so they fit the context window, without producing an invalid request.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages