From e8799b21c52bdeecf0d2901f330bbbd89c15fc0e Mon Sep 17 00:00:00 2001 From: n1flh31mur <66485293+n1flh31mur@users.noreply.github.com> Date: Sun, 3 May 2026 01:43:47 +0300 Subject: [PATCH 1/2] feat(plugin): add pre_chat.messages.transform hook for image stripping Add a new hook type that fires BEFORE the LLM is invoked, allowing plugins to inspect and transform the messages array. This enables: - Image-to-text stripping (replace FilePart with text descriptions) - Vision plugin integration (add Qwen2.5-VL-3B descriptions) - Pre-inference message modification The existing experimental.chat.messages.transform had empty input and was non-functional. Mark it as deprecated. Changes: - Add pre_chat.messages.transform hook with full messages input - Mark experimental.chat.messages.transform as @deprecated - Document the hook's purpose and usage pattern --- packages/plugin/src/index.ts | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/packages/plugin/src/index.ts b/packages/plugin/src/index.ts index 2e96dd980179..a8520ab2dcd9 100644 --- a/packages/plugin/src/index.ts +++ b/packages/plugin/src/index.ts @@ -278,6 +278,39 @@ export interface Hooks { metadata: any }, ) => Promise + /** + * Called before the LLM is invoked. Allows plugins to inspect and + * transform the messages array (e.g., strip images, add vision + * descriptions, modify system prompts). + * + * - `messages`: The full messages array including all parts + * - `output.messages`: Return a new array or mutate to transform messages. + * Remove `FilePart` objects with `image: true` to strip images. + * Replace with `TextPart` containing the vision description. + * - Runs BEFORE `experimental.chat.messages.transform`. + */ + "pre_chat.messages.transform"?: ( + input: { + sessionID: string + agent: string + model: Model + messages: { + info: Message + parts: Part[] + }[] + }, + output: { + messages: { + info: Message + parts: Part[] + }[] + }, + ) => Promise + /** + * @deprecated Use `pre_chat.messages.transform` instead. + * This hook exists for backward compatibility but has empty input + * and does not receive messages. + */ "experimental.chat.messages.transform"?: ( input: {}, output: { From b80f529963e2a265f716617638cbf8c25f0f0575 Mon Sep 17 00:00:00 2001 From: n1flh31mur <66485293+n1flh31mur@users.noreply.github.com> Date: Sun, 3 May 2026 02:36:32 +0300 Subject: [PATCH 2/2] feat(prompt): dispatch pre_chat.messages.transform hook before LLM invocation Add the runtime dispatch of pre_chat.messages.transform in the main prompt processing pipeline. This fires after system prompt assembly but before tool resolution and the LLM API call. Also fixes the existing experimental.chat.messages.transform call to pass proper input context (sessionID, agent, model, messages) instead of an empty object. This is the companion to PR #25493 which adds the SDK type definitions. Without this dispatch, the hook type exists but is never called at runtime. --- packages/opencode/src/session/prompt.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 80c47d3ceda0..bb906b0f2290 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1448,7 +1448,23 @@ NOTE: At any point in time through this workflow you should feel free to ask the } } - yield* plugin.trigger("experimental.chat.messages.transform", {}, { messages: msgs }) + // Allow plugins to transform messages before LLM invocation + // (e.g., replace image attachments with text descriptions via Vision) + yield* plugin.trigger( + "pre_chat.messages.transform", + { + sessionID, + agent: agent.name, + model, + messages: msgs, + }, + { messages: structuredClone(msgs) }, + ) + yield* plugin.trigger( + "experimental.chat.messages.transform", + { sessionID, agent: agent.name, model, messages: msgs }, + { messages: msgs }, + ) const [skills, env, instructions, modelMsgs] = yield* Effect.all([ sys.skills(agent),