-
-
Notifications
You must be signed in to change notification settings - Fork 62
Description
Summary
When a user navigates to a previous/old session in OpenCode, the DCP plugin loses all knowledge of which tool calls were previously pruned. This causes duplicated context replacements to fail and potentially re-pruning decisions that are incorrect because the plugin has no memory of its prior pruning decisions for that session.
Root Cause Analysis
Current Implementation (In-Memory Only)
The plugin stores pruned tool IDs in in-memory Map structures that are initialized when the plugin loads:
// index.ts:37-40
const prunedIdsState = new Map<string, string[]>()
const statsState = new Map<string, SessionStats>()
const toolParametersCache = new Map<string, any>() // callID -> parameters
const modelCache = new Map<string, { providerID: string; modelID: string }>()This state only persists for the lifetime of the current OpenCode process. When:
- OpenCode is restarted, OR
- A user navigates to a different session and then back
...all pruning history for that session is lost.
The Fetch Wrapper Problem
The global fetch wrapper (index.ts:72-142) is designed to replace pruned tool outputs before they're sent to the LLM:
// Collect all pruned IDs across all sessions
const allPrunedIds = new Set<string>()
for (const session of allSessions.data) {
if (session.parentID) continue
const prunedIds = prunedIdsState.get(session.id) ?? [] // ← Returns empty array for restored sessions!
prunedIds.forEach((id: string) => allPrunedIds.add(id))
}When a user returns to an old session, prunedIdsState.get(session.id) returns undefined (defaulting to []), so no tool outputs are replaced.
Impact on Conversation Flow
- Token waste: Previously pruned tool outputs are sent back to the LLM in full
- Re-pruning chaos: The janitor may re-analyze tools it already pruned, potentially making different decisions
- Inconsistent context: Some tool outputs may have been replaced with
[Output removed...]in the user's view but the LLM sees full content (or vice versa)
Reproduction Steps
- Start OpenCode and create a new session
- Perform several tool calls (reads, greps, etc.)
- Wait for DCP to prune some tools (you'll see the notification)
- Note which tools were pruned
- Navigate to a different session (or restart OpenCode)
- Navigate back to the original session
- Continue the conversation
Expected: Previously pruned tool outputs remain replaced with placeholder text
Actual: Full tool outputs are sent to the LLM, wasting tokens
Proposed Solution
DCP needs to persist pruned tool IDs to disk. Options:
Option 1: Session-scoped state file (Recommended)
Store state in a file per session:
~/.config/opencode/dcp-state/{sessionID}.json
Contents:
{
"prunedIds": ["call_abc123", "call_def456"],
"stats": { "totalToolsPruned": 5, "totalTokensSaved": 12000 },
"lastUpdated": "2025-01-15T10:30:00Z"
}Pros: Simple, isolated per session, easy to clean up
Cons: Requires state restoration on session load
Option 2: OpenCode storage API (if available)
Use OpenCode's plugin storage API if one exists to store arbitrary plugin state per session.
Option 3: Embed in session messages
Store pruned IDs in a hidden/ignored message in the session itself. This has the advantage of traveling with the session.
Additional Considerations
-
Janitor context sanitization: The
replacePrunedToolOutputsmethod injanitor.ts:395-423also relies on the in-memory state. Without persistence, the janitor sends already-pruned content back to the analysis LLM. -
State migration: Need to handle:
- Sessions created before state persistence was added
- Corrupted state files
- State file cleanup when sessions are deleted
-
Thread safety: Multiple DCP instances (multiple OpenCode windows) could race on state updates.
Environment
- DCP Version: Check
package.json - OpenCode Version: Latest
- OS: Linux
Related Code Locations
- State initialization:
index.ts:37-40 - Fetch wrapper using state:
index.ts:86-93 - Janitor state update:
janitor.ts:308-309 - Janitor context sanitization:
janitor.ts:395-423