mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-11 03:13:12 +00:00
fix(extended-context): manage ANTHROPIC_DEFAULT_MODEL and keep the overlay in sync
Claude Code reads ANTHROPIC_DEFAULT_MODEL as its lowest-priority startup model (after --model, ANTHROPIC_MODEL and the settings `model` field). It is a plain model id, so behind a proxy a bare value is clamped to 200k exactly like the tier keys. Add it to EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS so --1m/--no-1m and the saved preference toggle it with the rest. The `--settings` launch overlay only carried the routing keys plus the five Anthropic tier keys. Claude Code applies the settings `env` block on top of the process environment, so a bare CLAUDE_CODE_SUBAGENT_MODEL or ANTHROPIC_DEFAULT_MODEL persisted on disk silently undid the resolved --1m result (and a saved [1m] undid --no-1m). Overlay the extended-context extra keys too. Verified with a stub Claude binary reading the overlay file: ccs claude --no-1m -> ANTHROPIC_DEFAULT_MODEL=claude-opus-5, CLAUDE_CODE_SUBAGENT_MODEL=claude-sonnet-5 ccs claude --1m -> both keys suffixed with [1m]
This commit is contained in:
1 parent
02c45e088a
commit
7ae7a5eba6
4 files changed
+56
-4
No files matched your search
@@ -57,6 +57,30 @@ describe('buildLaunchSettingsOverlay', () => {
|
|||||||
expect(env.ANTHROPIC_AUTH_TOKEN).toBe('ccs-internal-managed');
|
expect(env.ANTHROPIC_AUTH_TOKEN).toBe('ccs-internal-managed');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('overlays the extended-context extra model keys so a saved bare value cannot clobber --1m/--no-1m', () => {
|
||||||
|
writePersisted({
|
||||||
|
env: {
|
||||||
|
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/claude',
|
||||||
|
ANTHROPIC_MODEL: 'claude-opus-5[1m]',
|
||||||
|
ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5[1m]',
|
||||||
|
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-sonnet-5[1m]',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { settings, changed } = buildLaunchSettingsOverlay(settingsPath, {
|
||||||
|
ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/claude',
|
||||||
|
ANTHROPIC_MODEL: 'claude-opus-5',
|
||||||
|
ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5',
|
||||||
|
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-sonnet-5',
|
||||||
|
} as NodeJS.ProcessEnv);
|
||||||
|
|
||||||
|
expect(changed).toBe(true);
|
||||||
|
const env = settings.env as Record<string, string>;
|
||||||
|
expect(env.ANTHROPIC_MODEL).toBe('claude-opus-5');
|
||||||
|
expect(env.ANTHROPIC_DEFAULT_MODEL).toBe('claude-opus-5');
|
||||||
|
expect(env.CLAUDE_CODE_SUBAGENT_MODEL).toBe('claude-sonnet-5');
|
||||||
|
});
|
||||||
|
|
||||||
it('preserves non-env settings (permissions, hooks, etc.)', () => {
|
it('preserves non-env settings (permissions, hooks, etc.)', () => {
|
||||||
writePersisted({
|
writePersisted({
|
||||||
env: { ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/codex' },
|
env: { ANTHROPIC_BASE_URL: 'http://127.0.0.1:8317/api/provider/codex' },
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import * as os from 'os';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
|
||||||
import { ANTHROPIC_MODEL_ENV_KEYS, ANTHROPIC_ROUTING_ENV_KEYS } from '../../utils/shell-executor';
|
import { ANTHROPIC_MODEL_ENV_KEYS, ANTHROPIC_ROUTING_ENV_KEYS } from '../../utils/shell-executor';
|
||||||
|
import { EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS } from '../../shared/extended-context-utils';
|
||||||
|
|
||||||
// SIBLING HELPER: src/utils/openai-compat-launch-settings.ts solves the same
|
// SIBLING HELPER: src/utils/openai-compat-launch-settings.ts solves the same
|
||||||
// "persisted --settings env clobbers runtime routing env" problem by STRIPPING
|
// "persisted --settings env clobbers runtime routing env" problem by STRIPPING
|
||||||
@@ -38,9 +39,15 @@ import { ANTHROPIC_MODEL_ENV_KEYS, ANTHROPIC_ROUTING_ENV_KEYS } from '../../util
|
|||||||
* Environment keys that control provider routing/model selection and are read
|
* Environment keys that control provider routing/model selection and are read
|
||||||
* by Claude from the settings `env` block. These must reflect the resolved
|
* by Claude from the settings `env` block. These must reflect the resolved
|
||||||
* proxy-chain environment, not the persisted on-disk values. Reuses the
|
* proxy-chain environment, not the persisted on-disk values. Reuses the
|
||||||
* canonical routing/model key lists from shell-executor.
|
* canonical routing/model key lists from shell-executor, plus the extra model
|
||||||
|
* keys the [1m] preference manages (subagent + startup default): a saved bare
|
||||||
|
* value there would otherwise clobber the resolved --1m/--no-1m result.
|
||||||
*/
|
*/
|
||||||
const ROUTING_ENV_KEYS = [...ANTHROPIC_ROUTING_ENV_KEYS, ...ANTHROPIC_MODEL_ENV_KEYS];
|
const ROUTING_ENV_KEYS = [
|
||||||
|
...ANTHROPIC_ROUTING_ENV_KEYS,
|
||||||
|
...ANTHROPIC_MODEL_ENV_KEYS,
|
||||||
|
...EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS,
|
||||||
|
];
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||||
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
||||||
|
|||||||
@@ -19,8 +19,15 @@ export type AnthropicModelEnvKey = (typeof ANTHROPIC_MODEL_ENV_KEYS)[number];
|
|||||||
* model id, so the extended-context preference has to cover them too. Kept
|
* model id, so the extended-context preference has to cover them too. Kept
|
||||||
* separate from ANTHROPIC_MODEL_ENV_KEYS, which also drives routing, model-id
|
* separate from ANTHROPIC_MODEL_ENV_KEYS, which also drives routing, model-id
|
||||||
* normalization and profile validation.
|
* normalization and profile validation.
|
||||||
|
*
|
||||||
|
* ANTHROPIC_DEFAULT_MODEL is Claude Code's lowest-priority startup model
|
||||||
|
* (after --model, ANTHROPIC_MODEL and the settings `model` field), so it needs
|
||||||
|
* the same [1m] treatment whenever a profile carries it.
|
||||||
*/
|
*/
|
||||||
export const EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS = ['CLAUDE_CODE_SUBAGENT_MODEL'] as const;
|
export const EXTRA_EXTENDED_CONTEXT_MODEL_ENV_KEYS = [
|
||||||
|
'CLAUDE_CODE_SUBAGENT_MODEL',
|
||||||
|
'ANTHROPIC_DEFAULT_MODEL',
|
||||||
|
] as const;
|
||||||
|
|
||||||
/** Every model env key the [1m] preference is applied to. */
|
/** Every model env key the [1m] preference is applied to. */
|
||||||
export const EXTENDED_CONTEXT_MODEL_ENV_KEYS = [
|
export const EXTENDED_CONTEXT_MODEL_ENV_KEYS = [
|
||||||
|
|||||||
@@ -28,13 +28,16 @@ describe('Anthropic model env keys', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('extended-context model env keys', () => {
|
describe('extended-context model env keys', () => {
|
||||||
it('covers the Anthropic tiers plus the subagent model key', () => {
|
it('covers the Anthropic tiers plus the subagent and startup-default model keys', () => {
|
||||||
expect([...EXTENDED_CONTEXT_MODEL_ENV_KEYS]).toEqual([
|
expect([...EXTENDED_CONTEXT_MODEL_ENV_KEYS]).toEqual([
|
||||||
...ANTHROPIC_MODEL_ENV_KEYS,
|
...ANTHROPIC_MODEL_ENV_KEYS,
|
||||||
'CLAUDE_CODE_SUBAGENT_MODEL',
|
'CLAUDE_CODE_SUBAGENT_MODEL',
|
||||||
|
'ANTHROPIC_DEFAULT_MODEL',
|
||||||
]);
|
]);
|
||||||
expect(isExtendedContextModelEnvKey('CLAUDE_CODE_SUBAGENT_MODEL')).toBe(true);
|
expect(isExtendedContextModelEnvKey('CLAUDE_CODE_SUBAGENT_MODEL')).toBe(true);
|
||||||
|
expect(isExtendedContextModelEnvKey('ANTHROPIC_DEFAULT_MODEL')).toBe(true);
|
||||||
expect(isAnthropicModelEnvKey('CLAUDE_CODE_SUBAGENT_MODEL')).toBe(false);
|
expect(isAnthropicModelEnvKey('CLAUDE_CODE_SUBAGENT_MODEL')).toBe(false);
|
||||||
|
expect(isAnthropicModelEnvKey('ANTHROPIC_DEFAULT_MODEL')).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -64,6 +67,7 @@ describe('applyExtendedContextPreferenceToAnthropicModels', () => {
|
|||||||
const env = applyExtendedContextPreferenceToAnthropicModels(
|
const env = applyExtendedContextPreferenceToAnthropicModels(
|
||||||
{
|
{
|
||||||
ANTHROPIC_MODEL: 'claude-opus-5[1m]',
|
ANTHROPIC_MODEL: 'claude-opus-5[1m]',
|
||||||
|
ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5[1m]',
|
||||||
ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5-1[1m]',
|
ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5-1[1m]',
|
||||||
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-fable-5-1[1m]',
|
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-fable-5-1[1m]',
|
||||||
},
|
},
|
||||||
@@ -72,11 +76,21 @@ describe('applyExtendedContextPreferenceToAnthropicModels', () => {
|
|||||||
|
|
||||||
expect(env).toEqual({
|
expect(env).toEqual({
|
||||||
ANTHROPIC_MODEL: 'claude-opus-5',
|
ANTHROPIC_MODEL: 'claude-opus-5',
|
||||||
|
ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5',
|
||||||
ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5-1',
|
ANTHROPIC_DEFAULT_FABLE_MODEL: 'claude-fable-5-1',
|
||||||
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-fable-5-1',
|
CLAUDE_CODE_SUBAGENT_MODEL: 'claude-fable-5-1',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('suffixes the startup-default model key when the preference is on', () => {
|
||||||
|
const env = applyExtendedContextPreferenceToAnthropicModels(
|
||||||
|
{ ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5' },
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(env).toEqual({ ANTHROPIC_DEFAULT_MODEL: 'claude-opus-5[1m]' });
|
||||||
|
});
|
||||||
|
|
||||||
it('honors a caller compatibility predicate', () => {
|
it('honors a caller compatibility predicate', () => {
|
||||||
const env = applyExtendedContextPreferenceToAnthropicModels(
|
const env = applyExtendedContextPreferenceToAnthropicModels(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user