Skip to content

Commit de76de1

Browse files
committed
feat: add showDistillation option to display extracted findings as notification
1 parent 5cbc483 commit de76de1

File tree

4 files changed

+64
-9
lines changed

4 files changed

+64
-9
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,9 @@ DCP uses its own config file:
104104
"nudge": {
105105
"enabled": true,
106106
"frequency": 10
107-
}
107+
},
108+
// Show distillation content as an ignored message notification
109+
"showDistillation": false
108110
},
109111
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
110112
"onIdle": {

lib/config.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export interface ExtractTool {
4646
protectedTools: string[]
4747
turnProtection: PruneToolTurnProtection
4848
nudge: PruneToolNudge
49+
showDistillation: boolean
4950
}
5051

5152
export interface SupersedeWrites {
@@ -108,7 +109,8 @@ export const VALID_CONFIG_KEYS = new Set([
108109
'strategies.extractTool.turnProtection.turns',
109110
'strategies.extractTool.nudge',
110111
'strategies.extractTool.nudge.enabled',
111-
'strategies.extractTool.nudge.frequency'
112+
'strategies.extractTool.nudge.frequency',
113+
'strategies.extractTool.showDistillation'
112114
])
113115

114116
// Extract all key paths from a config object for validation
@@ -234,6 +236,9 @@ function validateConfigTypes(config: Record<string, any>): ValidationError[] {
234236
errors.push({ key: 'strategies.extractTool.nudge.frequency', expected: 'number', actual: typeof strategies.extractTool.nudge.frequency })
235237
}
236238
}
239+
if (strategies.extractTool.showDistillation !== undefined && typeof strategies.extractTool.showDistillation !== 'boolean') {
240+
errors.push({ key: 'strategies.extractTool.showDistillation', expected: 'boolean', actual: typeof strategies.extractTool.showDistillation })
241+
}
237242
}
238243

239244
// supersedeWrites
@@ -327,7 +332,8 @@ const defaultConfig: PluginConfig = {
327332
nudge: {
328333
enabled: true,
329334
frequency: 10
330-
}
335+
},
336+
showDistillation: false
331337
},
332338
onIdle: {
333339
enabled: false,
@@ -450,7 +456,9 @@ function createDefaultConfig(): void {
450456
"nudge": {
451457
"enabled": true,
452458
"frequency": 10
453-
}
459+
},
460+
// Show distillation content as an ignored message notification
461+
"showDistillation": false
454462
},
455463
// (Legacy) Run an LLM to analyze what tool calls are no longer relevant on idle
456464
"onIdle": {
@@ -555,7 +563,8 @@ function mergeStrategies(
555563
nudge: {
556564
enabled: override.extractTool?.nudge?.enabled ?? base.extractTool.nudge.enabled,
557565
frequency: override.extractTool?.nudge?.frequency ?? base.extractTool.nudge.frequency
558-
}
566+
},
567+
showDistillation: override.extractTool?.showDistillation ?? base.extractTool.showDistillation
559568
},
560569
supersedeWrites: {
561570
enabled: override.supersedeWrites?.enabled ?? base.supersedeWrites.enabled
@@ -585,7 +594,8 @@ function deepCloneConfig(config: PluginConfig): PluginConfig {
585594
...config.strategies.extractTool,
586595
protectedTools: [...config.strategies.extractTool.protectedTools],
587596
turnProtection: { ...config.strategies.extractTool.turnProtection },
588-
nudge: { ...config.strategies.extractTool.nudge }
597+
nudge: { ...config.strategies.extractTool.nudge },
598+
showDistillation: config.strategies.extractTool.showDistillation
589599
},
590600
supersedeWrites: {
591601
...config.strategies.supersedeWrites

lib/strategies/tools.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { tool } from "@opencode-ai/plugin"
22
import type { SessionState, ToolParameterEntry, WithParts } from "../state"
33
import type { PluginConfig } from "../config"
44
import { buildToolIdList } from "../messages/utils"
5-
import { PruneReason, sendUnifiedNotification } from "../ui/notification"
5+
import { PruneReason, sendUnifiedNotification, sendDistillationNotification } from "../ui/notification"
66
import { formatPruningResultForTool } from "../ui/utils"
77
import { ensureSessionInitialized } from "../state"
88
import { saveSessionState } from "../state/persistence"
@@ -27,7 +27,8 @@ async function executePruneOperation(
2727
toolCtx: { sessionID: string },
2828
ids: string[],
2929
reason: PruneReason,
30-
toolName: string
30+
toolName: string,
31+
distillation?: Record<string, any>
3132
): Promise<string> {
3233
const { client, state, logger, config, workingDirectory } = ctx
3334
const sessionId = toolCtx.sessionID
@@ -113,6 +114,16 @@ async function executePruneOperation(
113114
workingDirectory
114115
)
115116

117+
if (distillation && config.strategies.extractTool.showDistillation) {
118+
await sendDistillationNotification(
119+
client,
120+
logger,
121+
sessionId,
122+
distillation,
123+
currentParams
124+
)
125+
}
126+
116127
state.stats.totalPruneTokens += state.stats.pruneTokenCounter
117128
state.stats.pruneTokenCounter = 0
118129
state.nudgeCounter = 0
@@ -191,7 +202,8 @@ export function createExtractTool(
191202
toolCtx,
192203
args.ids,
193204
"consolidation" as PruneReason,
194-
"Extract"
205+
"Extract",
206+
args.distillation
195207
)
196208
},
197209
})

lib/ui/notification.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,37 @@ export async function sendUnifiedNotification(
8282
return true
8383
}
8484

85+
function formatDistillationMessage(distillation: Record<string, any>): string {
86+
const lines: string[] = ['▣ DCP | Extracted Distillation']
87+
88+
for (const [id, findings] of Object.entries(distillation)) {
89+
lines.push(`\n─── ID ${id} ───`)
90+
if (typeof findings === 'object' && findings !== null) {
91+
lines.push(JSON.stringify(findings, null, 2))
92+
} else {
93+
lines.push(String(findings))
94+
}
95+
}
96+
97+
return lines.join('\n')
98+
}
99+
100+
export async function sendDistillationNotification(
101+
client: any,
102+
logger: Logger,
103+
sessionId: string,
104+
distillation: Record<string, any>,
105+
params: any
106+
): Promise<boolean> {
107+
if (!distillation || Object.keys(distillation).length === 0) {
108+
return false
109+
}
110+
111+
const message = formatDistillationMessage(distillation)
112+
await sendIgnoredMessage(client, sessionId, message, params, logger)
113+
return true
114+
}
115+
85116
export async function sendIgnoredMessage(
86117
client: any,
87118
sessionID: string,

0 commit comments

Comments
 (0)