diff --git a/lib/strategies/tools.ts b/lib/strategies/tools.ts index 74ecf7e..220553d 100644 --- a/lib/strategies/tools.ts +++ b/lib/strategies/tools.ts @@ -2,11 +2,7 @@ import { tool } from "@opencode-ai/plugin" import type { SessionState, ToolParameterEntry, WithParts } from "../state" import type { PluginConfig } from "../config" import { buildToolIdList } from "../messages/utils" -import { - PruneReason, - sendUnifiedNotification, - sendDistillationNotification, -} from "../ui/notification" +import { PruneReason, sendUnifiedNotification } from "../ui/notification" import { formatPruningResultForTool } from "../ui/utils" import { ensureSessionInitialized } from "../state" import { saveSessionState } from "../state/persistence" @@ -120,12 +116,9 @@ async function executePruneOperation( reason, currentParams, workingDirectory, + distillation, ) - if (distillation && config.tools.extract.showDistillation) { - await sendDistillationNotification(client, logger, sessionId, distillation, currentParams) - } - state.stats.totalPruneTokens += state.stats.pruneTokenCounter state.stats.pruneTokenCounter = 0 state.nudgeCounter = 0 @@ -192,7 +185,7 @@ export function createExtractTool(ctx: PruneToolContext): ReturnType = { completion: "Task Complete", noise: "Noise Removal", - consolidation: "Consolidation", + extraction: "Extraction", } -function formatStatsHeader(totalTokensSaved: number, pruneTokenCounter: number): string { - const totalTokensSavedStr = `~${formatTokenCount(totalTokensSaved + pruneTokenCounter)}` - return [`▣ DCP | ${totalTokensSavedStr} saved total`].join("\n") -} - -function buildMinimalMessage(state: SessionState, reason: PruneReason | undefined): string { - const reasonSuffix = reason ? ` [${PRUNE_REASON_LABELS[reason]}]` : "" - return ( +function buildMinimalMessage( + state: SessionState, + reason: PruneReason | undefined, + distillation?: string[], +): string { + const reasonSuffix = reason ? ` — ${PRUNE_REASON_LABELS[reason]}` : "" + let message = formatStatsHeader(state.stats.totalPruneTokens, state.stats.pruneTokenCounter) + reasonSuffix - ) + + return message + formatExtracted(distillation) } function buildDetailedMessage( @@ -30,6 +35,7 @@ function buildDetailedMessage( pruneToolIds: string[], toolMetadata: Map, workingDirectory?: string, + distillation?: string[], ): string { let message = formatStatsHeader(state.stats.totalPruneTokens, state.stats.pruneTokenCounter) @@ -42,7 +48,7 @@ function buildDetailedMessage( message += "\n" + itemLines.join("\n") } - return message.trim() + return (message + formatExtracted(distillation)).trim() } export async function sendUnifiedNotification( @@ -56,6 +62,7 @@ export async function sendUnifiedNotification( reason: PruneReason | undefined, params: any, workingDirectory: string, + distillation?: string[], ): Promise { const hasPruned = pruneToolIds.length > 0 if (!hasPruned) { @@ -66,42 +73,20 @@ export async function sendUnifiedNotification( return false } + const showExtraction = config.tools.extract.showDistillation ? distillation : undefined + const message = config.pruneNotification === "minimal" - ? buildMinimalMessage(state, reason) - : buildDetailedMessage(state, reason, pruneToolIds, toolMetadata, workingDirectory) - - await sendIgnoredMessage(client, sessionId, message, params, logger) - return true -} - -function formatDistillationMessage(distillation: Record): string { - const lines: string[] = ["▣ DCP | Extracted Distillation"] - - for (const findings of Object.values(distillation)) { - lines.push(`\n───`) - if (typeof findings === "object" && findings !== null) { - lines.push(JSON.stringify(findings, null, 2)) - } else { - lines.push(String(findings)) - } - } - - return lines.join("\n") -} - -export async function sendDistillationNotification( - client: any, - logger: Logger, - sessionId: string, - distillation: Record, - params: any, -): Promise { - if (!distillation || Object.keys(distillation).length === 0) { - return false - } + ? buildMinimalMessage(state, reason, showExtraction) + : buildDetailedMessage( + state, + reason, + pruneToolIds, + toolMetadata, + workingDirectory, + showExtraction, + ) - const message = formatDistillationMessage(distillation) await sendIgnoredMessage(client, sessionId, message, params, logger) return true } diff --git a/lib/ui/utils.ts b/lib/ui/utils.ts index 11a4363..9bee8d5 100644 --- a/lib/ui/utils.ts +++ b/lib/ui/utils.ts @@ -1,6 +1,22 @@ import { ToolParameterEntry } from "../state" import { extractParameterKey } from "../messages/utils" +export function formatExtracted(distillation?: string[]): string { + if (!distillation || distillation.length === 0) { + return "" + } + let result = `\n\n▣ Extracted` + for (const finding of distillation) { + result += `\n───\n${finding}` + } + return result +} + +export function formatStatsHeader(totalTokensSaved: number, pruneTokenCounter: number): string { + const totalTokensSavedStr = `~${formatTokenCount(totalTokensSaved + pruneTokenCounter)}` + return [`▣ DCP | ${totalTokensSavedStr} saved total`].join("\n") +} + export function formatTokenCount(tokens: number): string { if (tokens >= 1000) { return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K") + " tokens" diff --git a/package-lock.json b/package-lock.json index 4943876..1ff2d1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@tarquinen/opencode-dcp", - "version": "1.1.1", + "version": "1.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "1.1.1", + "version": "1.1.2", "license": "MIT", "dependencies": { "@ai-sdk/openai-compatible": "^1.0.28", diff --git a/package.json b/package.json index 15e8d5c..8f28dfa 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://json.schemastore.org/package.json", "name": "@tarquinen/opencode-dcp", - "version": "1.1.1", + "version": "1.1.2", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js",