Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 0 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ DCP uses multiple tools and strategies to reduce context size:

**Purge Errors** — Prunes tool inputs for tools that returned errors after a configurable number of turns (default: 4). Error messages are preserved for context, but the potentially large input content is removed. Runs automatically on every request with zero LLM cost.

**On Idle Analysis** — Uses a language model to semantically analyze conversation context during idle periods and identify tool outputs that are no longer relevant. Disabled by default (legacy behavior).

Your session history is never modified—DCP replaces pruned content with placeholders before sending requests to your LLM.

## Impact on Prompt Caching
Expand Down Expand Up @@ -118,18 +116,6 @@ DCP uses its own config file:
// Additional tools to protect from pruning
"protectedTools": [],
},
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
"onIdle": {
"enabled": false,
// Additional tools to protect from pruning
"protectedTools": [],
// Override model for analysis (format: "provider/model")
// "model": "anthropic/claude-haiku-4-5",
// Show toast notifications when model selection fails
"showModelErrorToasts": true,
// When true, fallback models are not permitted
"strictModelSelection": false,
},
},
}
```
Expand Down
5 changes: 2 additions & 3 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { Plugin } from "@opencode-ai/plugin"
import { getConfig } from "./lib/config"
import { Logger } from "./lib/logger"
import { loadPrompt } from "./lib/prompt"
import { loadPrompt } from "./lib/prompts"
import { createSessionState } from "./lib/state"
import { createDiscardTool, createExtractTool } from "./lib/strategies"
import { createChatMessageTransformHandler, createEventHandler } from "./lib/hooks"
import { createChatMessageTransformHandler } from "./lib/hooks"

const plugin: Plugin = (async (ctx) => {
const config = getConfig(ctx)
Expand Down Expand Up @@ -91,7 +91,6 @@ const plugin: Plugin = (async (ctx) => {
)
}
},
event: createEventHandler(ctx.client, config, state, logger, ctx.directory),
}
}) satisfies Plugin

Expand Down
106 changes: 0 additions & 106 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,6 @@ export interface Deduplication {
protectedTools: string[]
}

export interface OnIdle {
enabled: boolean
model?: string
showModelErrorToasts?: boolean
strictModelSelection?: boolean
protectedTools: string[]
}

export interface DiscardTool {
enabled: boolean
}
Expand Down Expand Up @@ -63,7 +55,6 @@ export interface PluginConfig {
deduplication: Deduplication
supersedeWrites: SupersedeWrites
purgeErrors: PurgeErrors
onIdle: OnIdle
}
}

Expand Down Expand Up @@ -102,13 +93,6 @@ export const VALID_CONFIG_KEYS = new Set([
"strategies.purgeErrors.enabled",
"strategies.purgeErrors.turns",
"strategies.purgeErrors.protectedTools",
// strategies.onIdle
"strategies.onIdle",
"strategies.onIdle.enabled",
"strategies.onIdle.model",
"strategies.onIdle.showModelErrorToasts",
"strategies.onIdle.strictModelSelection",
"strategies.onIdle.protectedTools",
])

// Extract all key paths from a config object for validation
Expand Down Expand Up @@ -272,60 +256,6 @@ function validateConfigTypes(config: Record<string, any>): ValidationError[] {
})
}

// onIdle
if (strategies.onIdle) {
if (
strategies.onIdle.enabled !== undefined &&
typeof strategies.onIdle.enabled !== "boolean"
) {
errors.push({
key: "strategies.onIdle.enabled",
expected: "boolean",
actual: typeof strategies.onIdle.enabled,
})
}
if (
strategies.onIdle.model !== undefined &&
typeof strategies.onIdle.model !== "string"
) {
errors.push({
key: "strategies.onIdle.model",
expected: "string",
actual: typeof strategies.onIdle.model,
})
}
if (
strategies.onIdle.showModelErrorToasts !== undefined &&
typeof strategies.onIdle.showModelErrorToasts !== "boolean"
) {
errors.push({
key: "strategies.onIdle.showModelErrorToasts",
expected: "boolean",
actual: typeof strategies.onIdle.showModelErrorToasts,
})
}
if (
strategies.onIdle.strictModelSelection !== undefined &&
typeof strategies.onIdle.strictModelSelection !== "boolean"
) {
errors.push({
key: "strategies.onIdle.strictModelSelection",
expected: "boolean",
actual: typeof strategies.onIdle.strictModelSelection,
})
}
if (
strategies.onIdle.protectedTools !== undefined &&
!Array.isArray(strategies.onIdle.protectedTools)
) {
errors.push({
key: "strategies.onIdle.protectedTools",
expected: "string[]",
actual: typeof strategies.onIdle.protectedTools,
})
}
}

// supersedeWrites
if (strategies.supersedeWrites) {
if (
Expand Down Expand Up @@ -459,12 +389,6 @@ const defaultConfig: PluginConfig = {
turns: 4,
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
},
onIdle: {
enabled: false,
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
showModelErrorToasts: true,
strictModelSelection: false,
},
},
}

Expand Down Expand Up @@ -587,18 +511,6 @@ function createDefaultConfig(): void {
"turns": 4,
// Additional tools to protect from pruning
"protectedTools": []
},
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
"onIdle": {
"enabled": false,
// Additional tools to protect from pruning
"protectedTools": [],
// Override model for analysis (format: "provider/model")
// "model": "anthropic/claude-haiku-4-5",
// Show toast notifications when model selection fails
"showModelErrorToasts": true,
// When true, fallback models are not permitted
"strictModelSelection": false
}
}
}
Expand Down Expand Up @@ -660,20 +572,6 @@ function mergeStrategies(
]),
],
},
onIdle: {
enabled: override.onIdle?.enabled ?? base.onIdle.enabled,
model: override.onIdle?.model ?? base.onIdle.model,
showModelErrorToasts:
override.onIdle?.showModelErrorToasts ?? base.onIdle.showModelErrorToasts,
strictModelSelection:
override.onIdle?.strictModelSelection ?? base.onIdle.strictModelSelection,
protectedTools: [
...new Set([
...base.onIdle.protectedTools,
...(override.onIdle?.protectedTools ?? []),
]),
],
},
}
}

Expand Down Expand Up @@ -728,10 +626,6 @@ function deepCloneConfig(config: PluginConfig): PluginConfig {
...config.strategies.purgeErrors,
protectedTools: [...config.strategies.purgeErrors.protectedTools],
},
onIdle: {
...config.strategies.onIdle,
protectedTools: [...config.strategies.onIdle.protectedTools],
},
},
}
}
Expand Down
31 changes: 0 additions & 31 deletions lib/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { syncToolCache } from "./state/tool-cache"
import { deduplicate, supersedeWrites, purgeErrors } from "./strategies"
import { prune, insertPruneToolContext } from "./messages"
import { checkSession } from "./state"
import { runOnIdle } from "./strategies/on-idle"

export function createChatMessageTransformHandler(
client: any,
Expand Down Expand Up @@ -35,33 +34,3 @@ export function createChatMessageTransformHandler(
}
}
}

export function createEventHandler(
client: any,
config: PluginConfig,
state: SessionState,
logger: Logger,
workingDirectory?: string,
) {
return async ({ event }: { event: any }) => {
if (state.sessionId === null || state.isSubAgent) {
return
}

if (event.type === "session.status" && event.properties.status.type === "idle") {
if (!config.strategies.onIdle.enabled) {
return
}
if (state.lastToolPrune) {
logger.info("Skipping OnIdle pruning - last tool was prune")
return
}

try {
await runOnIdle(client, state, logger, config, workingDirectory)
} catch (err: any) {
logger.error("OnIdle pruning failed", { error: err.message })
}
}
}
}
2 changes: 1 addition & 1 deletion lib/messages/inject.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { SessionState, WithParts } from "../state"
import type { Logger } from "../logger"
import type { PluginConfig } from "../config"
import { loadPrompt } from "../prompt"
import { loadPrompt } from "../prompts"
import { extractParameterKey, buildToolIdList, createSyntheticUserMessage } from "./utils"
import { getLastUserMessage } from "../shared-utils"

Expand Down
Loading