diff --git a/index.ts b/index.ts index 13df1b08..2372d2c2 100644 --- a/index.ts +++ b/index.ts @@ -37,14 +37,10 @@ const plugin: Plugin = (async (ctx) => { "Summarize what was done in this conversation", ] if (internalAgentSignatures.some((sig) => systemText.includes(sig))) { - logger.info("Skipping DCP injection for internal agent") - state.isInternalAgent = true + logger.info("Skipping DCP system prompt injection for internal agent") return } - // Reset flag for normal sessions - state.isInternalAgent = false - const discardEnabled = config.tools.discard.enabled const extractEnabled = config.tools.extract.enabled @@ -68,6 +64,21 @@ const plugin: Plugin = (async (ctx) => { logger, config, ), + "chat.message": async ( + input: { + sessionID: string + agent?: string + model?: { providerID: string; modelID: string } + messageID?: string + variant?: string + }, + _output: any, + ) => { + // Cache variant from real user messages (not synthetic) + // This avoids scanning all messages to find variant + state.variant = input.variant + logger.debug("Cached variant from chat.message hook", { variant: input.variant }) + }, tool: { ...(config.tools.discard.enabled && { discard: createDiscardTool({ diff --git a/lib/hooks.ts b/lib/hooks.ts index 231357df..e70c892a 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -15,7 +15,7 @@ export function createChatMessageTransformHandler( return async (input: {}, output: { messages: WithParts[] }) => { await checkSession(client, state, logger, output.messages) - if (state.isSubAgent || state.isInternalAgent) { + if (state.isSubAgent) { return } diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index debfd83c..11153b7e 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -1,6 +1,7 @@ import type { SessionState, WithParts } from "../state" import type { Logger } from "../logger" import type { PluginConfig } from "../config" +import type { UserMessage } from "@opencode-ai/sdk/v2" import { loadPrompt } from "../prompts" import { extractParameterKey, buildToolIdList, createSyntheticUserMessage } from "./utils" import { getLastUserMessage } from "../shared-utils" @@ -125,5 +126,6 @@ export const insertPruneToolContext = ( if (!lastUserMessage) { return } - messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent)) + const variant = state.variant ?? (lastUserMessage.info as UserMessage).variant + messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent, variant)) } diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 756c1de1..0338d866 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -1,12 +1,16 @@ import { Logger } from "../logger" import { isMessageCompacted } from "../shared-utils" import type { SessionState, WithParts } from "../state" -import type { UserMessage } from "@opencode-ai/sdk" +import type { UserMessage } from "@opencode-ai/sdk/v2" const SYNTHETIC_MESSAGE_ID = "msg_01234567890123456789012345" const SYNTHETIC_PART_ID = "prt_01234567890123456789012345" -export const createSyntheticUserMessage = (baseMessage: WithParts, content: string): WithParts => { +export const createSyntheticUserMessage = ( + baseMessage: WithParts, + content: string, + variant?: string, +): WithParts => { const userInfo = baseMessage.info as UserMessage return { info: { @@ -19,6 +23,7 @@ export const createSyntheticUserMessage = (baseMessage: WithParts, content: stri providerID: userInfo.model.providerID, modelID: userInfo.model.modelID, }, + ...(variant !== undefined && { variant }), }, parts: [ { diff --git a/lib/state/state.ts b/lib/state/state.ts index 05ebc0cb..e68ecf89 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -43,7 +43,6 @@ export function createSessionState(): SessionState { return { sessionId: null, isSubAgent: false, - isInternalAgent: false, prune: { toolIds: [], }, @@ -56,13 +55,13 @@ export function createSessionState(): SessionState { lastToolPrune: false, lastCompaction: 0, currentTurn: 0, + variant: undefined, } } export function resetSessionState(state: SessionState): void { state.sessionId = null state.isSubAgent = false - state.isInternalAgent = false state.prune = { toolIds: [], } @@ -75,6 +74,7 @@ export function resetSessionState(state: SessionState): void { state.lastToolPrune = false state.lastCompaction = 0 state.currentTurn = 0 + state.variant = undefined } export async function ensureSessionInitialized( diff --git a/lib/state/types.ts b/lib/state/types.ts index f9dfd7af..1e41170d 100644 --- a/lib/state/types.ts +++ b/lib/state/types.ts @@ -1,4 +1,4 @@ -import { Message, Part } from "@opencode-ai/sdk" +import { Message, Part } from "@opencode-ai/sdk/v2" export interface WithParts { info: Message @@ -27,7 +27,6 @@ export interface Prune { export interface SessionState { sessionId: string | null isSubAgent: boolean - isInternalAgent: boolean prune: Prune stats: SessionStats toolParameters: Map @@ -35,4 +34,5 @@ export interface SessionState { lastToolPrune: boolean lastCompaction: number currentTurn: number + variant: string | undefined } diff --git a/lib/strategies/tools.ts b/lib/strategies/tools.ts index 370c5d10..6ca7d59a 100644 --- a/lib/strategies/tools.ts +++ b/lib/strategies/tools.ts @@ -58,7 +58,7 @@ async function executePruneOperation( await ensureSessionInitialized(ctx.client, state, sessionId, logger, messages) - const currentParams = getCurrentParams(messages, logger) + const currentParams = getCurrentParams(state, messages, logger) const toolIdList: string[] = buildToolIdList(state, messages, logger) // Validate that all numeric IDs are within bounds diff --git a/lib/strategies/utils.ts b/lib/strategies/utils.ts index 5f141cad..c75081f7 100644 --- a/lib/strategies/utils.ts +++ b/lib/strategies/utils.ts @@ -1,27 +1,36 @@ import { SessionState, WithParts } from "../state" -import { UserMessage } from "@opencode-ai/sdk" +import { UserMessage } from "@opencode-ai/sdk/v2" import { Logger } from "../logger" import { encode } from "gpt-tokenizer" import { getLastUserMessage, isMessageCompacted } from "../shared-utils" export function getCurrentParams( + state: SessionState, messages: WithParts[], logger: Logger, ): { providerId: string | undefined modelId: string | undefined agent: string | undefined + variant: string | undefined } { const userMsg = getLastUserMessage(messages) if (!userMsg) { logger.debug("No user message found when determining current params") - return { providerId: undefined, modelId: undefined, agent: undefined } + return { + providerId: undefined, + modelId: undefined, + agent: undefined, + variant: state.variant, + } } - const agent: string = (userMsg.info as UserMessage).agent - const providerId: string | undefined = (userMsg.info as UserMessage).model.providerID - const modelId: string | undefined = (userMsg.info as UserMessage).model.modelID + const userInfo = userMsg.info as UserMessage + const agent: string = userInfo.agent + const providerId: string | undefined = userInfo.model.providerID + const modelId: string | undefined = userInfo.model.modelID + const variant: string | undefined = state.variant ?? userInfo.variant - return { providerId, modelId, agent } + return { providerId, modelId, agent, variant } } /** diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts index 159246cc..81bba142 100644 --- a/lib/ui/notification.ts +++ b/lib/ui/notification.ts @@ -99,6 +99,7 @@ export async function sendIgnoredMessage( logger: Logger, ): Promise { const agent = params.agent || undefined + const variant = params.variant || undefined const model = params.providerId && params.modelId ? { @@ -116,6 +117,7 @@ export async function sendIgnoredMessage( noReply: true, agent: agent, model: model, + variant: variant, parts: [ { type: "text", diff --git a/package-lock.json b/package-lock.json index 50e3705d..bb5a9507 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,15 @@ { "name": "@tarquinen/opencode-dcp", - "version": "1.1.3", + "version": "1.1.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@tarquinen/opencode-dcp", - "version": "1.1.3", + "version": "1.1.4", "license": "MIT", "dependencies": { - "@opencode-ai/sdk": "latest", + "@opencode-ai/sdk": "^1.1.3", "gpt-tokenizer": "^3.4.0", "jsonc-parser": "^3.3.1", "zod": "^4.1.13" @@ -477,6 +477,12 @@ "zod": "4.1.8" } }, + "node_modules/@opencode-ai/plugin/node_modules/@opencode-ai/sdk": { + "version": "1.0.143", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.0.143.tgz", + "integrity": "sha512-dtmkBfJ7IIAHzL6KCzAlwc9GybfJONVeCsF6ePYySpkuhslDbRkZBJYb5vqGd1H5zdsgjc6JjuvmOf0rPWUL6A==", + "dev": true + }, "node_modules/@opencode-ai/plugin/node_modules/zod": { "version": "4.1.8", "resolved": "https://registry.npmjs.org/zod/-/zod-4.1.8.tgz", @@ -488,9 +494,10 @@ } }, "node_modules/@opencode-ai/sdk": { - "version": "1.0.143", - "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.0.143.tgz", - "integrity": "sha512-dtmkBfJ7IIAHzL6KCzAlwc9GybfJONVeCsF6ePYySpkuhslDbRkZBJYb5vqGd1H5zdsgjc6JjuvmOf0rPWUL6A==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@opencode-ai/sdk/-/sdk-1.1.3.tgz", + "integrity": "sha512-P4ERbfuT7CilZYyB1l6J/DM6KD0i5V15O+xvsjUitxSS3S2Gr0YsA4bmXU+EsBQGHryUHc81bhJF49a8wSU+tw==", + "license": "MIT" }, "node_modules/@types/node": { "version": "24.10.1", diff --git a/package.json b/package.json index 1f8714b5..99c8e648 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.3", + "version": "1.1.4", "type": "module", "description": "OpenCode plugin that optimizes token usage by pruning obsolete tool outputs from conversation context", "main": "./dist/index.js", @@ -40,7 +40,7 @@ "@opencode-ai/plugin": ">=0.13.7" }, "dependencies": { - "@opencode-ai/sdk": "latest", + "@opencode-ai/sdk": "^1.1.3", "gpt-tokenizer": "^3.4.0", "jsonc-parser": "^3.3.1", "zod": "^4.1.13"