Skip to content

[SECURITY] Context pruning silently drops instruction/constraint-bearing content from the context window (integrity, not just cost) #42437

Description

@shafqatevo

[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

  1. 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).
  2. Early in the session, state a binding constraint (e.g. "never modify X") or have a permission denial logged.
  3. 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)

  1. 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.
  2. 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).
  3. 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.
  4. 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

  1. 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).
  2. Early in the session, state a binding constraint (e.g. "never modify X") or have a permission denial logged.
  3. 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

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions