From 42aa09d8198d9a317b1ce49653110887078ae18e Mon Sep 17 00:00:00 2001 From: cau1k Date: Mon, 5 Jan 2026 21:38:33 -0500 Subject: [PATCH 1/6] fix: finds and adds missing variant in synthetic messages --- lib/messages/inject.ts | 5 +++-- lib/messages/utils.ts | 4 +++- lib/shared-utils.ts | 13 +++++++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index debfd83..bec4a0c 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -3,7 +3,7 @@ import type { Logger } from "../logger" import type { PluginConfig } from "../config" import { loadPrompt } from "../prompts" import { extractParameterKey, buildToolIdList, createSyntheticUserMessage } from "./utils" -import { getLastUserMessage } from "../shared-utils" +import { getLastUserMessage, findUserVariant } from "../shared-utils" const getNudgeString = (config: PluginConfig): string => { const discardEnabled = config.tools.discard.enabled @@ -125,5 +125,6 @@ export const insertPruneToolContext = ( if (!lastUserMessage) { return } - messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent)) + const variant = findUserVariant(messages) + messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent, variant)) } diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 756c1de..499d677 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -6,7 +6,7 @@ import type { UserMessage } from "@opencode-ai/sdk" 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 +19,8 @@ export const createSyntheticUserMessage = (baseMessage: WithParts, content: stri providerID: userInfo.model.providerID, modelID: userInfo.model.modelID, }, + // @opencode-ai/sdk doesn't yet ship a variant type + ...(variant !== undefined && { variant }), }, parts: [ { diff --git a/lib/shared-utils.ts b/lib/shared-utils.ts index ce3be56..7b016dc 100644 --- a/lib/shared-utils.ts +++ b/lib/shared-utils.ts @@ -13,3 +13,16 @@ export const getLastUserMessage = (messages: WithParts[]): WithParts | null => { } return null } + +export const findUserVariant = (messages: WithParts[]): string | undefined => { + for (let i = messages.length - 1; i >= 0; i--) { + const msg = messages[i] + if (msg.info.role === "user") { + const variant = (msg.info as any).variant + if (variant !== undefined && variant !== null) { + return variant + } + } + } + return undefined +} From 3bd5ddbd23dea12fe0c5e31ec17f640469f35acd Mon Sep 17 00:00:00 2001 From: cau1k Date: Tue, 6 Jan 2026 10:56:23 -0500 Subject: [PATCH 2/6] fix: client.session.prompt missing variant --- index.ts | 15 +++++++++++++++ lib/messages/inject.ts | 6 +++--- lib/messages/utils.ts | 4 ++-- lib/shared-utils.ts | 13 ------------- lib/state/state.ts | 2 ++ lib/state/types.ts | 3 ++- lib/strategies/tools.ts | 2 +- lib/strategies/utils.ts | 10 +++++++--- lib/ui/notification.ts | 2 ++ package.json | 2 +- 10 files changed, 35 insertions(+), 24 deletions(-) diff --git a/index.ts b/index.ts index 13df1b0..28b4a6c 100644 --- a/index.ts +++ b/index.ts @@ -68,6 +68,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/messages/inject.ts b/lib/messages/inject.ts index bec4a0c..826793d 100644 --- a/lib/messages/inject.ts +++ b/lib/messages/inject.ts @@ -3,7 +3,7 @@ import type { Logger } from "../logger" import type { PluginConfig } from "../config" import { loadPrompt } from "../prompts" import { extractParameterKey, buildToolIdList, createSyntheticUserMessage } from "./utils" -import { getLastUserMessage, findUserVariant } from "../shared-utils" +import { getLastUserMessage } from "../shared-utils" const getNudgeString = (config: PluginConfig): string => { const discardEnabled = config.tools.discard.enabled @@ -125,6 +125,6 @@ export const insertPruneToolContext = ( if (!lastUserMessage) { return } - const variant = findUserVariant(messages) - messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent, variant)) + // Use cached variant from state (set by chat.message hook) + messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent, state.variant)) } diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 499d677..796486b 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -1,7 +1,7 @@ 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" @@ -19,7 +19,7 @@ export const createSyntheticUserMessage = (baseMessage: WithParts, content: stri providerID: userInfo.model.providerID, modelID: userInfo.model.modelID, }, - // @opencode-ai/sdk doesn't yet ship a variant type + // variant is passed from state.variant (cached by chat.message hook) ...(variant !== undefined && { variant }), }, parts: [ diff --git a/lib/shared-utils.ts b/lib/shared-utils.ts index 7b016dc..ce3be56 100644 --- a/lib/shared-utils.ts +++ b/lib/shared-utils.ts @@ -13,16 +13,3 @@ export const getLastUserMessage = (messages: WithParts[]): WithParts | null => { } return null } - -export const findUserVariant = (messages: WithParts[]): string | undefined => { - for (let i = messages.length - 1; i >= 0; i--) { - const msg = messages[i] - if (msg.info.role === "user") { - const variant = (msg.info as any).variant - if (variant !== undefined && variant !== null) { - return variant - } - } - } - return undefined -} diff --git a/lib/state/state.ts b/lib/state/state.ts index 05ebc0c..a85d61e 100644 --- a/lib/state/state.ts +++ b/lib/state/state.ts @@ -56,6 +56,7 @@ export function createSessionState(): SessionState { lastToolPrune: false, lastCompaction: 0, currentTurn: 0, + variant: undefined, } } @@ -75,6 +76,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 f9dfd7a..9d72e19 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 @@ -35,4 +35,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 370c5d1..6ca7d59 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 5f141ca..46fd43e 100644 --- a/lib/strategies/utils.ts +++ b/lib/strategies/utils.ts @@ -1,27 +1,31 @@ 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 + // Use cached variant from state (set by chat.message hook) + const variant: string | undefined = state.variant - return { providerId, modelId, agent } + return { providerId, modelId, agent, variant } } /** diff --git a/lib/ui/notification.ts b/lib/ui/notification.ts index 159246c..81bba14 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.json b/package.json index 1f8714b..a1d02ae 100644 --- a/package.json +++ b/package.json @@ -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" From f7647bc4a98187f8ca52ab22ded5c0f695b94fa7 Mon Sep 17 00:00:00 2001 From: cau1k Date: Tue, 6 Jan 2026 11:04:38 -0500 Subject: [PATCH 3/6] fix: add fallback to read variant directly if state.variant is initially undefined --- lib/messages/inject.ts | 5 +++-- lib/strategies/utils.ts | 10 +++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/messages/inject.ts b/lib/messages/inject.ts index 826793d..11153b7 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,6 +126,6 @@ export const insertPruneToolContext = ( if (!lastUserMessage) { return } - // Use cached variant from state (set by chat.message hook) - messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent, state.variant)) + const variant = state.variant ?? (lastUserMessage.info as UserMessage).variant + messages.push(createSyntheticUserMessage(lastUserMessage, prunableToolsContent, variant)) } diff --git a/lib/strategies/utils.ts b/lib/strategies/utils.ts index 46fd43e..987bd9a 100644 --- a/lib/strategies/utils.ts +++ b/lib/strategies/utils.ts @@ -19,11 +19,11 @@ export function getCurrentParams( logger.debug("No user message found when determining current params") 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 - // Use cached variant from state (set by chat.message hook) - const variant: string | undefined = state.variant + 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, variant } } From 0f6f700101197a20d63c5602f6dffe27eae8c039 Mon Sep 17 00:00:00 2001 From: cau1k Date: Tue, 6 Jan 2026 11:22:33 -0500 Subject: [PATCH 4/6] chore: bump package-lock to latest --- package-lock.json | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 50e3705..20fae79 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.1.3", "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", From 04635e248d0ba77b122df0c15a21dbeb046fbfaa Mon Sep 17 00:00:00 2001 From: cau1k Date: Tue, 6 Jan 2026 12:49:25 -0500 Subject: [PATCH 5/6] chore fmt --- lib/messages/utils.ts | 6 +++++- lib/strategies/utils.ts | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 796486b..1cc51e6 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -6,7 +6,11 @@ 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, variant?: string): WithParts => { +export const createSyntheticUserMessage = ( + baseMessage: WithParts, + content: string, + variant?: string, +): WithParts => { const userInfo = baseMessage.info as UserMessage return { info: { diff --git a/lib/strategies/utils.ts b/lib/strategies/utils.ts index 987bd9a..c75081f 100644 --- a/lib/strategies/utils.ts +++ b/lib/strategies/utils.ts @@ -17,7 +17,12 @@ export function getCurrentParams( const userMsg = getLastUserMessage(messages) if (!userMsg) { logger.debug("No user message found when determining current params") - return { providerId: undefined, modelId: undefined, agent: undefined, variant: state.variant } + return { + providerId: undefined, + modelId: undefined, + agent: undefined, + variant: state.variant, + } } const userInfo = userMsg.info as UserMessage const agent: string = userInfo.agent From 5a4b901096c8cb5ecbaec2a9e57a2c110648578d Mon Sep 17 00:00:00 2001 From: cau1k Date: Tue, 6 Jan 2026 13:31:29 -0500 Subject: [PATCH 6/6] fix: use variant param instead --- lib/messages/utils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/messages/utils.ts b/lib/messages/utils.ts index 9a01de1..0338d86 100644 --- a/lib/messages/utils.ts +++ b/lib/messages/utils.ts @@ -12,7 +12,6 @@ export const createSyntheticUserMessage = ( variant?: string, ): WithParts => { const userInfo = baseMessage.info as UserMessage - const variant = (userInfo as any).variant return { info: { id: SYNTHETIC_MESSAGE_ID,