From 801fe14cfd98b05fa90946ead7f703b9e730c3b0 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Tue, 16 Dec 2025 22:08:31 -0500 Subject: [PATCH 1/7] Remove showUpdateToasts config option --- README.md | 2 -- lib/config.ts | 11 ----------- 2 files changed, 13 deletions(-) diff --git a/README.md b/README.md index 9e2efec5..a8e76615 100644 --- a/README.md +++ b/README.md @@ -58,8 +58,6 @@ DCP uses its own config file: "enabled": true, // Enable debug logging to ~/.config/opencode/logs/dcp/ "debug": false, - // Show toast notifications when a new version is available - "showUpdateToasts": true, // Summary display: "off", "minimal", or "detailed" "pruningSummary": "detailed", // Strategies for pruning tokens from chat history diff --git a/lib/config.ts b/lib/config.ts index 594a0467..31949431 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -31,7 +31,6 @@ export interface PruneTool { export interface PluginConfig { enabled: boolean debug: boolean - showUpdateToasts?: boolean pruningSummary: "off" | "minimal" | "detailed" strategies: { deduplication: Deduplication @@ -47,7 +46,6 @@ export const VALID_CONFIG_KEYS = new Set([ // Top-level keys 'enabled', 'debug', - 'showUpdateToasts', 'pruningSummary', 'strategies', // strategies.deduplication @@ -106,9 +104,6 @@ function validateConfigTypes(config: Record): ValidationError[] { if (config.debug !== undefined && typeof config.debug !== 'boolean') { errors.push({ key: 'debug', expected: 'boolean', actual: typeof config.debug }) } - if (config.showUpdateToasts !== undefined && typeof config.showUpdateToasts !== 'boolean') { - errors.push({ key: 'showUpdateToasts', expected: 'boolean', actual: typeof config.showUpdateToasts }) - } if (config.pruningSummary !== undefined) { const validValues = ['off', 'minimal', 'detailed'] if (!validValues.includes(config.pruningSummary)) { @@ -217,7 +212,6 @@ function showConfigValidationWarnings( const defaultConfig: PluginConfig = { enabled: true, debug: false, - showUpdateToasts: true, pruningSummary: 'detailed', strategies: { deduplication: { @@ -310,8 +304,6 @@ function createDefaultConfig(): void { "enabled": true, // Enable debug logging to ~/.config/opencode/logs/dcp/ "debug": false, - // Show toast notifications when a new version is available - "showUpdateToasts": true, // Summary display: "off", "minimal", or "detailed" "pruningSummary": "detailed", // Strategies for pruning tokens from chat history @@ -468,7 +460,6 @@ export function getConfig(ctx: PluginInput): PluginConfig { config = { enabled: result.data.enabled ?? config.enabled, debug: result.data.debug ?? config.debug, - showUpdateToasts: result.data.showUpdateToasts ?? config.showUpdateToasts, pruningSummary: result.data.pruningSummary ?? config.pruningSummary, strategies: mergeStrategies(config.strategies, result.data.strategies as any) } @@ -500,7 +491,6 @@ export function getConfig(ctx: PluginInput): PluginConfig { config = { enabled: result.data.enabled ?? config.enabled, debug: result.data.debug ?? config.debug, - showUpdateToasts: result.data.showUpdateToasts ?? config.showUpdateToasts, pruningSummary: result.data.pruningSummary ?? config.pruningSummary, strategies: mergeStrategies(config.strategies, result.data.strategies as any) } @@ -529,7 +519,6 @@ export function getConfig(ctx: PluginInput): PluginConfig { config = { enabled: result.data.enabled ?? config.enabled, debug: result.data.debug ?? config.debug, - showUpdateToasts: result.data.showUpdateToasts ?? config.showUpdateToasts, pruningSummary: result.data.pruningSummary ?? config.pruningSummary, strategies: mergeStrategies(config.strategies, result.data.strategies as any) } From 87470c638198b715ea2a37184797db2fb87de5a6 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 17 Dec 2025 00:35:31 -0500 Subject: [PATCH 2/7] Restore showUpdateToasts config for backwards compat --- lib/config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/config.ts b/lib/config.ts index 31949431..1887442f 100644 --- a/lib/config.ts +++ b/lib/config.ts @@ -46,6 +46,7 @@ export const VALID_CONFIG_KEYS = new Set([ // Top-level keys 'enabled', 'debug', + 'showUpdateToasts', // Deprecated but kept for backwards compatibility 'pruningSummary', 'strategies', // strategies.deduplication From 55475b4fec45799703c00e7886fbfe07cb2ce572 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 17 Dec 2025 02:15:24 -0500 Subject: [PATCH 3/7] Detect compaction from message data instead of event - Scan messages for assistant messages with info.summary === true - Run compaction detection on every checkSession call, not just init - Remove session.compacted event handler (no longer needed) - Remove lastCompacted from persistence (derived from messages now) - Remove unused checkForCompaction function This ensures compaction is detected even when: - Plugin was disabled during compaction - Session was loaded before plugin was installed - Any other case where the event wasn't captured --- lib/hooks.ts | 5 ----- lib/shared-utils.ts | 9 --------- lib/state/persistence.ts | 4 +--- lib/state/state.ts | 26 +++++++++++++++++++++++--- lib/strategies/prune-tool.ts | 4 ++-- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/lib/hooks.ts b/lib/hooks.ts index f24d52c3..72fda69e 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -48,11 +48,6 @@ export function createEventHandler( return } - if (event.type === "session.compacted") { - logger.info("Session compaction detected - updating state") - state.lastCompaction = Date.now() - } - if (event.type === "session.status" && event.properties.status.type === "idle") { if (!config.strategies.onIdle.enabled) { return diff --git a/lib/shared-utils.ts b/lib/shared-utils.ts index 9cb60a18..fe6fbd60 100644 --- a/lib/shared-utils.ts +++ b/lib/shared-utils.ts @@ -1,4 +1,3 @@ -import { Logger } from "./logger" import { SessionState, WithParts } from "./state" export const isMessageCompacted = ( @@ -20,12 +19,4 @@ export const getLastUserMessage = ( return null } -export const checkForCompaction = ( - state: SessionState, - messages: WithParts[], - logger: Logger -): void => { - for (const msg of messages) { - } -} diff --git a/lib/state/persistence.ts b/lib/state/persistence.ts index 89d67729..eb975511 100644 --- a/lib/state/persistence.ts +++ b/lib/state/persistence.ts @@ -16,7 +16,6 @@ export interface PersistedSessionState { prune: Prune stats: SessionStats; lastUpdated: string; - lastCompacted: number } const STORAGE_DIR = join( @@ -55,8 +54,7 @@ export async function saveSessionState( sessionName: sessionName, prune: sessionState.prune, stats: sessionState.stats, - lastUpdated: new Date().toISOString(), - lastCompacted: sessionState.lastCompaction + lastUpdated: new Date().toISOString() }; const filePath = getSessionFilePath(sessionState.sessionId); diff --git a/lib/state/state.ts b/lib/state/state.ts index 035f81bc..4a2af7a4 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -21,11 +21,18 @@ export const checkSession = async ( if (state.sessionId === null || state.sessionId !== lastSessionId) { logger.info(`Session changed: ${state.sessionId} -> ${lastSessionId}`) try { - await ensureSessionInitialized(client, state, lastSessionId, logger) + await ensureSessionInitialized(client, state, lastSessionId, logger, messages) } catch (err: any) { logger.error("Failed to initialize session state", { error: err.message }) } } + + // Always check for compaction on every message check + const lastCompactionTimestamp = findLastCompactionTimestamp(messages) + if (lastCompactionTimestamp > state.lastCompaction) { + state.lastCompaction = lastCompactionTimestamp + logger.info("Detected compaction from messages", { timestamp: lastCompactionTimestamp }) + } } export function createSessionState(): SessionState { @@ -66,7 +73,8 @@ export async function ensureSessionInitialized( client: any, state: SessionState, sessionId: string, - logger: Logger + logger: Logger, + messages: WithParts[] ): Promise { if (state.sessionId === sessionId) { return; @@ -97,5 +105,17 @@ export async function ensureSessionInitialized( pruneTokenCounter: persisted.stats?.pruneTokenCounter || 0, totalPruneTokens: persisted.stats?.totalPruneTokens || 0, } - state.lastCompaction = persisted.lastCompacted || 0 +} + +function findLastCompactionTimestamp(messages: WithParts[]): number { + let lastTimestamp = 0 + for (const msg of messages) { + if (msg.info.role === "assistant" && msg.info.summary === true) { + const created = msg.info.time.created + if (created > lastTimestamp) { + lastTimestamp = created + } + } + } + return lastTimestamp } diff --git a/lib/strategies/prune-tool.ts b/lib/strategies/prune-tool.ts index e361325b..6cf9052c 100644 --- a/lib/strategies/prune-tool.ts +++ b/lib/strategies/prune-tool.ts @@ -66,14 +66,14 @@ export function createPruneTool( return "No numeric IDs provided. Format: [reason, id1, id2, ...] where reason is 'completion', 'noise', or 'consolidation'." } - await ensureSessionInitialized(ctx.client, state, sessionId, logger) - // Fetch messages to calculate tokens and find current agent const messagesResponse = await client.session.messages({ path: { id: sessionId } }) const messages: WithParts[] = messagesResponse.data || messagesResponse + await ensureSessionInitialized(ctx.client, state, sessionId, logger, messages) + const currentParams = getCurrentParams(messages, logger) const toolIdList: string[] = buildToolIdList(state, messages, logger) From a41ff814820936a81123a71c7345b575cec48451 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 17 Dec 2025 02:55:07 -0500 Subject: [PATCH 4/7] Clear tool caches on compaction and add stale entry warning - Clear toolParameters and prune.toolIds when compaction is detected - Add warning log when tool is in cache but not in toolIdList - Stale tool data from pre-compaction no longer lingers in state --- lib/messages/prune.ts | 4 ++++ lib/state/state.ts | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/messages/prune.ts b/lib/messages/prune.ts index 918056e4..33d9a7a4 100644 --- a/lib/messages/prune.ts +++ b/lib/messages/prune.ts @@ -27,6 +27,10 @@ const buildPrunableToolsList = ( return } const numericId = toolIdList.indexOf(toolCallId) + if (numericId === -1) { + logger.warn(`Tool in cache but not in toolIdList - possible stale entry`, { toolCallId, tool: toolParameterEntry.tool }) + return + } const paramKey = extractParameterKey(toolParameterEntry.tool, toolParameterEntry.parameters) const description = paramKey ? `${toolParameterEntry.tool}, ${paramKey}` : toolParameterEntry.tool lines.push(`${numericId}: ${description}`) diff --git a/lib/state/state.ts b/lib/state/state.ts index 4a2af7a4..b9ad05f9 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -31,7 +31,10 @@ export const checkSession = async ( const lastCompactionTimestamp = findLastCompactionTimestamp(messages) if (lastCompactionTimestamp > state.lastCompaction) { state.lastCompaction = lastCompactionTimestamp - logger.info("Detected compaction from messages", { timestamp: lastCompactionTimestamp }) + // Clear stale tool data - these tools no longer exist in context + state.toolParameters.clear() + state.prune.toolIds = [] + logger.info("Detected compaction from messages - cleared tool cache", { timestamp: lastCompactionTimestamp }) } } From 4ae8475adedeb11329458196a6cecead8e8a2dff Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 17 Dec 2025 03:45:55 -0500 Subject: [PATCH 5/7] Optimize findLastCompactionTimestamp to iterate from end --- lib/state/state.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/lib/state/state.ts b/lib/state/state.ts index b9ad05f9..3244b168 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -111,14 +111,11 @@ export async function ensureSessionInitialized( } function findLastCompactionTimestamp(messages: WithParts[]): number { - let lastTimestamp = 0 - for (const msg of messages) { + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i] if (msg.info.role === "assistant" && msg.info.summary === true) { - const created = msg.info.time.created - if (created > lastTimestamp) { - lastTimestamp = created - } + return msg.info.time.created } } - return lastTimestamp + return 0 } From 0288214a84a5df4f20770cf23f44e24801792ff4 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 17 Dec 2025 03:50:17 -0500 Subject: [PATCH 6/7] cleanup --- lib/state/state.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/state/state.ts b/lib/state/state.ts index 3244b168..6d47f1db 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -31,7 +31,6 @@ export const checkSession = async ( const lastCompactionTimestamp = findLastCompactionTimestamp(messages) if (lastCompactionTimestamp > state.lastCompaction) { state.lastCompaction = lastCompactionTimestamp - // Clear stale tool data - these tools no longer exist in context state.toolParameters.clear() state.prune.toolIds = [] logger.info("Detected compaction from messages - cleared tool cache", { timestamp: lastCompactionTimestamp }) From 9468cd804ee9e637f2dca4dbb359819be42e3d27 Mon Sep 17 00:00:00 2001 From: Daniel Smolsky Date: Wed, 17 Dec 2025 03:53:13 -0500 Subject: [PATCH 7/7] cleanup --- lib/state/state.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/state/state.ts b/lib/state/state.ts index 6d47f1db..7ad26174 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -27,7 +27,6 @@ export const checkSession = async ( } } - // Always check for compaction on every message check const lastCompactionTimestamp = findLastCompactionTimestamp(messages) if (lastCompactionTimestamp > state.lastCompaction) { state.lastCompaction = lastCompactionTimestamp