Problem
PR #148 introduced a workaround for Anthropic's OAuth validation by relocating system entries to the first user message. The current implementation in transforms.ts (transformBody) relocates all non-identity, non-billing system entries:
if (txt.startsWith(BILLING_PREFIX) || txt.startsWith(SYSTEM_IDENTITY)) {
keptSystem.push(entry); // kept in system[]
} else if (txt.length > 0) {
movedTexts.push(txt); // moved to first user message
}
This means every plugin using OpenCode's experimental.chat.system.transform hook to inject system-level instructions gets its content relocated from system[] to a user message. The system.transform hook exists specifically so plugins can provide system-level guidance to the model — the blanket relocation undermines that for every other plugin on the stack, not just OpenCode's own system content.
Concern
System prompt content and user message content serve different roles in the Anthropic API — the system parameter exists as a distinct concept precisely because it has different semantics. When plugin-injected system instructions are moved to a user message:
- In short sessions, the relocated content is near the top of the conversation and still influential — no noticeable issue.
- In longer sessions (150K+ tokens, 200+ messages), the first user message is far back in the conversation history. Instructions that were designed to be system-level persistent guidance are now competing with hundreds of subsequent messages for the model's attention.
We use multiple plugins that rely on system.transform for operational instructions (state management, agent coordination). In longer sessions, we've observed the model progressively ignoring these instructions — a pattern consistent with user-message content being deprioritized as conversation length grows, though we can't confirm the relocation is the direct cause since the issue is intermittent.
Question
Is the blanket relocation necessary, or would a more targeted approach work? For example:
- Only relocate OpenCode's own system content (e.g., the environment block that OpenCode builds), keeping plugin
system.transform contributions in system[]
- Relocate based on content characteristics rather than "everything that isn't billing/identity"
- Provide an opt-out mechanism — a way for plugins to mark their system entries as needing system-level positioning
What exactly does Anthropic's validation check? Is it a strict allowlist that only permits the identity + billing entries in system[], or does it reject specific patterns? If additional system entries are permitted as long as they don't match certain rejection criteria, the relocation scope could be narrowed significantly.
Environment
- OpenCode custom build (from source)
- Multiple plugins using
system.transform for operational instructions
- Long sessions (150K+ tokens) where system instruction persistence matters
opencode-claude-auth@latest (currently 1.5.0)
Problem
PR #148 introduced a workaround for Anthropic's OAuth validation by relocating system entries to the first user message. The current implementation in
transforms.ts(transformBody) relocates all non-identity, non-billing system entries:This means every plugin using OpenCode's
experimental.chat.system.transformhook to inject system-level instructions gets its content relocated fromsystem[]to a user message. Thesystem.transformhook exists specifically so plugins can provide system-level guidance to the model — the blanket relocation undermines that for every other plugin on the stack, not just OpenCode's own system content.Concern
System prompt content and user message content serve different roles in the Anthropic API — the system parameter exists as a distinct concept precisely because it has different semantics. When plugin-injected system instructions are moved to a user message:
We use multiple plugins that rely on
system.transformfor operational instructions (state management, agent coordination). In longer sessions, we've observed the model progressively ignoring these instructions — a pattern consistent with user-message content being deprioritized as conversation length grows, though we can't confirm the relocation is the direct cause since the issue is intermittent.Question
Is the blanket relocation necessary, or would a more targeted approach work? For example:
system.transformcontributions insystem[]What exactly does Anthropic's validation check? Is it a strict allowlist that only permits the identity + billing entries in
system[], or does it reject specific patterns? If additional system entries are permitted as long as they don't match certain rejection criteria, the relocation scope could be narrowed significantly.Environment
system.transformfor operational instructionsopencode-claude-auth@latest(currently 1.5.0)