From f0421106a736c58a44867dab08c073fd6d382487 Mon Sep 17 00:00:00 2001 From: Resham Joshi <65915470+iamtoruk@users.noreply.github.com> Date: Fri, 9 Oct 2026 08:42:05 -0700 Subject: [PATCH] fix(copilot): price JetBrains claude-opus-4.7 as Opus 4.7 The dot-minor lookahead from #1531 stops claude-opus-4 matching claude-opus-4.7, and 4.7 was not in the token list, so those turns fell back to copilot-anthropic-auto (Sonnet 4.5 rates). Add the token and bump the copilot session-cache parse tag so cached turns re-parse. --- src/providers/copilot.ts | 1 + src/session-cache.ts | 4 +++- tests/providers/copilot.test.ts | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/providers/copilot.ts b/src/providers/copilot.ts index f289a7c5..168f75a3 100644 --- a/src/providers/copilot.ts +++ b/src/providers/copilot.ts @@ -1858,6 +1858,7 @@ function createChatSessionParser( // specific name (e.g. "gpt-4.1-mini" before "gpt-4.1"). const JETBRAINS_MODEL_TOKENS = [ 'claude-opus-4.8', + 'claude-opus-4.7', 'claude-opus-4.6', 'claude-opus-4.5', 'claude-opus-4.1', diff --git a/src/session-cache.ts b/src/session-cache.ts index b4717a0a..95603c6b 100644 --- a/src/session-cache.ts +++ b/src/session-cache.ts @@ -487,7 +487,9 @@ export const PROVIDER_PARSE_VERSIONS: Record = { // store-row-output-v1: every session-store row carries its own // output_tokens; serve time zeroes it where a per-turn call owns the output. // Keys are unchanged, so the re-parse replaces cached output-0 rows in place. - copilot: 'cli-shutdown-cost-v1-skills-source-provenance-v1-session-store-v3-chatsession-otel-skills-v1-otel-trace-metadata-once-v1-transcript-unknown-usage-v1-otel-workspace-project-v1-journal-request-input-v1-tokenless-turns-v1-store-row-output-v1', + // jetbrains-opus-4-7-v1: JetBrains claude-opus-4.7 turns priced as the + // anthropic-auto fallback; cached entries must re-parse once. + copilot: 'cli-shutdown-cost-v1-skills-source-provenance-v1-session-store-v3-chatsession-otel-skills-v1-otel-trace-metadata-once-v1-transcript-unknown-usage-v1-otel-workspace-project-v1-journal-request-input-v1-tokenless-turns-v1-store-row-output-v1-jetbrains-opus-4-7-v1', // authoritative-usage-v4: persist one Grok session call from top-level // authoritative totals, use modelUsage only for priced attribution, clamp // reasoning per record, and label mixed sessions estimated. diff --git a/tests/providers/copilot.test.ts b/tests/providers/copilot.test.ts index 155dde5e..19b2f0b6 100644 --- a/tests/providers/copilot.test.ts +++ b/tests/providers/copilot.test.ts @@ -4400,11 +4400,18 @@ describe('copilot provider - JetBrains model token scan', () => { expect(findJetBrainsModelToken('"model":"gpt-5.6"')).toBe('gpt-5.6') expect(findJetBrainsModelToken('gpt-5.4-mini beta')).toBe('gpt-5.4-mini') expect(findJetBrainsModelToken('switched to claude-opus-4.8 today')).toBe('claude-opus-4-8') + expect(findJetBrainsModelToken('switched to claude-opus-4.7 today')).toBe('claude-opus-4-7') expect(findJetBrainsModelToken('switched to claude-opus-4.6 today')).toBe('claude-opus-4-6') expect(findJetBrainsModelToken('switched to claude-sonnet-4.6 today')).toBe('claude-sonnet-4-6') expect(findJetBrainsModelToken('switched to claude-haiku-4.5 today')).toBe('claude-haiku-4-5') }) + it('prices a JetBrains claude-opus-4.7 chunk at the Opus 4.7 rate', () => { + const model = findJetBrainsModelToken('"model":"claude-opus-4.7"') + expect(model).toBe('claude-opus-4-7') + expect(calculateCost(model, 1_000_000, 1_000_000, 0, 0, 0)).toBeCloseTo(30, 6) + }) + it('still matches tokens followed by sentence punctuation or file extensions', () => { expect(findJetBrainsModelToken('switched to gpt-5. thanks')).toBe('gpt-5') expect(findJetBrainsModelToken('see configs/gpt-5.json for details')).toBe('gpt-5')