Skip to content

Commit 53190b6

Browse files
authored
Merge pull request #152 from Opencode-DCP/prune-write-and-edit
Prune write and edit
2 parents b810411 + 729854a commit 53190b6

File tree

6 files changed

+55
-7
lines changed

6 files changed

+55
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ DCP uses its own config file:
102102
### Protected Tools
103103

104104
By default, these tools are always protected from pruning across all strategies:
105-
`task`, `todowrite`, `todoread`, `prune`, `batch`, `write`, `edit`
105+
`task`, `todowrite`, `todoread`, `prune`, `batch`
106106

107107
The `protectedTools` arrays in each strategy add to this default list.
108108

lib/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export interface PluginConfig {
4040
}
4141
}
4242

43-
const DEFAULT_PROTECTED_TOOLS = ['task', 'todowrite', 'todoread', 'prune', 'batch', 'write', 'edit']
43+
const DEFAULT_PROTECTED_TOOLS = ['task', 'todowrite', 'todoread', 'prune', 'batch']
4444

4545
// Valid config keys for validation against user config
4646
export const VALID_CONFIG_KEYS = new Set([

lib/messages/prune.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { extractParameterKey, buildToolIdList } from "./utils"
66
import { getLastUserMessage } from "../shared-utils"
77
import { UserMessage } from "@opencode-ai/sdk"
88

9+
const PRUNED_TOOL_INPUT_REPLACEMENT = '[Input removed to save context]'
910
const PRUNED_TOOL_OUTPUT_REPLACEMENT = '[Output removed to save context - information superseded or no longer needed]'
1011
const NUDGE_STRING = loadPrompt("nudge")
1112

@@ -39,7 +40,7 @@ const buildPrunableToolsList = (
3940
return ""
4041
}
4142

42-
return `<prunable-tools>\nThe following tools have been invoked and are available for pruning. This list does not mandate immediate action. Consider your current goals and the resources you need before discarding valuable tool outputs. Keep the context free of noise.\n${lines.join('\n')}\n</prunable-tools>`
43+
return `<prunable-tools>\nThe following tools have been invoked and are available for pruning. This list does not mandate immediate action. Consider your current goals and the resources you need before discarding valuable tool inputs or outputs. Keep the context free of noise.\n${lines.join('\n')}\n</prunable-tools>`
4344
}
4445

4546
export const insertPruneToolContext = (
@@ -101,7 +102,7 @@ export const prune = (
101102
messages: WithParts[]
102103
): void => {
103104
pruneToolOutputs(state, logger, messages)
104-
// more prune methods coming here
105+
pruneToolInputs(state, logger, messages)
105106
}
106107

107108
const pruneToolOutputs = (
@@ -117,9 +118,37 @@ const pruneToolOutputs = (
117118
if (!state.prune.toolIds.includes(part.callID)) {
118119
continue
119120
}
121+
// Skip write and edit tools - their inputs are pruned instead
122+
if (part.tool === 'write' || part.tool === 'edit') {
123+
continue
124+
}
120125
if (part.state.status === 'completed') {
121126
part.state.output = PRUNED_TOOL_OUTPUT_REPLACEMENT
122127
}
123128
}
124129
}
125130
}
131+
132+
const pruneToolInputs = (
133+
state: SessionState,
134+
logger: Logger,
135+
messages: WithParts[]
136+
): void => {
137+
for (const msg of messages) {
138+
for (const part of msg.parts) {
139+
if (part.type !== 'tool') {
140+
continue
141+
}
142+
if (!state.prune.toolIds.includes(part.callID)) {
143+
continue
144+
}
145+
// Only prune inputs for write and edit tools
146+
if (part.tool !== 'write' && part.tool !== 'edit') {
147+
continue
148+
}
149+
if (part.state.input?.content !== undefined) {
150+
part.state.input.content = PRUNED_TOOL_INPUT_REPLACEMENT
151+
}
152+
}
153+
}
154+
}

lib/prompts/synthetic.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ You WILL use the `prune` tool when ANY of these are true:
1717
- You are about to start a new phase of work
1818
- You have distilled enough information in your messages to prune related tools
1919
- Context contains tools output that are unhelpful, noise, or made obsolete by newer outputs
20+
- Write or edit operations are complete (pruning removes the large input content)
2021

2122
You MUST NOT prune when:
2223
- The tool output will be needed for upcoming implementation work

lib/prompts/tool.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
Prunes tool outputs from context to manage conversation size and reduce noise.
1+
Prunes tool outputs from context to manage conversation size and reduce noise. For `write` and `edit` tools, the input content is pruned instead of the output.
22

33
## IMPORTANT: The Prunable List
44
A `<prunable-tools>` list is injected into user messages showing available tool outputs you can prune. Each line has the format `ID: tool, parameter` (e.g., `20: read, /path/to/file.ts`). You MUST only use numeric IDs that appear in this list to select which tools to prune.
55

6+
**Note:** For `write` and `edit` tools, pruning removes the input content (the code being written/edited) while preserving the output confirmation. This is useful after completing a file modification when you no longer need the raw content in context.
7+
68
## CRITICAL: When and How to Prune
79

810
You must use this tool in three specific scenarios. The rules for distillation (summarizing findings) differ for each. **You must specify the reason as the first element of the `ids` array** to indicate which scenario applies.
@@ -62,3 +64,9 @@ The tests passed. The feature is verified.
6264
Assistant: [Reads 'auth.ts' to understand the login flow]
6365
I've understood the auth flow. I'll need to modify this file to add the new validation, so I'm keeping this read in context rather than distilling and pruning.
6466
</example_keep>
67+
68+
<example_edit_completion>
69+
Assistant: [Edits 'auth.ts' to add validation]
70+
The edit was successful. I no longer need the raw edit content in context.
71+
[Uses prune with ids: ["completion", "15"]]
72+
</example_edit_completion>

lib/strategies/utils.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,23 @@ export const calculateTokensSaved = (
5050
if (part.type !== 'tool' || !pruneToolIds.includes(part.callID)) {
5151
continue
5252
}
53+
// For write and edit tools, count input content as that is all we prune for these tools
54+
// (input is present in both completed and error states)
55+
if (part.tool === "write" || part.tool === "edit") {
56+
const inputContent = part.state.input?.content
57+
const content = typeof inputContent === 'string'
58+
? inputContent
59+
: JSON.stringify(inputContent ?? '')
60+
contents.push(content)
61+
continue
62+
}
63+
// For other tools, count output or error based on status
5364
if (part.state.status === "completed") {
5465
const content = typeof part.state.output === 'string'
5566
? part.state.output
5667
: JSON.stringify(part.state.output)
5768
contents.push(content)
58-
}
59-
if (part.state.status === "error") {
69+
} else if (part.state.status === "error") {
6070
const content = typeof part.state.error === 'string'
6171
? part.state.error
6272
: JSON.stringify(part.state.error)

0 commit comments

Comments
 (0)