diff --git a/CHANGELOG.md b/CHANGELOG.md index fcd2b3f0..7fd1a8a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,33 +1,3 @@ -## [7.73.0](https://github.com/kaitranntt/ccs/compare/v7.72.1...v7.73.0) (2026-04-19) - -### Features - -* **proxy:** enhance Anthropic-to-OpenAI message transformation and schema sanitization ([2672e35](https://github.com/kaitranntt/ccs/commit/2672e35362938b054710de681760bd24c3c39e19)) - -### Bug Fixes - -* **browser:** bootstrap managed attach profile setup ([b525033](https://github.com/kaitranntt/ccs/commit/b52503300b0c434fbb9d88283643d3ceaa673d44)) -* **cliproxy:** preserve legacy openai-compat connectors on restart ([16f81fc](https://github.com/kaitranntt/ccs/commit/16f81fc8a595792f1c1a6df060f88cdf28d7afb6)) -* **codex:** probe Windows cmd wrappers via cmd shell ([210ec33](https://github.com/kaitranntt/ccs/commit/210ec33c04fe88be2b651c2163f638085b1b6bd2)) -* **proxy:** harden Anthropic request transformation semantics ([ebc9219](https://github.com/kaitranntt/ccs/commit/ebc92194bb9bf2810e10f64de1fa6e599c2fa156)) -* **proxy:** preserve valid enums during schema normalization ([22ab58b](https://github.com/kaitranntt/ccs/commit/22ab58b02e5dc34f152ecce048c1fbf7d615bdfb)) -* **proxy:** restore strict Anthropic message validation ([32d6bfd](https://github.com/kaitranntt/ccs/commit/32d6bfdda7881a42a376d0973c021f0b47eed7e2)) -* **repo:** remove tracked plans workspace files ([bc4ab98](https://github.com/kaitranntt/ccs/commit/bc4ab98af41e9c9f049cc5da2e6ff356832ef14b)) -* **targets:** use cmd.exe for escaped Windows wrapper launches ([a945b0b](https://github.com/kaitranntt/ccs/commit/a945b0b1047b85a7b343dfe35d8ae9680346e25f)) -* **windows:** align escaped wrapper shell handling ([ccdd0b6](https://github.com/kaitranntt/ccs/commit/ccdd0b6e8e4185ec2b01186cfc69e28f171d0906)) - -### Tests - -* **cliproxy:** assert routing persistence via unified config loader ([485fe4b](https://github.com/kaitranntt/ccs/commit/485fe4ba1c5878083c6e2b6a2d98b37208772a1f)) -* **cliproxy:** cover legacy connector removal cleanup ([00d902b](https://github.com/kaitranntt/ccs/commit/00d902b6e64839cd6b89446cc5dee540f2a059b8)) -* **cliproxy:** isolate routing strategy service state ([5e1e807](https://github.com/kaitranntt/ccs/commit/5e1e8070e821dbe660ea77bc96fec610937ed44a)) -* **websearch:** clear CCS_PROFILE_TYPE in SearXNG fallback spawn env ([e25d791](https://github.com/kaitranntt/ccs/commit/e25d791d9bee38e7143757d37c42bd850e471ae8)) - -### CI - -* add concurrency group and split build/test with artifact sharing ([ad54e17](https://github.com/kaitranntt/ccs/commit/ad54e179b6a3921fd26ad3ac585f5ef965b01c1b)) -* **pre-push:** skip gate for delete-only pushes ([6236cb7](https://github.com/kaitranntt/ccs/commit/6236cb7feb15e954f7f65ae98e6fd98b323a4adb)) - ## [7.72.1](https://github.com/kaitranntt/ccs/compare/v7.72.0...v7.72.1) (2026-04-17) ### Bug Fixes diff --git a/package.json b/package.json index 2ec0516e..f2ffe18a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.73.0", + "version": "7.72.1-dev.7", "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more", "keywords": [ "cli", diff --git a/scripts/code-reviewer.ts b/scripts/code-reviewer.ts index 02dda7f6..89d7a829 100755 --- a/scripts/code-reviewer.ts +++ b/scripts/code-reviewer.ts @@ -26,7 +26,7 @@ interface PRContext { // Config const MAX_DIFF_LINES = 10000; const CLIPROXY_URL = process.env.CLIPROXY_URL || 'http://localhost:8317'; -const MODEL = process.env.REVIEW_MODEL || 'claude-opus-4-7-thinking'; +const MODEL = process.env.REVIEW_MODEL || 'claude-opus-4-6-thinking'; // System prompt for code review - new style const CODE_REVIEWER_SYSTEM_PROMPT = `You are the CCS AGY Code Reviewer, an expert AI assistant reviewing pull requests for the CCS CLI project. diff --git a/src/cliproxy/config/generator.ts b/src/cliproxy/config/generator.ts index 7ea5abaf..43ce4672 100644 --- a/src/cliproxy/config/generator.ts +++ b/src/cliproxy/config/generator.ts @@ -40,10 +40,8 @@ export const CCS_CONTROL_PANEL_SECRET = 'ccs'; * v15: Prune stale generated Antigravity Gemini preview aliases during regeneration * v16: Narrow stale Gemini alias cleanup to broad multi-version guessed ranges * v17: Persist routing.strategy from CCS unified config - * v18: Added Claude Opus 4.7 Thinking alias and promoted to default AGY model - * v19: Added Claude Opus 4.7 non-thinking alias for symmetry with Sonnet */ -export const CLIPROXY_CONFIG_VERSION = 19; +export const CLIPROXY_CONFIG_VERSION = 17; interface RegenerateConfigOptions { configPath?: string; @@ -88,8 +86,6 @@ const DEFAULT_ANTIGRAVITY_ALIASES: OAuthModelAliasEntry[] = [ // Backward compatibility: legacy sonnet thinking alias now routes to canonical model ID. { name: 'claude-sonnet-4-6-thinking', alias: 'claude-sonnet-4-6', fork: true }, { name: 'claude-opus-4-6-thinking', alias: 'claude-opus-4-6-thinking', fork: true }, - { name: 'claude-opus-4-7-thinking', alias: 'claude-opus-4-7-thinking', fork: true }, - { name: 'claude-opus-4-7', alias: 'claude-opus-4-7', fork: true }, ]; const BUILT_IN_GEMINI_ALIAS_NAMES = new Set( diff --git a/src/cliproxy/model-catalog.ts b/src/cliproxy/model-catalog.ts index f2038161..6c19ab7c 100644 --- a/src/cliproxy/model-catalog.ts +++ b/src/cliproxy/model-catalog.ts @@ -87,42 +87,12 @@ export const MODEL_CATALOG: Partial> = agy: { provider: 'agy', displayName: 'Antigravity', - defaultModel: 'claude-opus-4-7-thinking', + defaultModel: 'claude-opus-4-6-thinking', models: [ - { - id: 'claude-opus-4-7-thinking', - name: 'Claude Opus 4.7 Thinking', - description: 'Latest flagship, extended thinking', - nativeImageInput: true, - thinking: { - type: 'budget', - min: 1024, - max: 128000, - zeroAllowed: true, - dynamicAllowed: true, - }, - // TODO: Re-enable when Antigravity backend supports 1M context (currently 256k) - // extendedContext: true, - extendedContext: false, - }, - { - id: 'claude-opus-4-7', - name: 'Claude Opus 4.7', - description: 'Latest flagship model', - nativeImageInput: true, - thinking: { - type: 'budget', - min: 1024, - max: 128000, - zeroAllowed: true, - dynamicAllowed: true, - }, - extendedContext: false, - }, { id: 'claude-opus-4-6-thinking', name: 'Claude Opus 4.6 Thinking', - description: 'Previous flagship, extended thinking', + description: 'Latest flagship, extended thinking', nativeImageInput: true, thinking: { type: 'budget', diff --git a/src/cursor/cursor-default-models.ts b/src/cursor/cursor-default-models.ts index 1a7f3eb6..01cc05c6 100644 --- a/src/cursor/cursor-default-models.ts +++ b/src/cursor/cursor-default-models.ts @@ -10,7 +10,7 @@ export const DEFAULT_CURSOR_MODEL = 'gpt-5.3-codex'; /** * Default models available through Cursor IDE. * Used as fallback when daemon is not reachable. - * Source: Cursor docs model catalog (Feb 2026) + * Source: Cursor docs / official announcements (Apr 2026) */ export const DEFAULT_CURSOR_MODELS: CursorModel[] = [ // Anthropic Models diff --git a/src/targets/droid-adapter.ts b/src/targets/droid-adapter.ts index a2bac165..c162f7f3 100644 --- a/src/targets/droid-adapter.ts +++ b/src/targets/droid-adapter.ts @@ -54,7 +54,7 @@ export class DroidAdapter implements TargetAdapter { model: creds.model, }); const modelRef = await upsertCcsModel(creds.profile, { - model: creds.model || 'claude-opus-4-7', + model: creds.model || 'claude-opus-4-6', displayName: `CCS ${creds.profile}`, baseUrl: creds.baseUrl, apiKey: creds.apiKey, diff --git a/src/web-server/model-pricing.ts b/src/web-server/model-pricing.ts index 011ff444..93da148b 100644 --- a/src/web-server/model-pricing.ts +++ b/src/web-server/model-pricing.ts @@ -235,12 +235,6 @@ const PRICING_REGISTRY: Record = { cacheCreationPerMillion: 6.25, cacheReadPerMillion: 0.5, }, - 'claude-opus-4-7-thinking': { - inputPerMillion: 5.0, - outputPerMillion: 25.0, - cacheCreationPerMillion: 6.25, - cacheReadPerMillion: 0.5, - }, // --------------------------------------------------------------------------- // OpenAI Models - Source: better-ccusage diff --git a/tests/unit/cliproxy/model-catalog.test.js b/tests/unit/cliproxy/model-catalog.test.js index 35ec26c5..5023bf32 100644 --- a/tests/unit/cliproxy/model-catalog.test.js +++ b/tests/unit/cliproxy/model-catalog.test.js @@ -66,21 +66,7 @@ describe('Model Catalog', () => { describe('AGY models', () => { it('has correct default model', () => { const { MODEL_CATALOG } = modelCatalog; - assert.strictEqual(MODEL_CATALOG.agy.defaultModel, 'claude-opus-4-7-thinking'); - }); - - it('includes Claude Opus 4.7 Thinking', () => { - const { MODEL_CATALOG } = modelCatalog; - const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-7-thinking'); - assert(opus, 'Should include Claude Opus 4.7 Thinking'); - assert.strictEqual(opus.name, 'Claude Opus 4.7 Thinking'); - }); - - it('includes Claude Opus 4.7 (non-thinking)', () => { - const { MODEL_CATALOG } = modelCatalog; - const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-7'); - assert(opus, 'Should include Claude Opus 4.7'); - assert.strictEqual(opus.name, 'Claude Opus 4.7'); + assert.strictEqual(MODEL_CATALOG.agy.defaultModel, 'claude-opus-4-6-thinking'); }); it('includes Claude Opus 4.6 Thinking', () => { @@ -131,9 +117,9 @@ describe('Model Catalog', () => { assert.strictEqual(flash.tier, undefined, 'AGY models should not have paid tier'); }); - it('has 7 models total', () => { + it('has 5 models total', () => { const { MODEL_CATALOG } = modelCatalog; - assert.strictEqual(MODEL_CATALOG.agy.models.length, 7); + assert.strictEqual(MODEL_CATALOG.agy.models.length, 5); }); }); @@ -291,11 +277,11 @@ describe('Model Catalog', () => { }); describe('findModel', () => { - it('finds Claude Opus 4.7 Thinking in agy', () => { + it('finds Claude Opus 4.7 in claude', () => { const { findModel } = modelCatalog; - const model = findModel('agy', 'claude-opus-4-7-thinking'); + const model = findModel('claude', 'claude-opus-4-7'); assert(model, 'Should find model'); - assert.strictEqual(model.name, 'Claude Opus 4.7 Thinking'); + assert.strictEqual(model.name, 'Claude Opus 4.7'); }); it('finds Claude Opus 4.6 Thinking in agy', () => { @@ -342,10 +328,10 @@ describe('Model Catalog', () => { it('falls back to the next supported model when the default is excluded', () => { const { getSuggestedReplacementModel } = modelCatalog; - expect(getSuggestedReplacementModel('agy', 'claude-opus-4-7-thinking')).toBe( - 'claude-opus-4-7' + expect(getSuggestedReplacementModel('agy', 'claude-opus-4-6-thinking')).toBe( + 'claude-sonnet-4-6' ); - expect(getSuggestedReplacementModel('agy')).toBe('claude-opus-4-7-thinking'); + expect(getSuggestedReplacementModel('agy')).toBe('claude-opus-4-6-thinking'); }); it('returns undefined when no provider catalog exists', () => { @@ -406,10 +392,10 @@ describe('Model Catalog', () => { }); describe('Thinking models ordering', () => { - it('Claude Opus 4.7 Thinking is not deprecated', () => { + it('Claude Opus 4.6 Thinking is not deprecated', () => { const { MODEL_CATALOG } = modelCatalog; - const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-7-thinking'); - assert(opus, 'Should include Claude Opus 4.7 Thinking'); + const opus = MODEL_CATALOG.agy.models.find((m) => m.id === 'claude-opus-4-6-thinking'); + assert(opus, 'Should include Claude Opus 4.6 Thinking'); assert.strictEqual(opus.deprecated, undefined, 'Should not be marked as deprecated'); }); @@ -429,14 +415,14 @@ describe('Model Catalog', () => { const models = MODEL_CATALOG.agy.models; // Find indices of thinking models - const opus47Idx = models.findIndex((m) => m.id === 'claude-opus-4-7-thinking'); + const opusIdx = models.findIndex((m) => m.id === 'claude-opus-4-6-thinking'); const sonnetThinkingIdx = models.findIndex((m) => m.id === 'claude-sonnet-4-6'); // Find indices of the remaining non-Claude model const geminiIdx = models.findIndex((m) => m.id === 'gemini-3.1-pro-high'); // Primary Claude choices should appear ahead of Gemini fallback. - assert(opus47Idx < geminiIdx, 'Opus 4.7 should be above Gemini'); + assert(opusIdx < geminiIdx, 'Opus should be above Gemini'); assert(sonnetThinkingIdx < geminiIdx, 'Sonnet should be above Gemini'); }); }); @@ -444,7 +430,6 @@ describe('Model Catalog', () => { describe('isModelDeprecated', () => { it('returns false for active AGY Claude models', () => { const { isModelDeprecated } = modelCatalog; - assert.strictEqual(isModelDeprecated('agy', 'claude-opus-4-7-thinking'), false); assert.strictEqual(isModelDeprecated('agy', 'claude-opus-4-6-thinking'), false); assert.strictEqual(isModelDeprecated('agy', 'claude-sonnet-4-6'), false); }); @@ -463,7 +448,6 @@ describe('Model Catalog', () => { describe('getModelDeprecationReason', () => { it('returns undefined for active AGY Claude models', () => { const { getModelDeprecationReason } = modelCatalog; - assert.strictEqual(getModelDeprecationReason('agy', 'claude-opus-4-7-thinking'), undefined); assert.strictEqual(getModelDeprecationReason('agy', 'claude-opus-4-6-thinking'), undefined); assert.strictEqual(getModelDeprecationReason('agy', 'claude-sonnet-4-6'), undefined); }); diff --git a/tests/unit/cursor/cursor-models.test.ts b/tests/unit/cursor/cursor-models.test.ts index ce6d6635..dddea9e8 100644 --- a/tests/unit/cursor/cursor-models.test.ts +++ b/tests/unit/cursor/cursor-models.test.ts @@ -26,6 +26,13 @@ describe('DEFAULT_CURSOR_MODELS', () => { expect(providers.has('google')).toBe(true); }); + it('keeps both Claude 4.6 Opus and Claude 4.7 Opus in the fallback catalog', () => { + const ids = DEFAULT_CURSOR_MODELS.map((model) => model.id); + expect(ids).toContain('claude-4.6-opus'); + expect(ids).toContain('claude-4.7-opus'); + expect(ids).toContain('claude-4.7-opus-fast-mode'); + }); + it('has exactly one default model', () => { const defaults = DEFAULT_CURSOR_MODELS.filter((m) => m.isDefault); expect(defaults).toHaveLength(1); @@ -88,6 +95,7 @@ describe('detectProvider', () => { it('detects anthropic models', () => { expect(detectProvider('claude-4.5-sonnet')).toBe('anthropic'); expect(detectProvider('claude-4.6-opus')).toBe('anthropic'); + expect(detectProvider('claude-4.7-opus')).toBe('anthropic'); }); it('detects openai models', () => { diff --git a/tests/unit/model-pricing.test.ts b/tests/unit/model-pricing.test.ts index 5abe6cb9..31ebb4c2 100644 --- a/tests/unit/model-pricing.test.ts +++ b/tests/unit/model-pricing.test.ts @@ -155,12 +155,6 @@ describe('model-pricing', () => { expect(opus47.outputPerMillion).toBe(25.0); }); - it('should return correct pricing for Claude Opus 4.7 thinking variant', () => { - const opus47t = getModelPricing('claude-opus-4-7-thinking'); - expect(opus47t.inputPerMillion).toBe(5.0); - expect(opus47t.outputPerMillion).toBe(25.0); - }); - it('should match date-stamped Claude Opus 4.7 to correct pricing', () => { const opus47dated = getModelPricing('claude-opus-4-7-20260401'); expect(opus47dated.inputPerMillion).toBe(5.0);