Skip to content

Commit a26121c

Browse files
authored
Merge pull request #214 from Opencode-DCP/dev
merge dev into master
2 parents a5731e6 + ba248c6 commit a26121c

File tree

17 files changed

+533
-2997
lines changed

17 files changed

+533
-2997
lines changed

README.md

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ DCP uses multiple tools and strategies to reduce context size:
4141

4242
**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.
4343

44-
**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).
45-
4644
Your session history is never modified—DCP replaces pruned content with placeholders before sending requests to your LLM.
4745

4846
## Impact on Prompt Caching
@@ -108,7 +106,7 @@ DCP uses its own config file:
108106
},
109107
// Prune write tool inputs when the file has been subsequently read
110108
"supersedeWrites": {
111-
"enabled": true,
109+
"enabled": false,
112110
},
113111
// Prune tool inputs for errored tools after X turns
114112
"purgeErrors": {
@@ -118,18 +116,6 @@ DCP uses its own config file:
118116
// Additional tools to protect from pruning
119117
"protectedTools": [],
120118
},
121-
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
122-
"onIdle": {
123-
"enabled": false,
124-
// Additional tools to protect from pruning
125-
"protectedTools": [],
126-
// Override model for analysis (format: "provider/model")
127-
// "model": "anthropic/claude-haiku-4-5",
128-
// Show toast notifications when model selection fails
129-
"showModelErrorToasts": true,
130-
// When true, fallback models are not permitted
131-
"strictModelSelection": false,
132-
},
133119
},
134120
}
135121
```
@@ -143,7 +129,7 @@ When enabled, turn protection prevents tool outputs from being pruned for a conf
143129
### Protected Tools
144130

145131
By default, these tools are always protected from pruning across all strategies:
146-
`task`, `todowrite`, `todoread`, `discard`, `extract`, `batch`
132+
`task`, `todowrite`, `todoread`, `discard`, `extract`, `batch`, `write`, `edit`
147133

148134
The `protectedTools` arrays in each section add to this default list.
149135

index.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { Plugin } from "@opencode-ai/plugin"
22
import { getConfig } from "./lib/config"
33
import { Logger } from "./lib/logger"
4-
import { loadPrompt } from "./lib/prompt"
4+
import { loadPrompt } from "./lib/prompts"
55
import { createSessionState } from "./lib/state"
66
import { createDiscardTool, createExtractTool } from "./lib/strategies"
7-
import { createChatMessageTransformHandler, createEventHandler } from "./lib/hooks"
7+
import { createChatMessageTransformHandler } from "./lib/hooks"
88

99
const plugin: Plugin = (async (ctx) => {
1010
const config = getConfig(ctx)
@@ -91,7 +91,6 @@ const plugin: Plugin = (async (ctx) => {
9191
)
9292
}
9393
},
94-
event: createEventHandler(ctx.client, config, state, logger, ctx.directory),
9594
}
9695
}) satisfies Plugin
9796

lib/config.ts

Lines changed: 12 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,6 @@ export interface Deduplication {
99
protectedTools: string[]
1010
}
1111

12-
export interface OnIdle {
13-
enabled: boolean
14-
model?: string
15-
showModelErrorToasts?: boolean
16-
strictModelSelection?: boolean
17-
protectedTools: string[]
18-
}
19-
2012
export interface DiscardTool {
2113
enabled: boolean
2214
}
@@ -63,11 +55,19 @@ export interface PluginConfig {
6355
deduplication: Deduplication
6456
supersedeWrites: SupersedeWrites
6557
purgeErrors: PurgeErrors
66-
onIdle: OnIdle
6758
}
6859
}
6960

70-
const DEFAULT_PROTECTED_TOOLS = ["task", "todowrite", "todoread", "discard", "extract", "batch"]
61+
const DEFAULT_PROTECTED_TOOLS = [
62+
"task",
63+
"todowrite",
64+
"todoread",
65+
"discard",
66+
"extract",
67+
"batch",
68+
"write",
69+
"edit",
70+
]
7171

7272
// Valid config keys for validation against user config
7373
export const VALID_CONFIG_KEYS = new Set([
@@ -102,13 +102,6 @@ export const VALID_CONFIG_KEYS = new Set([
102102
"strategies.purgeErrors.enabled",
103103
"strategies.purgeErrors.turns",
104104
"strategies.purgeErrors.protectedTools",
105-
// strategies.onIdle
106-
"strategies.onIdle",
107-
"strategies.onIdle.enabled",
108-
"strategies.onIdle.model",
109-
"strategies.onIdle.showModelErrorToasts",
110-
"strategies.onIdle.strictModelSelection",
111-
"strategies.onIdle.protectedTools",
112105
])
113106

114107
// Extract all key paths from a config object for validation
@@ -272,60 +265,6 @@ function validateConfigTypes(config: Record<string, any>): ValidationError[] {
272265
})
273266
}
274267

275-
// onIdle
276-
if (strategies.onIdle) {
277-
if (
278-
strategies.onIdle.enabled !== undefined &&
279-
typeof strategies.onIdle.enabled !== "boolean"
280-
) {
281-
errors.push({
282-
key: "strategies.onIdle.enabled",
283-
expected: "boolean",
284-
actual: typeof strategies.onIdle.enabled,
285-
})
286-
}
287-
if (
288-
strategies.onIdle.model !== undefined &&
289-
typeof strategies.onIdle.model !== "string"
290-
) {
291-
errors.push({
292-
key: "strategies.onIdle.model",
293-
expected: "string",
294-
actual: typeof strategies.onIdle.model,
295-
})
296-
}
297-
if (
298-
strategies.onIdle.showModelErrorToasts !== undefined &&
299-
typeof strategies.onIdle.showModelErrorToasts !== "boolean"
300-
) {
301-
errors.push({
302-
key: "strategies.onIdle.showModelErrorToasts",
303-
expected: "boolean",
304-
actual: typeof strategies.onIdle.showModelErrorToasts,
305-
})
306-
}
307-
if (
308-
strategies.onIdle.strictModelSelection !== undefined &&
309-
typeof strategies.onIdle.strictModelSelection !== "boolean"
310-
) {
311-
errors.push({
312-
key: "strategies.onIdle.strictModelSelection",
313-
expected: "boolean",
314-
actual: typeof strategies.onIdle.strictModelSelection,
315-
})
316-
}
317-
if (
318-
strategies.onIdle.protectedTools !== undefined &&
319-
!Array.isArray(strategies.onIdle.protectedTools)
320-
) {
321-
errors.push({
322-
key: "strategies.onIdle.protectedTools",
323-
expected: "string[]",
324-
actual: typeof strategies.onIdle.protectedTools,
325-
})
326-
}
327-
}
328-
329268
// supersedeWrites
330269
if (strategies.supersedeWrites) {
331270
if (
@@ -452,19 +391,13 @@ const defaultConfig: PluginConfig = {
452391
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
453392
},
454393
supersedeWrites: {
455-
enabled: true,
394+
enabled: false,
456395
},
457396
purgeErrors: {
458397
enabled: true,
459398
turns: 4,
460399
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
461400
},
462-
onIdle: {
463-
enabled: false,
464-
protectedTools: [...DEFAULT_PROTECTED_TOOLS],
465-
showModelErrorToasts: true,
466-
strictModelSelection: false,
467-
},
468401
},
469402
}
470403

@@ -578,7 +511,7 @@ function createDefaultConfig(): void {
578511
},
579512
// Prune write tool inputs when the file has been subsequently read
580513
"supersedeWrites": {
581-
"enabled": true
514+
"enabled": false
582515
},
583516
// Prune tool inputs for errored tools after X turns
584517
"purgeErrors": {
@@ -587,18 +520,6 @@ function createDefaultConfig(): void {
587520
"turns": 4,
588521
// Additional tools to protect from pruning
589522
"protectedTools": []
590-
},
591-
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
592-
"onIdle": {
593-
"enabled": false,
594-
// Additional tools to protect from pruning
595-
"protectedTools": [],
596-
// Override model for analysis (format: "provider/model")
597-
// "model": "anthropic/claude-haiku-4-5",
598-
// Show toast notifications when model selection fails
599-
"showModelErrorToasts": true,
600-
// When true, fallback models are not permitted
601-
"strictModelSelection": false
602523
}
603524
}
604525
}
@@ -660,20 +581,6 @@ function mergeStrategies(
660581
]),
661582
],
662583
},
663-
onIdle: {
664-
enabled: override.onIdle?.enabled ?? base.onIdle.enabled,
665-
model: override.onIdle?.model ?? base.onIdle.model,
666-
showModelErrorToasts:
667-
override.onIdle?.showModelErrorToasts ?? base.onIdle.showModelErrorToasts,
668-
strictModelSelection:
669-
override.onIdle?.strictModelSelection ?? base.onIdle.strictModelSelection,
670-
protectedTools: [
671-
...new Set([
672-
...base.onIdle.protectedTools,
673-
...(override.onIdle?.protectedTools ?? []),
674-
]),
675-
],
676-
},
677584
}
678585
}
679586

@@ -728,10 +635,6 @@ function deepCloneConfig(config: PluginConfig): PluginConfig {
728635
...config.strategies.purgeErrors,
729636
protectedTools: [...config.strategies.purgeErrors.protectedTools],
730637
},
731-
onIdle: {
732-
...config.strategies.onIdle,
733-
protectedTools: [...config.strategies.onIdle.protectedTools],
734-
},
735638
},
736639
}
737640
}

lib/hooks.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { syncToolCache } from "./state/tool-cache"
55
import { deduplicate, supersedeWrites, purgeErrors } from "./strategies"
66
import { prune, insertPruneToolContext } from "./messages"
77
import { checkSession } from "./state"
8-
import { runOnIdle } from "./strategies/on-idle"
98

109
export function createChatMessageTransformHandler(
1110
client: any,
@@ -35,33 +34,3 @@ export function createChatMessageTransformHandler(
3534
}
3635
}
3736
}
38-
39-
export function createEventHandler(
40-
client: any,
41-
config: PluginConfig,
42-
state: SessionState,
43-
logger: Logger,
44-
workingDirectory?: string,
45-
) {
46-
return async ({ event }: { event: any }) => {
47-
if (state.sessionId === null || state.isSubAgent) {
48-
return
49-
}
50-
51-
if (event.type === "session.status" && event.properties.status.type === "idle") {
52-
if (!config.strategies.onIdle.enabled) {
53-
return
54-
}
55-
if (state.lastToolPrune) {
56-
logger.info("Skipping OnIdle pruning - last tool was prune")
57-
return
58-
}
59-
60-
try {
61-
await runOnIdle(client, state, logger, config, workingDirectory)
62-
} catch (err: any) {
63-
logger.error("OnIdle pruning failed", { error: err.message })
64-
}
65-
}
66-
}
67-
}

lib/messages/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
export { prune, insertPruneToolContext } from "./prune"
1+
export { prune } from "./prune"
2+
export { insertPruneToolContext } from "./inject"

0 commit comments

Comments
 (0)