diff --git a/packages/opencode/src/session/prompt.ts b/packages/opencode/src/session/prompt.ts index 0590fc38274c..c4a663c7a2f1 100644 --- a/packages/opencode/src/session/prompt.ts +++ b/packages/opencode/src/session/prompt.ts @@ -1562,7 +1562,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), 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: {