opencode icon indicating copy to clipboard operation
opencode copied to clipboard

fix: Prevent excessive Copilot premium request consumption

Open ananas-viber opened this issue 20 hours ago • 9 comments

Summary

Fixes #8030

Builds on #8393 — Adds synthetic message detection to prevent excessive premium requests.

The official plugin (#8393) uses last?.role !== "user" which fails for synthetic user messages created by OpenCode (compaction, tool attachments, subtasks). This PR detects those synthetic messages and correctly marks them as agent-initiated.

How VSCode Copilot Chat Handles This

From microsoft/vscode-copilot-chat @ main:

// toolCallingLoop.ts:469
userInitiatedRequest: iterationNumber === 0 && !isContinuation && !this.options.request.subAgentInvocationId

VSCode uses explicit flags to mark tool loops, continuations, and subagents as agent. OpenCode doesn't have these flags, so we infer the same intent by checking message history.

Detection Logic

Scenario X-Initiator Charges Premium?
First user message user ✅ Yes
After assistant/tool message exists agent ❌ No
Synthetic user message (compaction, tool result) agent ❌ No

Rules:

  1. If ANY assistant/tool message exists → agent (matches VSCode's tool loop behavior)
  2. If LAST user message is synthetic → agent
  3. Otherwise → user

Synthetic patterns detected:

  • "What did we do so far?" (compaction)
  • "Tool X returned an attachment:" (tool results)
  • "The following tool was executed by the user" (subtasks)

Why This Approach

OpenCode creates synthetic user messages for internal operations that VSCode handles differently. Since we can't use VSCode's explicit flags (iterationNumber, isContinuation, subAgentInvocationId), we infer agent status from:

  1. Presence of assistant/tool messages in history
  2. Pattern matching on synthetic user message content

This aligns with third-party Copilot clients (litellm, copilot-api, crush) that use the same "Sticky" inference approach.

Changes

copilot.ts:

  • Added isSynthetic() and hasSyntheticContent() for pattern matching
  • Added detectAgent() with history-based inference
  • Unified detectVision() for both Completions and Responses API

copilot.test.ts (new):

  • 16 tests covering synthetic detection and agent detection

Verification

Check Result
Prettier ✅ Pass
Type check ✅ Pass
Copilot tests (local) ✅ 16 pass
Copilot tests (Docker) ✅ 16 pass
Rebased on latest dev ✅ (includes #8393)

Related

  • #8067 — Original discussion on Copilot premium request consumption (closed, discussion ongoing)
  • #8030 — Report of excessive premium requests
  • #8700 — Synthetic user messages burning premium requests
  • #8766 — Follow-up: migrate to metadata-based detection (long-term)

ananas-viber avatar Jan 15 '26 19:01 ananas-viber