Skip to content

system.transform fires after messages.transform — plugins can't coordinate system+message mutations #19960

@ualtinok

Description

@ualtinok

Description

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.tsllm.ts:

  1. experimental.chat.messages.transform — plugins mutate messages (blind to system prompt)
  2. System prompt partially built (env + skills + instructions)
  3. 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.
  • Related: Feature: Include user message text in experimental.chat.system.transform input #17637 — a plugin author needed user message text in system.transform for targeted context injection; the hook ordering makes this even harder.

Proposed order:

  1. Full system prompt assembled (agent prompt + env + skills + instructions + user.system)
  2. experimental.chat.system.transform — plugins inspect/mutate system prompt
  3. 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

  1. Create a plugin that uses both experimental.chat.system.transform and experimental.chat.messages.transform
  2. In messages.transform, try to access/react to the system prompt state
  3. Observe that system.transform hasn't fired yet — the system prompt isn't available

Operating System

macOS

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions