fix: Prevent excessive Copilot premium request consumption
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:
- If ANY assistant/tool message exists →
agent(matches VSCode's tool loop behavior) - If LAST user message is synthetic →
agent - 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:
- Presence of assistant/tool messages in history
- 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()andhasSyntheticContent()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)