You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
experimental.chat.system.transform currently fires inside llm.ts (inside the processor call), while experimental.chat.messages.transform fires earlier in prompt.ts. This means plugins that need the final system prompt state to make correct decisions during messages.transform can't do it — the system prompt hasn't been assembled or transformed yet when messages are mutated.
Current order in prompt.ts → llm.ts:
experimental.chat.messages.transform — plugins mutate messages (blind to system prompt)
System prompt partially built (env + skills + instructions)
processor.process() called → inside llm.ts, full system assembled (agent prompt prepended), then experimental.chat.system.transform fires
Why this matters for plugins:
Plugins that need to coordinate message mutations with system prompt state can't do it atomically. Any reaction to system prompt changes requires a second turn.
This causes unnecessary cache prefix rebuilds for providers using prompt-prefix caching (Anthropic, Google) — the plugin pays for two separate cache invalidation cycles instead of one.
Full system prompt assembled (agent prompt + env + skills + instructions + user.system)
experimental.chat.system.transform — plugins inspect/mutate system prompt
experimental.chat.messages.transform — plugins mutate messages with full knowledge of system prompt state
This also opens the door for messages.transform to receive sessionID and model in its input (currently receives empty {}), enabling plugins to correlate transformations across hooks.
Plugins
Custom AFT plugin (code exploration tools that need to adapt based on system prompt state)
OpenCode version
v1.3.5
Steps to reproduce
Create a plugin that uses both experimental.chat.system.transform and experimental.chat.messages.transform
In messages.transform, try to access/react to the system prompt state
Observe that system.transform hasn't fired yet — the system prompt isn't available
Description
experimental.chat.system.transformcurrently fires insidellm.ts(inside the processor call), whileexperimental.chat.messages.transformfires earlier inprompt.ts. This means plugins that need the final system prompt state to make correct decisions duringmessages.transformcan't do it — the system prompt hasn't been assembled or transformed yet when messages are mutated.Current order in
prompt.ts→llm.ts:experimental.chat.messages.transform— plugins mutate messages (blind to system prompt)processor.process()called → insidellm.ts, full system assembled (agent prompt prepended), thenexperimental.chat.system.transformfiresWhy this matters for plugins:
system.transformfor targeted context injection; the hook ordering makes this even harder.Proposed order:
experimental.chat.system.transform— plugins inspect/mutate system promptexperimental.chat.messages.transform— plugins mutate messages with full knowledge of system prompt stateThis also opens the door for
messages.transformto receivesessionIDandmodelin its input (currently receives empty{}), enabling plugins to correlate transformations across hooks.Plugins
Custom AFT plugin (code exploration tools that need to adapt based on system prompt state)
OpenCode version
v1.3.5
Steps to reproduce
experimental.chat.system.transformandexperimental.chat.messages.transformmessages.transform, try to access/react to the system prompt statesystem.transformhasn't fired yet — the system prompt isn't availableOperating System
macOS