[SECURITY] Context pruning silently drops instruction/constraint-bearing content from the context window (integrity, not just cost)
Description
Description
Plugins
None required
OpenCode version
Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; see References below)
Steps to reproduce
- Run a long session until messages exceed
PRUNE_PROTECT = 40_000 tokens (every agent→user transition fires compaction.prune, packages/opencode/src/session/prompt.ts:1850).
- Early in the session, state a binding constraint (e.g. "never modify X") or have a permission denial logged.
- After pruning, the conversation window no longer contains that content (only
skill output is protected, session/compaction.ts:37-39); a later request is evaluated without it.
Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin)
Details
Pruning (compaction.prune) hard-deletes messages from the context window past PRUNE_PROTECT = 40_000 tokens on every transition to the primary (user) agent:
export const PRUNE_MINIMUM = 20_000
export const PRUNE_PROTECT = 40_000
const PRUNE_PROTECTED_TOOLS = ["skill"]
...
yield* compaction.prune({ sessionID }).pipe(Effect.ignore, Effect.forkIn(scope)) // prompt.ts:1850
The protection list contains only skill tool output (compaction.ts:39). Everything else past the protected window — including user messages, permission denials, prior instructions like "never touch production", and any constraint content that arrived mid-session (or was injected by untrusted content earlier in the conversation) — is dropped with no user notice.
Impact (why this is a security issue, not just a cost one)
- Constraint eviction = permission bypass: any future agent request to do something the user explicitly denied earlier in the session is judged against a window in which the denial no longer exists. For long sessions — exactly when such constraints matter — pruning removes the evidence of the constraint.
- Attacker-influenced shaping: content earlier in the window (from untrusted files/repos/instructions processed by tools) determines what survives via token volume; a long attacker-authored block naturally pushes later constraint-bearing user messages out of the protected tail (recent tokens preserved are only
2_000–8_000).
- Silent and periodic: fires on every user→agent transition; the user is never told which messages were dropped, and
Effect.ignore swallows errors. Compaction (the summarize path) at least preserves a "Constraints & Preferences" summary — pruning preserves nothing.
- The choice of what sits in the protected tail is heuristic (tokens/turns), and
PRUNE_PROTECTED_TOOLS encodes the assumption that only tool output is disposable — user/system constraints are treated as equally disposable.
Suggested fix
- Either extend the protected set to instruction-bearing content (user messages containing rule-like text, all permission/denial records) or drop hard-pruning in favor of compaction-only summaries that carry forward a constraints section.
- Surface pruning: log what was removed per transition (and raise a user-visible notice when protected-content removal occurs).
- Default: with pruning now optional, prefer documenting/off by default — and when enabled, never prune user messages whose text contains constraint patterns (denials, imperative rules) — false positives are cheaper than evicted denials.
Plugins
None required
OpenCode version
Observed in source at commits baef5cd43b and 743f6410f2, current dev branch (code-verified; file:line references in Details).
Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin).
Steps to reproduce
- Run a session long enough to exceed
PRUNE_PROTECT = 40_000 tokens (every agent→user transition fires compaction.prune, packages/opencode/src/session/prompt.ts:1850).
- Early in the session, state a binding constraint (e.g. "never modify X") or have a permission denial logged.
- After pruning, the window no longer contains that content — only
skill output is protected (session/compaction.ts:37-39) — so a later request is evaluated without it.
Details
[SECURITY] Context pruning silently drops instruction/constraint-bearing content from the context window (integrity, not just cost)
Description
Description
session/compaction.ts(PRUNE_MINIMUM/PRUNE_PROTECT/PRUNE_PROTECTED_TOOLS) +session/prompt.ts:1850(prune fired on every agent→user transition); default-ON in released builds (at currentdevit is opt-in,core/src/config/compaction.ts)Plugins
None required
OpenCode version
Observed in source at commits
baef5cd43band743f6410f2, currentdevbranch (code-verified; see References below)Steps to reproduce
PRUNE_PROTECT = 40_000tokens (every agent→user transition firescompaction.prune,packages/opencode/src/session/prompt.ts:1850).skilloutput is protected,session/compaction.ts:37-39); a later request is evaluated without it.Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin)
Details
Pruning (
compaction.prune) hard-deletes messages from the context window pastPRUNE_PROTECT = 40_000tokens on every transition to the primary (user) agent:The protection list contains only
skilltool output (compaction.ts:39). Everything else past the protected window — including user messages, permission denials, prior instructions like "never touch production", and any constraint content that arrived mid-session (or was injected by untrusted content earlier in the conversation) — is dropped with no user notice.Impact (why this is a security issue, not just a cost one)
2_000–8_000).Effect.ignoreswallows errors. Compaction (the summarize path) at least preserves a "Constraints & Preferences" summary — pruning preserves nothing.PRUNE_PROTECTED_TOOLSencodes the assumption that only tool output is disposable — user/system constraints are treated as equally disposable.Suggested fix
Plugins
None required
OpenCode version
Observed in source at commits
baef5cd43band743f6410f2, currentdevbranch (code-verified; file:line references in Details).Screenshot and/or share link
N/A — verified against source (details and file:line references below).
Operating System
All (cross-platform; verified on macOS Darwin).
Steps to reproduce
PRUNE_PROTECT = 40_000tokens (every agent→user transition firescompaction.prune,packages/opencode/src/session/prompt.ts:1850).skilloutput is protected (session/compaction.ts:37-39) — so a later request is evaluated without it.Details