Skip to content

Commit 187b9ba

Browse files
committed
add summary buffer config
1 parent 8562ccc commit 187b9ba

File tree

13 files changed

+164
-10
lines changed

13 files changed

+164
-10
lines changed

README.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ Compress is a tool exposed to your model that replaces closed, stale conversatio
3232

3333
DCP supports two compression modes:
3434

35-
- `range` mode compresses a contiguous span of conversation into one or more reusable block summaries.
36-
- `message` mode is experimental and compresses individual raw messages independently, letting the model manage context much more surgically around closed work.
35+
- `range` mode compresses contiguous spans of conversation into one or more summaries.
36+
- `message` mode (experimental) compresses individual raw messages independently, letting the model manage context much more surgically.
3737

3838
In `range` mode, when a new compression overlaps an earlier one, the earlier summary is nested inside the new one so information is preserved through layers of compression rather than diluted away. In both modes, protected tool outputs (such as subagents and skills) and protected file patterns are kept in compression summaries, ensuring that the most important information is never lost. You can also enable `protectUserMessages` to preserve your messages verbatim during compression, though note that large prompts (e.g. copy-pasting log files in the prompt) will then never be compressed away.
3939

@@ -55,6 +55,9 @@ DCP uses its own config file, searched in order:
5555

5656
Each level overrides the previous, so project settings take priority over global. Restart OpenCode after making config changes.
5757

58+
> [!NOTE]
59+
> If you use models with smaller context windows, such as GitHub Copilot models or local models, lower `compress.minContextLimit` and `compress.maxContextLimit` in your configuration to match the available context.
60+
5861
> [!IMPORTANT]
5962
> Defaults are applied automatically. Expand this if you want to review or override settings.
6063
@@ -111,10 +114,12 @@ Each level overrides the previous, so project settings take priority over global
111114
"permission": "allow",
112115
// Show compression content in a chat notification
113116
"showCompression": false,
117+
// Let active summary tokens extend the effective maxContextLimit
118+
"summaryBuffer": true,
114119
// Soft upper threshold: above this, DCP keeps injecting strong
115120
// compression nudges (based on nudgeFrequency), so compression is
116121
// much more likely. Accepts: number or "X%" of model context window.
117-
"maxContextLimit": 150000,
122+
"maxContextLimit": 100000,
118123
// Soft lower threshold for reminder nudges: below this, turn/iteration
119124
// reminders are off (compression less likely). At/above this, reminders
120125
// are on. Accepts: number or "X%" of model context window.
@@ -201,9 +206,6 @@ To customize behavior, add a file with the same name under an overrides director
201206

202207
To reset an override, delete the matching file from your overrides directory.
203208

204-
> [!NOTE]
205-
> `compress-range` and `compress-message` prompt changes apply after plugin restart because tool descriptions are registered at startup.
206-
207209
### Protected Tools
208210

209211
By default, these tools are always protected from pruning:

dcp.schema.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,9 +145,14 @@
145145
"default": false,
146146
"description": "Show compression summaries in notifications"
147147
},
148+
"summaryBuffer": {
149+
"type": "boolean",
150+
"default": true,
151+
"description": "When enabled, active summary tokens extend the effective maxContextLimit used for context-limit nudges."
152+
},
148153
"maxContextLimit": {
149154
"description": "Soft upper threshold. Above this, DCP keeps sending strong compression nudges (based on nudgeFrequency), so the model is pushed to compress. Accepts number or \"X%\" of the model context window.",
150-
"default": 150000,
155+
"default": 100000,
151156
"oneOf": [
152157
{
153158
"type": "number"
@@ -237,7 +242,8 @@
237242
"mode": "range",
238243
"permission": "allow",
239244
"showCompression": false,
240-
"maxContextLimit": 150000,
245+
"summaryBuffer": true,
246+
"maxContextLimit": 100000,
241247
"minContextLimit": 50000,
242248
"nudgeFrequency": 5,
243249
"iterationNudgeThreshold": 15,

lib/compress/message.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ export function createCompressMessageTool(ctx: ToolContext): ReturnType<typeof t
107107
mode: "message",
108108
runId,
109109
compressMessageId: toolCtx.messageID,
110+
summaryTokens,
110111
},
111112
plan.selection,
112113
plan.anchorMessageId,

lib/compress/range.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export function createCompressRangeTool(ctx: ToolContext): ReturnType<typeof too
148148
mode: "range",
149149
runId,
150150
compressMessageId: toolCtx.messageID,
151+
summaryTokens,
151152
},
152153
preparedPlan.selection,
153154
preparedPlan.anchorMessageId,

lib/compress/state.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ export function applyCompressionState(
9292
active: true,
9393
deactivatedByUser: false,
9494
compressedTokens: 0,
95+
summaryTokens: input.summaryTokens,
9596
mode: input.mode,
9697
topic: input.topic,
9798
batchTopic: input.batchTopic,

lib/compress/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,5 @@ export interface CompressionStateInput {
102102
mode: CompressionMode
103103
runId: number
104104
compressMessageId: string
105+
summaryTokens: number
105106
}

lib/config.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ export interface CompressConfig {
1616
mode: CompressMode
1717
permission: Permission
1818
showCompression: boolean
19+
summaryBuffer: boolean
1920
maxContextLimit: number | `${number}%`
2021
minContextLimit: number | `${number}%`
2122
modelMaxLimits?: Record<string, number | `${number}%`>
@@ -111,6 +112,7 @@ export const VALID_CONFIG_KEYS = new Set([
111112
"compress.mode",
112113
"compress.permission",
113114
"compress.showCompression",
115+
"compress.summaryBuffer",
114116
"compress.maxContextLimit",
115117
"compress.minContextLimit",
116118
"compress.modelMaxLimits",
@@ -353,6 +355,17 @@ export function validateConfigTypes(config: Record<string, any>): ValidationErro
353355
})
354356
}
355357

358+
if (
359+
compress.summaryBuffer !== undefined &&
360+
typeof compress.summaryBuffer !== "boolean"
361+
) {
362+
errors.push({
363+
key: "compress.summaryBuffer",
364+
expected: "boolean",
365+
actual: typeof compress.summaryBuffer,
366+
})
367+
}
368+
356369
if (
357370
compress.nudgeFrequency !== undefined &&
358371
typeof compress.nudgeFrequency !== "number"
@@ -650,7 +663,8 @@ const defaultConfig: PluginConfig = {
650663
mode: "range",
651664
permission: "allow",
652665
showCompression: false,
653-
maxContextLimit: 150000,
666+
summaryBuffer: true,
667+
maxContextLimit: 100000,
654668
minContextLimit: 50000,
655669
nudgeFrequency: 5,
656670
iterationNudgeThreshold: 15,
@@ -812,6 +826,7 @@ function mergeCompress(
812826
mode: override.mode ?? base.mode,
813827
permission: override.permission ?? base.permission,
814828
showCompression: override.showCompression ?? base.showCompression,
829+
summaryBuffer: override.summaryBuffer ?? base.summaryBuffer,
815830
maxContextLimit: override.maxContextLimit ?? base.maxContextLimit,
816831
minContextLimit: override.minContextLimit ?? base.minContextLimit,
817832
modelMaxLimits: override.modelMaxLimits ?? base.modelMaxLimits,

lib/messages/inject/utils.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,42 @@ function resolveContextTokenLimit(
119119
return parseLimitValue(globalLimit)
120120
}
121121

122+
function getActiveSummaryTokenUsage(state: SessionState): number {
123+
let total = 0
124+
125+
for (const blockId of state.prune.messages.activeBlockIds) {
126+
const block = state.prune.messages.blocksById.get(blockId)
127+
if (!block || !block.active) {
128+
continue
129+
}
130+
131+
total += block.summaryTokens
132+
}
133+
134+
return total
135+
}
136+
122137
export function isContextOverLimits(
123138
config: PluginConfig,
124139
state: SessionState,
125140
providerId: string | undefined,
126141
modelId: string | undefined,
127142
messages: WithParts[],
128143
) {
129-
const maxContextLimit = resolveContextTokenLimit(config, state, providerId, modelId, "max")
144+
const summaryTokenExtension = config.compress.summaryBuffer
145+
? getActiveSummaryTokenUsage(state)
146+
: 0
147+
const resolvedMaxContextLimit = resolveContextTokenLimit(
148+
config,
149+
state,
150+
providerId,
151+
modelId,
152+
"max",
153+
)
154+
const maxContextLimit =
155+
resolvedMaxContextLimit === undefined
156+
? undefined
157+
: resolvedMaxContextLimit + summaryTokenExtension
130158
const minContextLimit = resolveContextTokenLimit(config, state, providerId, modelId, "min")
131159
const currentTokens = getCurrentTokenUsage(state, messages)
132160

lib/state/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export interface CompressionBlock {
3535
active: boolean
3636
deactivatedByUser: boolean
3737
compressedTokens: number
38+
summaryTokens: number
3839
mode?: CompressionMode
3940
topic: string
4041
batchTopic?: string

lib/state/utils.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type {
77
} from "./types"
88
import { isMessageCompacted, messageHasCompress } from "../shared-utils"
99
import { isIgnoredUserMessage } from "../messages/utils"
10+
import { countTokens } from "../strategies/utils"
1011

1112
interface PersistedPruneMessagesState {
1213
byMessageId?: Record<string, PrunedMessageEntry>
@@ -161,6 +162,12 @@ export function loadPruneMessagesState(
161162
Number.isFinite(block.compressedTokens)
162163
? Math.max(0, block.compressedTokens)
163164
: 0,
165+
summaryTokens:
166+
typeof block.summaryTokens === "number" && Number.isFinite(block.summaryTokens)
167+
? Math.max(0, block.summaryTokens)
168+
: typeof block.summary === "string"
169+
? countTokens(block.summary)
170+
: 0,
164171
mode: block.mode === "range" || block.mode === "message" ? block.mode : undefined,
165172
topic: typeof block.topic === "string" ? block.topic : "",
166173
batchTopic:

0 commit comments

Comments
 (0)