Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions lib/strategies/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -192,7 +185,7 @@ export function createExtractTool(ctx: PruneToolContext): ReturnType<typeof tool
ctx,
toolCtx,
args.ids,
"consolidation" as PruneReason,
"extraction" as PruneReason,
"Extract",
args.distillation,
)
Expand Down
77 changes: 31 additions & 46 deletions lib/ui/notification.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,32 @@
import type { Logger } from "../logger"
import type { SessionState } from "../state"
import { formatPrunedItemsList, formatTokenCount } from "./utils"
import {
formatExtracted,
formatPrunedItemsList,
formatStatsHeader,
formatTokenCount,
} from "./utils"
import { ToolParameterEntry } from "../state"
import { PluginConfig } from "../config"

export type PruneReason = "completion" | "noise" | "consolidation"
export type PruneReason = "completion" | "noise" | "extraction"
export const PRUNE_REASON_LABELS: Record<PruneReason, string> = {
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(
Expand All @@ -30,6 +35,7 @@ function buildDetailedMessage(
pruneToolIds: string[],
toolMetadata: Map<string, ToolParameterEntry>,
workingDirectory?: string,
distillation?: string[],
): string {
let message = formatStatsHeader(state.stats.totalPruneTokens, state.stats.pruneTokenCounter)

Expand All @@ -42,7 +48,7 @@ function buildDetailedMessage(
message += "\n" + itemLines.join("\n")
}

return message.trim()
return (message + formatExtracted(distillation)).trim()
}

export async function sendUnifiedNotification(
Expand All @@ -56,6 +62,7 @@ export async function sendUnifiedNotification(
reason: PruneReason | undefined,
params: any,
workingDirectory: string,
distillation?: string[],
): Promise<boolean> {
const hasPruned = pruneToolIds.length > 0
if (!hasPruned) {
Expand All @@ -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, any>): 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<string, any>,
params: any,
): Promise<boolean> {
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
}
Expand Down
16 changes: 16 additions & 0 deletions lib/ui/utils.ts
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down