diff --git a/docs-site/src/content/docs/fr/guides/claude-code.md b/docs-site/src/content/docs/fr/guides/claude-code.md index 22f57ac1954..02b1641d58b 100644 --- a/docs-site/src/content/docs/fr/guides/claude-code.md +++ b/docs-site/src/content/docs/fr/guides/claude-code.md @@ -423,7 +423,7 @@ d'OpenCodex, de réappliquer ce profil et de démarrer une nouvelle conversation dépannage, sans garantie de résolution. OpenCodex ne peut pas observer l'état du sélecteur ; il achemine l'identifiant du modèle porté par chaque requête. Vérifiez ce que le client envoie sous **Logs → requestedModel**. -Les modèles dont la fenêtre de contexte de référence atteint 1M obtiennent une ligne supplémentaire `…[1m]` dans le sélecteur. +Les modèles dont la fenêtre de contexte de référence atteint 1M ou au moins le seuil de compactage par défaut (829 800) obtiennent une ligne supplémentaire `…[1m]` dans le sélecteur. Sa sélection indique à Claude Code la fenêtre complète de 1M pour ce modèle, tout en maintenant le compactage automatique ; le proxy retire le marqueur avant le routage. La sélection est conservée dans le champ `model` de `settings.json` ; pour les requêtes entrantes, l'alias est de nouveau @@ -458,8 +458,10 @@ conservent leurs identifiants canoniques sur les deux interfaces. ### Marqueur `[1m]` de variante contextuelle -Les modèles dont la fenêtre de contexte de référence atteint 1M — ou, avec le contexte automatique, dépasse 200k tout en atteignant -au moins le seuil de compactage — obtiennent une ligne supplémentaire `…[1m]` dans le sélecteur. En la sélectionnant, Claude Code +Les modèles dont la fenêtre de contexte de référence atteint 1M ou au moins le seuil de compactage par défaut (829 800) +obtiennent une ligne supplémentaire `…[1m]` dans le sélecteur. Ce plancher est fixe : modifier la valeur de compactage ne l'abaisse pas, +et un modèle Claude sur une route Anthropic doit réellement atteindre 1M. Sans contexte automatique, seule une fenêtre de 1M compte. +Au-delà de la fenêtre réelle, l'erreur `prompt is too long` déclenche le compactage automatique de Claude Code. En la sélectionnant, Claude Code tient compte d'un contexte complet de 1M. Le proxy supprime le suffixe `[1m]`, sans tenir compte de la casse, avant la résolution de l'alias et le routage. @@ -468,8 +470,9 @@ résolution de l'alias et le routage. Claude Code attribue une limite de 200k jetons à tout modèle qu'il ne reconnaît pas. Le **contexte automatique**, activé par défaut, corrige ce comportement : -1. Les modèles dont la fenêtre réelle dépasse 200k **et** atteint au moins le seuil de compactage automatique obtiennent le - marqueur `[1m]` dans les lignes du sélecteur et les variables d'environnement qui les désignent. +1. Les variables d'environnement de lancement reçoivent le marqueur `[1m]` lorsque la fenêtre réelle dépasse 200k **et** + atteint au moins le seuil de compactage automatique configuré. Les lignes de découverte et du sélecteur Desktop ne suivent pas + ce seuil : elles utilisent le plancher fixe de 829 800 jetons (1M réel pour un modèle Claude sur une route Anthropic). 2. `CLAUDE_CODE_AUTO_COMPACT_WINDOW` (`829800` par défaut, plage `100000`–`1000000`) est injecté afin que la conversation soit automatiquement résumée à ce seuil. @@ -482,8 +485,9 @@ Trois états de configuration : La valeur de compactage est réglable dans **Connexion → Claude**. **Avertissement :** une valeur supérieure à la fenêtre réelle d'un modèle rend ce modèle inutilisable : les tours échouent avant que le résumé puisse se déclencher. -Les modèles Anthropic natifs dont le contexte est inférieur à 1M ne sont jamais marqués automatiquement. Les valeurs que vous exportez vous-même -restent prioritaires ; le proxy s'appuie sur votre valeur pour déterminer les modèles qui peuvent recevoir le marqueur sans risque. +Les modèles Anthropic natifs dont le contexte est inférieur à 1M ne sont jamais marqués automatiquement. Une fenêtre de compactage que vous exportez vous-même +reste prioritaire pour le marquage des variables de lancement ; le proxy s'appuie sur votre valeur pour déterminer les modèles qui peuvent recevoir le marqueur sans risque. +Les lignes de découverte ignorent cet export et conservent le plancher fixe. Les valeurs de configuration invalides définies manuellement reviennent à 829,800. ### Environnement effectif des modèles @@ -744,7 +748,7 @@ une valeur `ANTHROPIC_BASE_URL` obsolète. Ouvrez un nouveau terminal ou réexé **Plafond de contexte 200k malgré un grand modèle** — Sélectionnez la variante `[1m]` dans le sélecteur ou activez le contexte automatique, activé par défaut. Si le sélecteur n'affiche aucune ligne `[1m]`, la fenêtre de contexte de référence du modèle -peut être inférieure au seuil de compactage automatique. +peut être inférieure au plancher fixe de 829 800 jetons. **Nombre élevé de jetons provenant du chargement des compétences** — La compétence `claude-api` fournie (~136k jetons) se charge automatiquement quand un modèle Claude est mentionné. Ce comportement est normal avec le transfert natif ; pour les modèles routés, opencodex la remplace @@ -768,7 +772,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/guides/claude-code.md b/docs-site/src/content/docs/guides/claude-code.md index ecdc703d3ce..9922dde3fa5 100644 --- a/docs-site/src/content/docs/guides/claude-code.md +++ b/docs-site/src/content/docs/guides/claude-code.md @@ -596,7 +596,8 @@ reapplying the profile, and starting a new conversation. This is a troubleshooti guaranteed fix. OpenCodex cannot observe picker state; it routes the model id carried by each request. Confirm what the client sends under **Logs → requestedModel**. -Models with an authoritative 1M context window get an extra `…[1m]` picker row: selecting it makes +Models with a long context window (1M, or at least the 829,800-token default compaction threshold; +Claude models on an Anthropic route need a genuine 1M) get an extra `…[1m]` picker row: selecting it makes Claude Code account a full 1M context for that model (auto-compaction stays on) — the proxy strips the marker before routing. Selecting one persists it to Claude Code's `settings.json` `model` field; inbound requests resolve @@ -632,8 +633,10 @@ keep their canonical ids on both surfaces. ### Context-variant `[1m]` marker -Models with an authoritative context window of 1M (or, under auto-context, above 200k and at -least the compaction threshold) get an extra `…[1m]` picker row. Selecting it makes Claude Code +Models with an authoritative context window of 1M, or at least the 829,800-token default +compaction threshold, get an extra `…[1m]` picker row; the floor is fixed, so a custom compaction +value does not lower it, and Claude models on an Anthropic route need a genuine 1M. Turning +auto-context off limits the row to 1M windows. Selecting it makes Claude Code account a full 1M context. The proxy strips the case-insensitive `[1m]` suffix before alias resolution and routing. @@ -642,8 +645,10 @@ resolution and routing. Claude Code accounts 200k tokens for any model it does not recognize. **Auto context** (on by default) fixes that: -1. Models whose real window is above 200k **and** at least the auto-compact threshold get the - `[1m]` marker on their picker rows and env slots. +1. Launch env slots get the `[1m]` marker when the model's real window is above 200k **and** at + least the configured auto-compact threshold. Discovery and Desktop picker rows do not follow + that threshold: they use the fixed 829,800-token floor (a genuine 1M for Claude models on an + Anthropic route). 2. `CLAUDE_CODE_AUTO_COMPACT_WINDOW` (default `829800`, range `100000`–`1000000`) is injected so the conversation auto-summarizes at that point. @@ -668,8 +673,9 @@ history and try compacting a fork with fewer enabled tool or MCP schemas, if you that workflow. A `[1m]` marker or larger client accounting setting does not raise the upstream limit, and OpenCodex does not silently remove history or tools to make it fit. -Sub-1M native Anthropic models are never auto-marked. Values you export yourself always win (the -proxy uses YOUR value to decide which models are safe to mark). Invalid hand-edited config values +Sub-1M native Anthropic models are never auto-marked. A compact window you export yourself wins +for launch-slot marking (the proxy uses YOUR value to decide which models are safe to mark). +Discovery rows ignore that export and keep the fixed floor. Invalid hand-edited config values fall back to 829,800. ### Effective model environment @@ -1001,7 +1007,7 @@ set (automatic with `ocx claude`). Run `ocx claude` to refresh the gateway model **200k context ceiling despite big model** — Select the `[1m]` variant in the picker, or enable auto-context (on by default). If the picker shows no `[1m]` row, the model's authoritative context -window may be below the auto-compact threshold. +window may be below the fixed 829,800-token floor. **High token count from skill loads** — The bundled `claude-api` skill (~136k tokens) auto-loads on Claude model mentions. This is normal for native passthrough; on routed models, opencodex stubs @@ -1059,7 +1065,7 @@ the confirmed obsolete token file and applying first-party mode again; never del ### First-party picker context markers -The Desktop Code-tab picker adds `[1m]` to routed models whose authoritative context window is at least one million tokens, so Claude uses its 1M accounting instead of the smaller custom-model fallback. Labels, profile order, and provider routes stay unchanged. Unknown and sub-million windows remain unmarked, including native long-window opt-ins: the picker cannot guarantee that a Desktop or remote runner receives the matching compaction environment. The paired auto-context setup for `ocx claude` is unchanged. An existing conversation keeps its saved selector until you select the model again from the refreshed picker. +The Desktop Code-tab picker adds `[1m]` to routed models whose authoritative context window is at least one million tokens or at least the 829,800-token default compaction threshold (for example a native GPT-6 model opted into its 872k window), so Claude uses its 1M accounting instead of the smaller custom-model fallback. Claude models on an Anthropic route still need a genuine 1M. Labels, profile order, and provider routes stay unchanged. Unknown and shorter windows remain unmarked. The picker cannot guarantee that a Desktop or remote runner receives the matching compaction environment, so a long-window model that outgrows its real window gets a `prompt is too long` error, which Claude Code answers by compacting. Desktop 3P `supports1m`/`prefer1m` and generated subagents follow the same rule. The paired auto-context setup for `ocx claude` is unchanged. An existing conversation keeps its saved selector until you select the model again from the refreshed picker. ### Forced Claude Code subagent model @@ -1067,7 +1073,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/ja/guides/claude-code.md b/docs-site/src/content/docs/ja/guides/claude-code.md index c6308ab24b8..c5690dc3a67 100644 --- a/docs-site/src/content/docs/ja/guides/claude-code.md +++ b/docs-site/src/content/docs/ja/guides/claude-code.md @@ -311,8 +311,9 @@ v2 エイリアスはエスケープを展開します。読みやすい形式 ### コンテキスト変種 `[1m]` 標識 -公式コンテキストウィンドウが 1M のモデルには `…[1m]` ピッカー行がもう一つできます。自動コンテキスト使用時は -コンテキストが 200k を超え、圧縮しきい値以上のモデルも該当します。この行を選ぶと Claude Code が +公式コンテキストウィンドウが 1M、またはデフォルトの圧縮しきい値(829,800)以上のモデルには `…[1m]` ピッカー行がもう一つできます。 +この基準は固定で、圧縮値を変えても下がりません。Anthropic 経路の Claude モデルは実際に 1M である必要があります。自動コンテキストをオフにすると +1M のモデルだけが対象です。実際のウィンドウを超えると `prompt is too long` エラーが返り、Claude Code が自動で圧縮します。この行を選ぶと Claude Code が 全体 1M コンテキストを計算します。プロキシはエイリアス解決とルーティング前に大文字小文字を区別せず `[1m]` 接尾辞を削除します。 @@ -321,8 +322,9 @@ v2 エイリアスはエスケープを展開します。読みやすい形式 Claude Code は未知モデルのコンテキストを 200k トークンとして計算します。デフォルトでオンの**自動 コンテキスト**がこの問題を解決します。 -1. 実際のコンテキストウィンドウが 200k より大きく自動圧縮しきい値以上のモデルのピッカー行と環境スロットに - `[1m]` 標識が付きます。 +1. 起動時の環境スロットには、実際のコンテキストウィンドウが 200k より大きく、設定した自動圧縮しきい値以上のとき + `[1m]` 標識が付きます。ディスカバリーと Desktop のピッカー行はこのしきい値に従わず、固定の 829,800 トークン下限 + (Anthropic 経路の Claude モデルは実際の 1M)を使います。 2. `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(デフォルト `829800`、範囲 `100000`–`1000000`)を注入し、該当 地点で会話を自動要約します。 @@ -335,7 +337,7 @@ Claude Code は未知モデルのコンテキストを 200k トークンとし **接続 → Claude** で圧縮値を調整できます。**警告:** モデルの実際のコンテキストウィンドウより大きく上げると 要約を開始する前にチャットエラーが発生します。 -1M 未満のネイティブ Anthropic モデルには自動で標識を付けません。直接 export した値が常に優先し、プロキシは**ユーザーが指定した**値を基準にどのモデルに安全に標識を付けるか決定します。 +1M 未満のネイティブ Anthropic モデルには自動で標識を付けません。自分で export した圧縮値は起動スロットの標識に優先し、プロキシは**ユーザーが指定した**値を基準にどのモデルに安全に標識を付けるか決定します。ディスカバリー行はその export を無視し、固定の下限を保ちます。 直接編集した設定値が不正な場合は 829,800 に戻ります。 ### 実モデル環境 @@ -603,7 +605,7 @@ Anthropic バックエンドを明示すると意図的に失敗後停止しま **大型モデルなのにコンテキストが 200k に制限される** — ピッカーで `[1m]` 変種を選ぶか、デフォルトでオンの 自動コンテキストを使ってください。ピッカーに `[1m]` 行がない場合はモデルの公式コンテキストウィンドウが -自動圧縮しきい値より小さい可能性があります。 +固定の 829,800 トークン下限より小さい可能性があります。 **スキル呼び出し時のトークン数が多い** — バンドル `claude-api` スキル(約 136k トークン)は Claude モデルに 言及すると自動で読み込まれます。ネイティブパススルーでは正常で、ルーティングモデルでは opencodex が @@ -623,7 +625,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/ko/guides/claude-code.md b/docs-site/src/content/docs/ko/guides/claude-code.md index 1de6c56ba53..91de7d969bd 100644 --- a/docs-site/src/content/docs/ko/guides/claude-code.md +++ b/docs-site/src/content/docs/ko/guides/claude-code.md @@ -283,7 +283,7 @@ import/export는 로컬 설정만 다뤄요. 허브 프로필을 바꾸지 않 능력 정보(추론 강도 사다리, thinking 타입)를 실어 보냅니다 — Claude Desktop의 서드파티 게이트웨이 모드가 추론 강도 선택 UI를 열 수 있게 하기 위해서입니다. 실제 Anthropic 모델은 원래 id를 그대로 유지합니다. 합성된 2026 날짜는 내부 슬롯이며 출시일이 아닙니다. 구버전의 -해시 별칭과 `claude-ocx---`, `claude-ocx2---` 별칭도 계속 해석됩니다. 저장된 `claude-ocx-`는 `ocx-claude-`로, 이스케이프된 `claude-ocx2-`는 `ocx-claude2-`로 한 번 다시 고르면 실제 컨텍스트 창과 compact가 함께 적용됩니다. 컨텍스트가 1M인 모델에는 +해시 별칭과 `claude-ocx---`, `claude-ocx2---` 별칭도 계속 해석됩니다. 저장된 `claude-ocx-`는 `ocx-claude-`로, 이스케이프된 `claude-ocx2-`는 `ocx-claude2-`로 한 번 다시 고르면 실제 컨텍스트 창과 compact가 함께 적용됩니다. 컨텍스트가 1M이거나 기본 압축 임곗값(829,800) 이상인 모델에는 `…[1m]` 행이 하나 더 생깁니다 — 이걸 고르면 Claude Code가 그 모델의 컨텍스트를 1M로 계산합니다 (자동 요약 유지, 프록시가 표식을 떼고 라우팅). 선택하면 Claude Code의 `settings.json` `model` 필드에 저장되고, 인바운드 요청에서 @@ -343,8 +343,9 @@ v2 별칭은 이스케이프를 펼쳐요. 읽기 쉬운 형식으로 표현할 ### 컨텍스트 변형 `[1m]` 표식 -공식 컨텍스트 창이 1M인 모델에는 `…[1m]` 선택기 행이 하나 더 생겨요. 자동 컨텍스트를 사용할 -때는 컨텍스트가 200k를 넘고 압축 임곗값 이상인 모델도 해당해요. 이 행을 선택하면 Claude Code가 +공식 컨텍스트 창이 1M이거나 기본 압축 임곗값(829,800) 이상인 모델에는 `…[1m]` 선택기 행이 하나 더 +생겨요. 이 기준은 고정이라 압축 값을 직접 바꿔도 낮아지지 않고, Anthropic 경로의 Claude 모델은 실제 1M이어야 +해요. 자동 컨텍스트를 끄면 1M 모델만 해당해요. 실제 창을 넘으면 `prompt is too long` 오류가 오고 Claude Code가 자동으로 압축해요. 이 행을 선택하면 Claude Code가 전체 1M 컨텍스트를 계산해요. 프록시는 별칭 해석과 라우팅 전에 대소문자를 구분하지 않고 `[1m]` 접미사를 제거해요. @@ -353,8 +354,9 @@ v2 별칭은 이스케이프를 펼쳐요. 읽기 쉬운 형식으로 표현할 Claude Code는 알 수 없는 모델의 컨텍스트를 200k 토큰으로 계산해요. 기본으로 켜져 있는 **자동 컨텍스트**는 이 문제를 해결해요. -1. 실제 컨텍스트 창이 200k보다 크고 자동 압축 임곗값 이상인 모델의 선택기 행과 환경 슬롯에 - `[1m]` 표식이 붙어요. +1. 실행 환경 슬롯에는 실제 컨텍스트 창이 200k보다 크고 설정한 자동 압축 임곗값 이상일 때 + `[1m]` 표식이 붙어요. 디스커버리와 Desktop 선택기 행은 그 임곗값을 따르지 않고, 고정된 829,800 토큰 하한 + (Anthropic 경로의 Claude 모델은 실제 1M)을 써요. 2. `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(기본값 `829800`, 범위 `100000`–`1000000`)를 주입해 해당 지점에서 대화를 자동으로 요약해요. @@ -367,8 +369,9 @@ Claude Code는 알 수 없는 모델의 컨텍스트를 200k 토큰으로 계산 **연결 → Claude**에서 압축 값을 조절할 수 있어요. **경고:** 모델의 실제 컨텍스트 창보다 크게 올리면 요약을 시작하기 전에 채팅 오류가 발생해요. -1M 미만인 네이티브 Anthropic 모델에는 자동으로 표식을 붙이지 않아요. 직접 내보낸 값이 항상 +1M 미만인 네이티브 Anthropic 모델에는 자동으로 표식을 붙이지 않아요. 직접 내보낸 압축 값은 실행 슬롯 표식에서 항상 우선하며, 프록시는 **사용자가 지정한** 값을 기준으로 어떤 모델에 안전하게 표식을 붙일지 결정해요. +디스커버리 행은 그 값을 무시하고 고정 하한을 유지해요. 직접 편집한 설정값이 잘못되면 829,800로 돌아가요. ### 실제 모델 환경 @@ -666,7 +669,7 @@ Anthropic 백엔드를 명시하면 의도적으로 실패 후 중단해요. **대형 모델인데도 컨텍스트가 200k로 제한됨** — 선택기에서 `[1m]` 변형을 고르거나 기본으로 켜져 있는 자동 컨텍스트를 사용하세요. 선택기에 `[1m]` 행이 없다면 모델의 공식 컨텍스트 창이 -자동 압축 임곗값보다 작을 수 있어요. +고정된 829,800 토큰 하한보다 작을 수 있어요. **스킬을 불러올 때 토큰 수가 많음** — 번들 `claude-api` 스킬(약 136k 토큰)은 Claude 모델을 언급하면 자동으로 불러와요. 네이티브 패스스루에서는 정상이며, 라우팅 모델에서는 opencodex가 @@ -686,7 +689,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/ru/guides/claude-code.md b/docs-site/src/content/docs/ru/guides/claude-code.md index 6bb458df309..dce282cfcc9 100644 --- a/docs-site/src/content/docs/ru/guides/claude-code.md +++ b/docs-site/src/content/docs/ru/guides/claude-code.md @@ -330,8 +330,10 @@ Anthropic сохраняют канонические id в обоих инте ### Маркер контекстного варианта `[1m]` -Модели с подтверждённым контекстным окном 1M (или, при автоконтексте, больше 200k и не ниже -порога сжатия) получают дополнительную строку `…[1m]` в селекторе. Её выбор заставляет Claude Code +Модели с подтверждённым контекстным окном 1M или не ниже порога сжатия по умолчанию (829 800) +получают дополнительную строку `…[1m]` в селекторе. Этот порог фиксирован: изменение значения сжатия его +не понижает, а модель Claude на маршруте Anthropic должна действительно иметь 1M. Без автоконтекста учитываются +только окна 1M. При выходе за реальное окно приходит ошибка `prompt is too long`, и Claude Code сам сжимает контекст. Её выбор заставляет Claude Code учитывать полный контекст 1M. Прокси удаляет суффикс `[1m]` (без учёта регистра) до разрешения алиасов и маршрутизации. @@ -340,8 +342,9 @@ Anthropic сохраняют канонические id в обоих инте Для любой незнакомой модели Claude Code считает контекст равным 200k токенов. **Автоконтекст** (включён по умолчанию) исправляет это: -1. Модели, чьё реальное окно больше 200k **и** не меньше порога автосжатия, получают маркер - `[1m]` в строках селектора и слотах окружения. +1. Слоты окружения при запуске получают маркер `[1m]`, когда реальное окно больше 200k **и** не меньше + настроенного порога автосжатия. Строки обнаружения и селектора Desktop этому порогу не следуют: + для них действует фиксированный пол 829 800 токенов (настоящий 1M для моделей Claude на маршруте Anthropic). 2. Внедряется `CLAUDE_CODE_AUTO_COMPACT_WINDOW` (по умолчанию `829800`, диапазон `100000`–`1000000`), чтобы в этой точке диалог автоматически резюмировался. @@ -355,9 +358,9 @@ Anthropic сохраняют канонические id в обоих инте реального окна модели, эта модель ломается — чат завершится ошибкой раньше, чем успеет сработать резюмирование. -Нативные модели Anthropic с окном меньше 1M никогда не помечаются автоматически. Экспортированные -вами значения всегда имеют приоритет (прокси использует ВАШЕ значение, чтобы решить, какие модели -можно безопасно пометить). Некорректные значения, внесённые в конфигурацию вручную, откатываются +Нативные модели Anthropic с окном меньше 1M никогда не помечаются автоматически. Экспортированное +вами окно сжатия имеет приоритет при пометке слотов запуска (прокси использует ВАШЕ значение, чтобы решить, какие модели +можно безопасно пометить). Строки обнаружения игнорируют этот экспорт и сохраняют фиксированный пол. Некорректные значения, внесённые в конфигурацию вручную, откатываются к 829,800. ### Эффективное окружение моделей @@ -637,7 +640,7 @@ Responses `web_search_call` в парные блоки Anthropic `server_tool_us **Потолок контекста 200k, хотя модель больше** — выберите в селекторе вариант `[1m]` или включите автоконтекст (включён по умолчанию). Если строки `[1m]` в селекторе нет, подтверждённое -контекстное окно модели может быть меньше порога автосжатия. +контекстное окно модели может быть меньше фиксированного пола в 829 800 токенов. **Большой расход токенов при загрузке скиллов** — встроенный скилл `claude-api` (~136k токенов) автоматически загружается при упоминании моделей Claude. Для нативного проброса это нормально; @@ -658,7 +661,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/tr/guides/claude-code.md b/docs-site/src/content/docs/tr/guides/claude-code.md index b0015531c14..1a99421d774 100644 --- a/docs-site/src/content/docs/tr/guides/claude-code.md +++ b/docs-site/src/content/docs/tr/guides/claude-code.md @@ -470,10 +470,11 @@ giderme adımıdır; kesin çözüm değildir. OpenCodex seçici durumunu gözle her isteğin taşıdığı model kimliğini yönlendirir. İstemcinin ne gönderdiğini **Logs → requestedModel** altında kontrol edin. -Yetkili 1M bağlam penceresine sahip modeller fazladan bir `…[1m]` seçici satırı -alır: bunu seçmek Claude Code'un bu model için tam 1M bağlam hesabı yapmasını -sağlar (otomatik sıkıştırma açık kalır) — proxy yönlendirmeden önce işaretçiyi -kaldırır. +Uzun bağlam penceresine sahip modeller (1M, veya en az 829.800 token varsayılan +sıkıştırma eşiği; Anthropic rotasındaki Claude modellerinin gerçekten 1M olması +gerekir) fazladan bir `…[1m]` seçici satırı alır: bunu seçmek Claude Code'un bu +model için tam 1M bağlam hesabı yapmasını sağlar (otomatik sıkıştırma açık kalır) +— proxy yönlendirmeden önce işaretçiyi kaldırır. Birini seçmek, onu Claude Code'un `settings.json` `model` alanına kaydeder; gelen istekler takma adı yönlendirilen modele geri çözer. Eski Claude Code sürümlerinde seçici yerel kalır — `ANTHROPIC_MODEL` aracılığıyla yuvaları @@ -512,10 +513,12 @@ kimliklerini korur. ### Bağlam değişkeni `[1m]` işaretçisi -Yetkili bağlam penceresi 1M olan (veya otomatik bağlam altında 200k üzerinde ve -en az sıkıştırma eşiğinde olan) modeller fazladan bir `…[1m]` seçici satırı -alır. Bunu seçmek Claude Code'un tam 1M bağlam hesabı yapmasını sağlar. Proxy, -takma ad çözümleme ve yönlendirmeden önce büyük/küçük harfe duyarsız `[1m]` +Yetkili bağlam penceresi 1M olan veya varsayılan sıkıştırma eşiği olan 829.800 +tokene ulaşan modeller fazladan bir `…[1m]` seçici satırı alır. Bu taban sabittir: +sıkıştırma değerini değiştirmek onu düşürmez ve Anthropic rotasındaki Claude +modellerinin gerçekten 1M olması gerekir. Otomatik bağlam kapatılırsa satır yalnızca +1M pencerelere kalır. Bunu seçmek Claude Code'un tam 1M bağlam hesabı yapmasını sağlar. +Proxy, takma ad çözümleme ve yönlendirmeden önce büyük/küçük harfe duyarsız `[1m]` sonekini kaldırır. ## Otomatik bağlam (200k tavanı olmayan büyük bağlamlı modeller) @@ -523,9 +526,10 @@ sonekini kaldırır. Claude Code, tanımadığı herhangi bir model için 200k token hesabı yapar. **Otomatik bağlam** (varsayılan olarak açık) bunu düzeltir: -1. Gerçek penceresi 200k'nın üzerinde **ve** en az otomatik sıkıştırma eşiğinde - olan modeller, seçici satırlarında ve ortam yuvalarında `[1m]` işaretçisini - alır. +1. Başlatma ortam yuvaları, gerçek pencere 200k'nın üzerinde **ve** en az yapılandırılmış + otomatik sıkıştırma eşiğindeyken `[1m]` işaretçisini alır. Keşif ve Desktop seçici + satırları bu eşiği izlemez: sabit 829.800 token tabanını kullanırlar (Anthropic + rotasındaki Claude modelleri için gerçek 1M). 2. Görüşmenin bu noktada otomatik olarak özetlenmesi için `CLAUDE_CODE_AUTO_COMPACT_WINDOW` (varsayılan `829800`, aralık `100000`–`1000000`) enjekte edilir. @@ -542,8 +546,9 @@ penceresinin üzerine çıkarmak o modeli bozar — sohbet özetleme tetiklenmed önce hata verir. 1M altı yerel Anthropic modelleri hiçbir zaman otomatik olarak işaretlenmez. -Kendi dışa aktardığınız değerler her zaman kazanır (proxy hangi modellerin +Kendi dışa aktardığınız sıkıştırma penceresi, başlatma yuvası işaretlemesinde her zaman kazanır (proxy hangi modellerin işaretlenmesinin güvenli olduğuna karar vermek için SİZİN değerinizi kullanır). +Keşif satırları bu dışa aktarımı yok sayar ve sabit tabanı korur. Geçersiz elle düzenlenen yapılandırma değerleri 829,800'ya geri döner. ### Geçerli model ortamı (Effective model environment) @@ -852,8 +857,8 @@ veya `ocx claude` komutunu yeniden çalıştırın. **Büyük modele rağmen 200k bağlam tavanı** — Seçicide `[1m]` varyantını seçin veya otomatik bağlamı etkinleştirin (varsayılan olarak açık). Seçici hiçbir -`[1m]` satırı göstermiyorsa, modelin yetkili bağlam penceresi otomatik -sıkıştırma eşiğinin altında olabilir. +`[1m]` satırı göstermiyorsa, modelin yetkili bağlam penceresi sabit 829.800 +token tabanının altında olabilir. **Yetenek yüklemelerinden kaynaklanan yüksek token sayısı** — Paketlenmiş `claude-api` yeteneği (~136k token) Claude model adları anıldığında otomatik @@ -880,7 +885,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/zh-cn/guides/claude-code.md b/docs-site/src/content/docs/zh-cn/guides/claude-code.md index aafb1f310a7..9f791d54db0 100644 --- a/docs-site/src/content/docs/zh-cn/guides/claude-code.md +++ b/docs-site/src/content/docs/zh-cn/guides/claude-code.md @@ -239,7 +239,7 @@ UI。真实 Anthropic 模型保留其原始 id。合成的 2026 日期是内部 别名和 `claude-ocx---` 别名仍可解析,转义的 `claude-ocx2---` 也同样可解析。 已保存的旧 id 仍会路由,但 Claude Code 对它仍按 200k 计算。把已保存的 `claude-ocx-` 重新选一次对应的 `ocx-claude-`,转义的 `claude-ocx2-` 重新选一次 `ocx-claude2-`,即可同时用上真实上下文窗口和 compact。 -拥有 1M 上下文的模型会多出一行 `…[1m]`: +上下文为 1M 或不低于默认压缩阈值(829,800)的模型会多出一行 `…[1m]`: 选中后 Claude Code 会按 1M 计算该模型的上下文(自动压缩保留,代理在路由前去掉该标记)。 选中后会保存到 Claude Code 的 `settings.json` `model` 字段;入站请求会将别名解析回路由 模型。旧版 Claude Code 中选择器保持原生 — 通过 `ANTHROPIC_MODEL` 设置槽位,或直接在 `/model` @@ -293,8 +293,9 @@ v1 别名按字面解码(历史上 model ID 中包含的两字符序列 `~s` / ### 上下文变体 `[1m]` 标记 -权威上下文窗口为 1M 的模型(或者启用自动上下文时,窗口大于 200k 且至少达到压缩阈值的模型) -会多出一个带 `…[1m]` 的选择器条目。选择它后,Claude Code 会按完整的 1M 上下文计算。 +权威上下文窗口为 1M 或不低于默认压缩阈值(829,800)的模型会多出一个带 `…[1m]` 的选择器条目。 +该下限是固定的,修改压缩值不会降低它;Anthropic 路由上的 Claude 模型必须真正达到 1M;关闭自动上下文后只对 1M 模型生效。 +超出真实窗口时会返回 `prompt is too long` 错误,Claude Code 会自动压缩。选择它后,Claude Code 会按完整的 1M 上下文计算。 代理会在进行别名解析和路由之前移除不区分大小写的 `[1m]` 后缀。 ## 自动上下文(突破 200k 上限的大上下文模型) @@ -302,8 +303,8 @@ v1 别名按字面解码(历史上 model ID 中包含的两字符序列 `~s` / 对于任何无法识别的模型,Claude Code 都会按 200k token 计算。默认开启的**自动上下文**可解决 这一问题: -1. 实际窗口大于 200k **且**至少达到自动压缩阈值的模型,其选择器条目和环境变量槽位会带有 - `[1m]` 标记。 +1. 启动环境槽位会在实际窗口大于 200k **且**至少达到所配置的自动压缩阈值时带上 + `[1m]` 标记。发现列表和 Desktop 选择器条目不跟随该阈值:它们使用固定的 829,800 token 下限(Anthropic 路由上的 Claude 模型必须真正达到 1M)。 2. 系统会注入 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(默认 `829800`,范围 `100000`–`1000000`), 使对话在该位置自动进行摘要。 @@ -316,8 +317,8 @@ v1 别名按字面解码(历史上 model ID 中包含的两字符序列 `~s` / 可以在 Claude 页面调整压缩值。**警告:**如果将其提高到超过模型的实际窗口,该模型将无法正常 工作——聊天会在触发摘要之前报错。 -低于 1M 的原生 Anthropic 模型绝不会被自动标记。你自行导出的值始终优先(代理会使用**你的** -值来判断哪些模型可以安全标记)。手动编辑配置时填入的无效值会回退到 829,800。 +低于 1M 的原生 Anthropic 模型绝不会被自动标记。你自行导出的压缩值在启动槽位标记上始终优先(代理会使用**你的** +值来判断哪些模型可以安全标记)。发现列表忽略该导出值并保持固定下限。手动编辑配置时填入的无效值会回退到 829,800。 ### 有效模型环境变量 @@ -566,7 +567,7 @@ OAuth 活动账户未标记 `needsReauth`。显式选择 Anthropic 却没有可 `ANTHROPIC_BASE_URL` 可能已经过时。请打开一个新终端,或重新运行 `ocx claude`。 **大模型仍受 200k 上下文上限限制**——在选择器中选择 `[1m]` 变体,或启用自动上下文 -(默认开启)。如果选择器中没有 `[1m]` 条目,该模型的权威上下文窗口可能低于自动压缩阈值。 +(默认开启)。如果选择器中没有 `[1m]` 条目,该模型的权威上下文窗口可能低于固定的 829,800 token 下限。 **技能加载导致 token 数量过高**——内置的 `claude-api` 技能(约 136k token)会在提及 Claude 模型时自动加载。对于原生透传,这是正常现象;对于已路由模型,opencodex 默认会将其 @@ -581,7 +582,7 @@ Claude 模型时自动加载。对于原生透传,这是正常现象;对于 ### 第一方模型选择器的上下文标记 -对于权威上下文窗口至少为一百万 token 的路由模型,Desktop Code 标签页的模型选择器会添加 `[1m]`,使 Claude 按 1M 窗口计量,而不是使用自定义模型较小的默认窗口。标签、配置中的顺序和提供方路由保持不变。窗口未知或小于一百万 token 的模型(包括原生模型的大窗口选项)不添加标记,因为选择器无法保证 Desktop 或远程运行器收到配套的压缩环境设置。`ocx claude` 的自动上下文与压缩配套设置保持不变。现有对话会保留已保存的选择器,直到你在刷新后的模型选择器中重新选择该模型。 +对于权威上下文窗口至少为一百万 token,或不低于默认压缩阈值 829,800(例如选择了 872k 窗口的原生 GPT-6 模型)的路由模型,Desktop Code 标签页的模型选择器会添加 `[1m]`,使 Claude 按 1M 窗口计量,而不是使用自定义模型较小的默认窗口。Anthropic 路由上的 Claude 模型仍必须真正达到 1M。标签、配置中的顺序和提供方路由保持不变。窗口未知或更短的模型不添加标记。选择器无法保证 Desktop 或远程运行器收到配套的压缩环境设置,因此超出真实窗口的长窗口模型会收到 `prompt is too long` 错误,Claude Code 会据此压缩。Desktop 3P 的 `supports1m`/`prefer1m` 和生成的子代理遵循同一规则。`ocx claude` 的自动上下文与压缩配套设置保持不变。现有对话会保留已保存的选择器,直到你在刷新后的模型选择器中重新选择该模型。 ### Forced Claude Code subagent model @@ -589,7 +590,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/docs-site/src/content/docs/zh-tw/guides/claude-code.md b/docs-site/src/content/docs/zh-tw/guides/claude-code.md index c6ec3d79339..4f033558616 100644 --- a/docs-site/src/content/docs/zh-tw/guides/claude-code.md +++ b/docs-site/src/content/docs/zh-tw/guides/claude-code.md @@ -339,7 +339,7 @@ user-agent 會獲得易讀的 CLI 形式,其他用戶端會獲得 Desktop 代 也同樣可解析。已儲存的舊 id 仍會路由,但 Claude Code 對它仍按 200k 計算。把已儲存的 `claude-ocx-` 重新選一次對應的 `ocx-claude-`,跳脫的 `claude-ocx2-` 重新選一次 `ocx-claude2-`,即可同時用上真實上下文 視窗與 compact。 -擁有權威 1M 上下文視窗的模型會多出一個 `…[1m]` 選擇器列:選中後 Claude Code 會按完整 1M 上下文 +權威上下文視窗為 1M 或不低於預設壓縮閾值(829,800)的模型會多出一個 `…[1m]` 選擇器列:選中後 Claude Code 會按完整 1M 上下文 計算該模型(自動壓縮仍開啟)——代理在路由前會去掉該標記。 選中後會儲存到 Claude Code 的 `settings.json` `model` 欄位;入站請求會將別名解析回路由 模型。在較舊的 Claude Code 版本中,選擇器保持原生——可透過 `ANTHROPIC_MODEL` 設定槽位,或在 @@ -369,8 +369,9 @@ v1 別名按字面解碼(歷史上 model ID 中包含的兩字元序列 `~s` / ### 上下文變體 `[1m]` 標記 -權威上下文視窗為 1M 的模型(或者啟用自動上下文時,視窗大於 200k 且至少達到壓縮閾值的模型) -會多出一個帶 `…[1m]` 的選擇器條目。選擇它後,Claude Code 會按完整的 1M 上下文計算。 +權威上下文視窗為 1M 或不低於預設壓縮閾值(829,800)的模型會多出一個帶 `…[1m]` 的選擇器條目。 +該下限是固定的,修改壓縮值不會降低它;Anthropic 路由上的 Claude 模型必須真正達到 1M;關閉自動上下文後只對 1M 模型生效。 +超出真實視窗時會回傳 `prompt is too long` 錯誤,Claude Code 會自動壓縮。選擇它後,Claude Code 會按完整的 1M 上下文計算。 代理會在進行別名解析和路由之前移除不區分大小寫的 `[1m]` 字尾。 ## 自動上下文(突破 200k 上限的大上下文模型) @@ -378,8 +379,8 @@ v1 別名按字面解碼(歷史上 model ID 中包含的兩字元序列 `~s` / 對於任何無法識別的模型,Claude Code 都會按 200k token 計算。預設開啟的**自動上下文**可解決 這一問題: -1. 實際視窗大於 200k **且**至少達到自動壓縮閾值的模型,其選擇器條目和環境變數槽位會帶有 - `[1m]` 標記。 +1. 啟動環境槽位會在實際視窗大於 200k **且**至少達到所設定的自動壓縮閾值時帶上 + `[1m]` 標記。探索列表和 Desktop 選擇器條目不跟隨該閾值:它們使用固定的 829,800 token 下限(Anthropic 路由上的 Claude 模型必須真正達到 1M)。 2. 系統會注入 `CLAUDE_CODE_AUTO_COMPACT_WINDOW`(預設 `829800`,範圍 `100000`–`1000000`), 使對話在該位置自動進行摘要。 @@ -392,8 +393,8 @@ v1 別名按字面解碼(歷史上 model ID 中包含的兩字元序列 `~s` / 可以在 Claude 頁面調整壓縮值。**警告:**如果將其提高到超過模型的實際視窗,該模型將無法正常 工作——聊天會在觸發摘要之前報錯。 -低於 1M 的原生 Anthropic 模型絕不會被自動標記。你自行匯出的值始終優先(代理會使用**你的** -值來判斷哪些模型可以安全標記)。手動編輯設定時填入的無效值會回退到 829,800。 +低於 1M 的原生 Anthropic 模型絕不會被自動標記。你自行匯出的壓縮值在啟動槽位標記上始終優先(代理會使用**你的** +值來判斷哪些模型可以安全標記)。探索列表忽略該匯出值並保持固定下限。手動編輯設定時填入的無效值會回退到 829,800。 ### 有效模型環境變數 @@ -642,7 +643,7 @@ OAuth 活動帳號未標記 `needsReauth`。顯式選擇 Anthropic 卻沒有可 `ANTHROPIC_BASE_URL` 可能已經過時。請開啟一個新終端,或重新執行 `ocx claude`。 **大模型仍受 200k 上下文上限限制**——在選擇器中選擇 `[1m]` 變體,或啟用自動上下文 -(預設開啟)。如果選擇器中沒有 `[1m]` 條目,該模型的權威上下文視窗可能低於自動壓縮閾值。 +(預設開啟)。如果選擇器中沒有 `[1m]` 條目,該模型的權威上下文視窗可能低於固定的 829,800 token 下限。 **技能載入導致 token 數量過高**——內建的 `claude-api` 技能(約 136k token)會在提及 Claude 模型時自動載入。對於原生透傳,這是正常現象;對於已路由模型,opencodex 預設會將其 @@ -665,7 +666,7 @@ The Subagents page offers **Force all subagents onto one model**, off by default `ocx agent subagents force combo/tev-auto` sets `claudeCode.subagentModelForce`; `ocx agent subagents force -` clears it. `ocx agent status` reports the setting. `GET /api/subagent-models` returns `force`, `forceAvailable`, and `forceStatus`; `PUT` accepts `{ "force": "combo/tev-auto" }` or `{ "force": null }` without changing the roster. Omitting `force` leaves it unchanged. Invalid or unexposed targets are rejected on write; stale targets are reported and skipped at launch. -This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` only for an authoritative million-token window; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. +This takes effect on the **next routed `ocx claude` launch**, injecting `CLAUDE_CODE_SUBAGENT_MODEL` as an explicit proxy alias (with `[1m]` when the authoritative window reaches the fixed 829,800-token floor, and only for a genuine 1M window on Anthropic Claude models and bare `claude-*` selectors; native Claude targets use a reversible native alias) and `CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1`. Each nonempty shell-exported variable independently wins. Native launches inject neither variable; plain `claude` is not affected. No plugin files or `settings.json` are modified by this setting. Claude Code **2.1.257 or newer** is required for FORCE. Plugin and built-in agents (including Explore/Plan) and per-call model arguments are overridden. Forks and subagent skills with `model: inherit` keep the main conversation model. The main loop and Haiku/small-fast sidecars are unaffected. Existing roster files remain available. diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index adb7db7b2b6..9e9d2939b1d 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -418,7 +418,7 @@ "messages-native-decline-trace.test.ts": "claude-integration", "messages-native-oauth.test.ts": "claude-integration", "messages-native-opaque-state.test.ts": "claude-integration", - "messages-surface-matrix.test.ts": "claude-integration", + "messages-surface-matrix.test.ts": "claude-integration", "claude-long-context-eligibility.test.ts": "claude-integration", "claude-model-info.test.ts": "claude-integration", "claude-models-discovery.test.ts": "claude-integration", "claude-native-affinity.test.ts": "claude-integration", diff --git a/src/claude/context-windows.ts b/src/claude/context-windows.ts index 858f62d1818..5c3caeda824 100644 --- a/src/claude/context-windows.ts +++ b/src/claude/context-windows.ts @@ -15,8 +15,9 @@ import { desktop3pAlias } from "./desktop-3p"; import { nativeOpenAiContextWindow, type CatalogModel, type NativeContextLimitsInput } from "../codex/catalog"; import { ANTHROPIC_MODEL_CONTEXT_WINDOWS } from "../providers/registry/model-seeds"; import type { OcxClaudeCodeConfig } from "../types"; +import { AUTO_COMPACT_WINDOW_DEFAULT, AUTO_CONTEXT_FLOOR, ONE_MILLION } from "./long-context"; -const ONE_MILLION = 1_000_000; +export { AUTO_COMPACT_WINDOW_DEFAULT, AUTO_CONTEXT_FLOOR } from "./long-context"; /** * The native id each Claude Code tier alias resolves to (2.1.282: `--model opus` sends @@ -27,21 +28,6 @@ const ONE_MILLION = 1_000_000; */ const CLAUDE_CODE_NATIVE_TIERS = { opus: "claude-opus-5-5", sonnet: "claude-sonnet-5", fable: "claude-fable-5-1" } as const; -/** - * Auto-context defaults (devlog 260712 020, user-approved). - * - * The compact window is the token count at which Claude Code starts compacting, and it is - * also the floor `shouldMarkOneMillion` uses — a model may only carry the marker if it can - * host this window. 350,000 was chosen when the widest native row advertised 372,000. - * - * It now matches the auto-compaction limit the Codex catalog ships for the same models - * (`nativeAutoCompactLimit`: 829,800 against the 922,000 native window). Leaving the two - * apart meant one model compacting at 350k under Claude Code and at 829,800 under Codex. - * The value stays clear of the measured 922,000 ceiling by ~92k, so compaction still has - * room to run before the upstream refuses. - */ -export const AUTO_COMPACT_WINDOW_DEFAULT = 829_800; -export const AUTO_CONTEXT_FLOOR = 200_000; /** Binary-verified accepted range for CLAUDE_CODE_AUTO_COMPACT_WINDOW (2.1.207: pSo=1e5, yDs=1e6). */ export const AUTO_COMPACT_WINDOW_MIN = 100_000; export const AUTO_COMPACT_WINDOW_MAX = ONE_MILLION; @@ -63,6 +49,13 @@ export interface AutoContextMode { export const AUTO_CONTEXT_OFF: AutoContextMode = { enabled: false, compactWindow: AUTO_COMPACT_WINDOW_DEFAULT }; +/** + * Marking mode for surfaces whose runner may not inherit the compaction env (Desktop pickers, + * discovery, generated subagents). shouldMarkOneMillion under it equals isLongContextWindow + * (long-context.ts); kept as a mode so the existing marker helpers serve every surface. + */ +export const UNPAIRED_AUTO_CONTEXT: AutoContextMode = { enabled: true, compactWindow: AUTO_COMPACT_WINDOW_DEFAULT }; + interface AutoContextConfigSlice { autoContext?: boolean; autoCompactWindow?: number; diff --git a/src/claude/desktop-3p.ts b/src/claude/desktop-3p.ts index e2f0eec30e6..26eb3fbfb2c 100644 --- a/src/claude/desktop-3p.ts +++ b/src/claude/desktop-3p.ts @@ -25,6 +25,7 @@ import { import { nativeOpenAiContextWindow, type NativeContextLimitsInput } from "../codex/catalog"; import { localAdmissionToken, localInferenceDestination } from "../lib/local-destinations"; import { assertDesktop3pModelsValid } from "./desktop-3p-guard"; +import { claudeSurfaceSupportsOneMillion, ONE_MILLION } from "./long-context"; export interface Desktop3pModelEntry { name: string; @@ -56,12 +57,12 @@ export interface Desktop3pRoutedModel { } /** - * 1M-context eligibility, shared with the Desktop DTO so the dashboard's 1M chip can - * never disagree with what the writer emits. The DTO imports this from here — keeping - * the constant in this module avoids a cycle, since shared.ts already reads - * claude/desktop-profile. + * The genuine-1M threshold real Anthropic rows still need. Every other row follows the + * long-window rule; the writer, the profile renderer and the dashboard DTO all decide through + * `claudeSurfaceSupportsOneMillion` (long-context.ts), so the 1M chip cannot disagree with the + * written config. */ -export const DESKTOP_SUPPORTS_1M_THRESHOLD = 1_000_000; +export const DESKTOP_SUPPORTS_1M_THRESHOLD = ONE_MILLION; /** CLI arg parsing for `ocx claude desktop` mode flags (mutually exclusive). */ export function parseDesktop3pModeArgs(flags: string[]): { mode: Desktop3pConfigMode } | { error: string } { @@ -252,7 +253,7 @@ function collectDesktop3pModels( for (const { provider, id, contextWindow } of candidates) { const route = `${provider}/${id}`; const alias = desktop3pAlias(provider, id); - const supports1m = typeof contextWindow === "number" && contextWindow >= DESKTOP_SUPPORTS_1M_THRESHOLD + const supports1m = claudeSurfaceSupportsOneMillion(provider, id, contextWindow) ? { supports1m: true as const } : {}; if (alias === id) { diff --git a/src/claude/desktop-profile.ts b/src/claude/desktop-profile.ts index 9f617f59181..f160cda9430 100644 --- a/src/claude/desktop-profile.ts +++ b/src/claude/desktop-profile.ts @@ -1,4 +1,5 @@ import { createHash } from "node:crypto"; +import { routeSupportsOneMillion } from "./long-context"; import type { OcxClaudeDesktopAssignment, OcxClaudeDesktopFamily, @@ -359,7 +360,7 @@ export function renderDesktopProfile( name: isRealAnthropicRoute(route) ? assignment.alias : desktopProfileWireAlias(assignment.alias), family: assignment.family, isFamilyDefault: effectiveDefaults[assignment.family] === route, - supports1m: typeof model.contextWindow === "number" && model.contextWindow >= 1_000_000, + supports1m: routeSupportsOneMillion(route, model.contextWindow), }; }); } diff --git a/src/claude/intercept/picker-models.ts b/src/claude/intercept/picker-models.ts index 79c85ca8f39..65d3ca93e13 100644 --- a/src/claude/intercept/picker-models.ts +++ b/src/claude/intercept/picker-models.ts @@ -7,7 +7,8 @@ import { dirname } from "node:path"; import { nativeOpenAiContextWindow, type NativeContextLimitsInput } from "../../codex/catalog"; import type { OcxClaudeDesktopProfile } from "../../types"; import { aliasForRoute, claudeCodeNativeAlias } from "../alias"; -import { AUTO_CONTEXT_OFF, stripOneMillionMarker, withOneMillionMarker } from "../context-windows"; +import { AUTO_CONTEXT_OFF, stripOneMillionMarker, UNPAIRED_AUTO_CONTEXT, withOneMillionMarker } from "../context-windows"; +import { isAnthropicClaudeRoute } from "../long-context"; import { activeDesktop3pAlias, displayModelId, resolveDesktop3pAlias, type Desktop3pRoutedModel } from "../desktop-3p"; import { reconcileDesktopProfile, renderDesktopProfile, type DesktopProfileModel } from "../desktop-profile"; import type { PickerModelEntry } from "./picker-bootstrap"; @@ -46,9 +47,14 @@ function splitRoute(route: string): { provider: string; id: string } { return { provider: route.slice(0, slash), id: route.slice(slash + 1) }; } -/** Desktop runners do not inherit the proxy's compaction env: mark only real >=1M windows. */ -function pickerSelector(alias: string, contextWindow: number | undefined): string { - return withOneMillionMarker(alias, contextWindow === undefined ? {} : { [alias]: contextWindow }, AUTO_CONTEXT_OFF)!; +/** + * Desktop runners do not inherit the proxy's compaction env, so a long window (>= the default + * compact window) is marked on the strength of the `prompt is too long` recovery instead + * (devlog/_plan/261009_claude_1m_default/020). Real Anthropic models need a genuine 1M. + */ +function pickerSelector(alias: string, contextWindow: number | undefined, provider: string, id: string): string { + const mode = isAnthropicClaudeRoute(provider, id) ? AUTO_CONTEXT_OFF : UNPAIRED_AUTO_CONTEXT; + return withOneMillionMarker(alias, contextWindow === undefined ? {} : { [alias]: contextWindow }, mode)!; } export function buildPickerModels(input: PickerRouteInput): PickerModelEntry[] { @@ -61,7 +67,7 @@ export function buildPickerModels(input: PickerRouteInput): PickerModelEntry[] { const alias = provider === "native" ? claudeCodeNativeAlias(id) : aliasForRoute(provider, id); if (!alias || seen.has(alias)) continue; seen.add(alias); - out.push({ id: pickerSelector(alias, model.contextWindow), name: model.label, + out.push({ id: pickerSelector(alias, model.contextWindow, provider, id), name: model.label, ...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }) }); } return out; @@ -81,7 +87,7 @@ export function buildCliPickerModels(input: PickerRouteInput): PickerModelEntry[ const alias = activeDesktop3pAlias(provider, id); if (seen.has(alias) || resolveDesktop3pAlias(alias) !== model.route) continue; seen.add(alias); - out.push({ id: pickerSelector(alias, model.contextWindow), name: model.label, + out.push({ id: pickerSelector(alias, model.contextWindow, provider, id), name: model.label, description: `opencodex · ${model.route}`, route: model.route, ...(model.contextWindow === undefined ? {} : { contextWindow: model.contextWindow }) }); } diff --git a/src/claude/long-context.ts b/src/claude/long-context.ts new file mode 100644 index 00000000000..9b1a922d23e --- /dev/null +++ b/src/claude/long-context.ts @@ -0,0 +1,66 @@ +/** + * Long-context thresholds shared by every Claude surface + * (devlog/_plan/261009_claude_1m_default/020_long_context_eligibility.md). + * + * A leaf module: desktop-3p.ts and desktop-profile.ts need the predicate, and context-windows.ts + * already imports desktop-3p.ts, so the predicate cannot live there. + */ +import { isAnthropicInstanceId } from "../providers/anthropic-instance-id"; + +export const ONE_MILLION = 1_000_000; + +/** A window at or below this is Claude Code's own default accounting; widening it gains nothing. */ +export const AUTO_CONTEXT_FLOOR = 200_000; + +/** + * Auto-context defaults (devlog 260712 020, user-approved). + * + * The compact window is the token count at which Claude Code starts compacting, and it is + * also the floor `shouldMarkOneMillion` uses — a model may only carry the marker if it can + * host this window. 350,000 was chosen when the widest native row advertised 372,000. + * + * It now matches the auto-compaction limit the Codex catalog ships for the same models + * (`nativeAutoCompactLimit`: 829,800 against the 922,000 native window). Leaving the two + * apart meant one model compacting at 350k under Claude Code and at 829,800 under Codex. + * The value stays clear of the measured 922,000 ceiling by ~92k, so compaction still has + * room to run before the upstream refuses. + */ +export const AUTO_COMPACT_WINDOW_DEFAULT = 829_800; + +/** + * Whether a window earns the 1M selection on a surface whose Claude Code runner may not inherit + * CLAUDE_CODE_AUTO_COMPACT_WINDOW (Desktop pickers, discovery, Desktop 3P, generated subagents). + * A window >= 1M always does. A smaller one does when it can host the default compact window, so + * a runner that has the variable compacts in time, and one that lacks it overflows into the + * `prompt is too long` envelope (anthropicErrorBody) and compacts reactively. The floor is fixed + * on purpose: a custom compact window must not re-admit a 372k route (#854). + */ +export function isLongContextWindow(window: number | undefined): boolean { + if (typeof window !== "number" || window <= 0) return false; + return window >= ONE_MILLION || (window > AUTO_CONTEXT_FLOOR && window >= AUTO_COMPACT_WINDOW_DEFAULT); +} + +/** + * A Claude model on an Anthropic-named route. Claude windows are 200k or a genuine 1M and a bare + * id rides the native passthrough, so the long-window rule never widens one: a Claude row listed + * under 1M is a capped one, and on a runner without the compaction env it keeps the 200k + * accounting it had before long windows were widened. Keyed on the model id as well, so a + * configured gateway that only shares the name `anthropic2` and serves other models keeps the + * long-window rule. A custom `anthropic2` gateway serving Claude below 1M is treated like a pool + * row on these surfaces, which is the pre-existing (unwidened) behavior. + */ +export function isAnthropicClaudeRoute(provider: string, modelId: string): boolean { + return isAnthropicInstanceId(provider) && modelId.startsWith("claude-"); +} + +/** Desktop 3P / picker / discovery / dashboard 1M eligibility for one provider route. */ +export function claudeSurfaceSupportsOneMillion(provider: string, modelId: string, window: number | undefined): boolean { + if (isAnthropicClaudeRoute(provider, modelId)) return typeof window === "number" && window >= ONE_MILLION; + return isLongContextWindow(window); +} + +/** Same, for a `provider/id` route string. */ +export function routeSupportsOneMillion(route: string, window: number | undefined): boolean { + const slash = route.indexOf("/"); + return claudeSurfaceSupportsOneMillion(route.slice(0, slash), route.slice(slash + 1), window); +} diff --git a/src/claude/model-info.ts b/src/claude/model-info.ts index 7f5fe0f7266..21162fc269c 100644 --- a/src/claude/model-info.ts +++ b/src/claude/model-info.ts @@ -19,7 +19,8 @@ import { orderForModelPicker, catalogModelEfforts, nativeEffortClamp, nativeOpen import { claudeCodeAlias, claudeCodeNativeAlias } from "./alias"; import { cursorFastIdFor } from "../adapters/cursor/catalog"; import { desktop3pAlias } from "./desktop-3p"; -import { AUTO_CONTEXT_OFF, type AutoContextMode } from "./context-windows"; +import { AUTO_CONTEXT_OFF, shouldMarkOneMillion, UNPAIRED_AUTO_CONTEXT, type AutoContextMode } from "./context-windows"; +import { isAnthropicClaudeRoute } from "./long-context"; const MODEL_INFO_CREATED_AT = "2026-01-01T00:00:00Z"; const ANTHROPIC_EFFORT_RUNGS = new Set(["low", "medium", "high", "xhigh", "max"]); @@ -144,23 +145,25 @@ export function buildAnthropicModelInfos( : aliasForRoute(m.provider, m.id); }), ]); - // [1m] picker variant (devlog 260712 B1): Claude Code accounts exactly 1M for ids - // carrying the marker (2.1.207 binary: /\[1m\]/i → 1e6, compaction preserved), so - // ONLY models with an authoritative >=1M window get a second selectable row — - // the auto-context widening that let a 372K route carry the marker (and be - // over-filled) is the #854 defect and does not come back. Guards (audit R1#11): - // same dedupe set, never double-suffix. + // [1m] picker variant (devlog 260712 B1; widened in devlog/_plan/261009_claude_1m_default/020): + // Claude Code accounts exactly 1M for ids carrying the marker (2.1.207 binary: /\[1m\]/i -> 1e6, + // compaction preserved). A discovery client may lack CLAUDE_CODE_AUTO_COMPACT_WINDOW, so the + // rule is the fixed unpaired one (long-context.ts), not the configurable launch compact window: + // a custom 350k window must not re-admit a 372k route (#854). The server's auto mode only turns + // widening off (autoContext: false / maxContextTokens), falling back to >= 1M. Base and Fast + // rows stay unmarked: this list is a choice list. Guards (audit R1#11): same dedupe set, never + // double-suffix. + const variantMode = auto.enabled ? UNPAIRED_AUTO_CONTEXT : AUTO_CONTEXT_OFF; const push1mVariant = ( base: AnthropicModelInfo, contextWindow: number | undefined, maxInputTokens?: number, selectorId?: string, + mode: AutoContextMode = variantMode, ) => { - // The [1m] marker makes Claude Code account 1e6 tokens for the row, so it - // may only name models whose AUTHORITATIVE effective window is >= 1M — - // never the auto-context widening, which would mark a 372K route and have - // Claude Code over-fill it (the #854 defect). - if (contextWindow === undefined || contextWindow < ONE_MILLION) return; + // A window >= 1M always earns the row; a long window earns it under the unpaired rule, + // because an overflow is answered as `prompt is too long`, which Claude Code compacts on. + if (!shouldMarkOneMillion(contextWindow, mode)) return; if (base.id.includes("[1m]")) return; const id = selectorId ?? `${base.id}[1m]`; if (seen.has(id)) return; @@ -170,9 +173,9 @@ export function buildAnthropicModelInfos( // (measured — see devlog/_plan/260817_native_gpt56_1m_context/001_measurement_evidence.md). // Advertising the flat 1e6 there would invite mid-session context_length_exceeded, so the // variant reports whichever of the two is smaller. - const advertised = typeof maxInputTokens === "number" && maxInputTokens > 0 - ? Math.min(ONE_MILLION, maxInputTokens) - : ONE_MILLION; + // A long window under 1M must not be advertised as 1e6 input either. + const ceiling = typeof maxInputTokens === "number" && maxInputTokens > 0 ? maxInputTokens : contextWindow; + const advertised = typeof ceiling === "number" && ceiling > 0 ? Math.min(ONE_MILLION, ceiling) : ONE_MILLION; out.push({ ...base, id, display_name: `${base.display_name} · 1M`, max_input_tokens: advertised }); }; /** @@ -251,7 +254,7 @@ export function buildAnthropicModelInfos( && listedModelId.startsWith("claude-fable-") ? `${claudeCodeNativeAlias(listedModelId)}[1m]` : undefined; - push1mVariant(info, m.contextWindow, routedMaxInput, oneMillionSelector); + push1mVariant(info, m.contextWindow, routedMaxInput, oneMillionSelector, isAnthropicClaudeRoute(m.provider, m.id) ? AUTO_CONTEXT_OFF : variantMode); // The whole model is passed, not a (provider, id) pair: a combo row lives in its own // namespace with no config.providers entry, so the caller classifies it from the // aggregated supportsServiceTier the row already carries. diff --git a/src/claude/subagent-model.ts b/src/claude/subagent-model.ts index db0ee655455..f5efe44f4d4 100644 --- a/src/claude/subagent-model.ts +++ b/src/claude/subagent-model.ts @@ -1,6 +1,7 @@ import type { OcxConfig } from "../types"; import { claudeCodeAlias, claudeCodeNativeAlias, resolveAlias } from "./alias"; -import { AUTO_CONTEXT_OFF, shouldMarkOneMillion, stripOneMillionMarker, withOneMillionMarker } from "./context-windows"; +import { AUTO_CONTEXT_OFF, shouldMarkOneMillion, stripOneMillionMarker, UNPAIRED_AUTO_CONTEXT, withOneMillionMarker, type AutoContextMode } from "./context-windows"; +import { isAnthropicClaudeRoute } from "./long-context"; import { hasOwnProvider } from "../config"; import { knownModelIdsForProvider } from "../router"; import { decodeRoutedModelIdOrThrow } from "../providers/slug-codec"; @@ -14,20 +15,38 @@ export { SAFE_AGENT_MODEL_ID } from "../config/subagent-models"; /** * Generated subagent defs cannot rely on the parent's auto-context compaction - * pairing, so their [1m] marker follows the AUTHORITATIVE window only: mark when - * the effective window (exact selector, then the canonical [1m] form, then bare) - * is genuinely >= 1M; strip an inherited unsafe marker back to the bare selector; - * with no window information, keep the selector as it was. Genuine routed [1m] - * ids are preserved through the canonical-exact lookup. (#854) + * pairing, so their [1m] marker follows the unpaired long-context rule + * (long-context.ts): mark when the effective window (exact selector, then the + * canonical [1m] form, then bare) is >= 1M or can host the default compact window, + * whose overflow Claude Code compacts on as `prompt is too long`; strip an inherited + * marker the window cannot carry; with no window information, keep the selector as it + * was. Genuine routed [1m] ids are preserved through the canonical-exact lookup. + * (#854; devlog/_plan/261009_claude_1m_default/020) */ +/** + * The marking mode a subagent selector gets: real Anthropic models (a bare `claude-*` passthrough + * id, or a Claude row on either Anthropic pool) need a genuine 1M; everything else follows the + * unpaired long-context rule. Connected launches read windows from the published catalog, which + * does not drop capped Anthropic rows, so this cannot rely on the window map alone. + */ +function subagentSelectorMode(bare: string): AutoContextMode { + const route = resolveAlias(bare) ?? resolveDesktop3pAlias(bare) ?? bare; + const slash = route.indexOf("/"); + const anthropic = slash < 0 + ? route.startsWith("claude-") + : isAnthropicClaudeRoute(route.slice(0, slash), route.slice(slash + 1)); + return anthropic ? AUTO_CONTEXT_OFF : UNPAIRED_AUTO_CONTEXT; +} + export function withSubagentContextMarker(selector: string, windows: Record): string { const bare = stripOneMillionMarker(selector); const wasMarked = selector !== bare; const canonicalExact = wasMarked ? `${bare}[1m]` : selector; const authoritativeWindow = windows[selector] ?? windows[canonicalExact] ?? windows[bare]; if (typeof authoritativeWindow === "number" && authoritativeWindow > 0) { - return shouldMarkOneMillion(authoritativeWindow, AUTO_CONTEXT_OFF) - ? (withOneMillionMarker(selector, windows) ?? selector) + const mode = subagentSelectorMode(bare); + return shouldMarkOneMillion(authoritativeWindow, mode) + ? (withOneMillionMarker(selector, windows, mode) ?? selector) : bare; } return wasMarked ? selector : bare; @@ -77,7 +96,7 @@ export function resolveSubagentForceModel(config: OcxConfig, windows: Record= 1_000_000)) return null; + if (requestedMarker && !exactMarkedExposure && !(typeof window === "number" && Number.isFinite(window) && shouldMarkOneMillion(window, subagentSelectorMode(bare)))) return null; const marked = exactMarkedExposure ? parts.alias : withSubagentContextMarker(parts.alias, windows); // Bare Anthropic names are indistinguishable from the old CLI's fallback. // Encode only the force selector; handlers restore native identity before diff --git a/src/server/management/shared.ts b/src/server/management/shared.ts index d3ca423f4d7..f5a0621922c 100644 --- a/src/server/management/shared.ts +++ b/src/server/management/shared.ts @@ -364,7 +364,7 @@ export function stripRegistryOnlyStaticHeaders(name: string, provider: OcxProvid /** Shared Desktop profile DTO builder for the management API and CLI. */ export async function buildClaudeDesktopState(config: OcxConfig, stored?: OcxClaudeDesktopProfile) { const { filterCatalogVisibleModels, nativeContextLimits, nativeOpenAiContextWindow, desktopVisibleNativeSlugs } = await import("../../codex/catalog"); - const { DESKTOP_SUPPORTS_1M_THRESHOLD } = await import("../../claude/desktop-3p"); + const { routeSupportsOneMillion } = await import("../../claude/long-context"); const { reconcileDesktopProfile, renderDesktopProfile } = await import("../../claude/desktop-profile"); const routed = filterCatalogVisibleModels(await fetchAllModels(config), config); const profileModels: DesktopProfileModel[] = [ @@ -413,8 +413,8 @@ export async function buildClaudeDesktopState(config: OcxConfig, stored?: OcxCla ...(modelByRoute.get(route)?.contextWindow ? { contextWindow: modelByRoute.get(route)!.contextWindow } : {}), effortSupported: effortByRoute.get(route) ?? false, // Read-only view of the 1M capability the written Desktop config already emits, - // derived from the SAME threshold so the dashboard chip can never disagree. - supports1m: (modelByRoute.get(route)?.contextWindow ?? 0) >= DESKTOP_SUPPORTS_1M_THRESHOLD, + // derived from the SAME predicate so the dashboard chip can never disagree. + supports1m: routeSupportsOneMillion(route, modelByRoute.get(route)?.contextWindow), assignment: profile.assignments[route]!, })); return { diff --git a/structure/clients/claude-desktop.md b/structure/clients/claude-desktop.md index dbc87ee40ae..a27fdadbcc3 100644 --- a/structure/clients/claude-desktop.md +++ b/structure/clients/claude-desktop.md @@ -287,7 +287,7 @@ intent and cannot repair the URL a running Desktop already pinned. The `claude.a certificate, streams every body and upgrade unchanged, and rewrites only the bootstrap response's local Code picker surfaces, `ccd` (what the Desktop Code tab reads) and its `code` fallback, never the remote `ccr` (`picker-bootstrap.ts`), failing open to the original bytes; the model list -comes from a persisted snapshot (`picker-models.ts`), so a bootstrap never waits on discovery. Picker aliases carry `[1m]` only for authoritative windows of at least 1M, using the shared context marker helper with auto-context disabled. Sub-million opt-ins remain unmarked because the picker cannot guarantee the Desktop runner's compaction environment. A +comes from a persisted snapshot (`picker-models.ts`), so a bootstrap never waits on discovery. Picker aliases carry `[1m]` for long windows (`src/claude/long-context.ts`: at least 1M, or at least the 829,800 default compact window), and Claude models on either Anthropic pool only at a genuine 1M. The picker cannot guarantee the Desktop runner's compaction environment, so an 872k route that outgrows its window relies on the `prompt is too long` envelope, which Claude Code compacts on; Desktop 3P `supports1m`/`prefer1m`, discovery `· 1M` rows and generated subagent markers use the same rule. A CONNECT to claude.ai that arrives before the first refresh waits at most 3 s, then goes blind. A picker proxy bind failure only disables picker mode; a picker construction or start failure closes every socket the start had bound before rethrowing. Ordinary session cookies within the header diff --git a/structure/subagents.md b/structure/subagents.md index bdaa57cae19..906ce2149e4 100644 --- a/structure/subagents.md +++ b/structure/subagents.md @@ -546,6 +546,6 @@ Automatic account exhaustion and recovery use the [spendable Codex credit eviden ### Forced Claude Code subagent model -`src/claude/subagent-model.ts` shares the roster alias and authoritative context-marker resolver with routed launch force. `claudeCode.subagentModelForce` is opt-in and default-off. Force availability excludes retained unavailable roster entries. Caller-added `[1m]` suffixes require finite authoritative million-token capacity or an exact advertised marked identity; exact upstream ids remain literal, while legacy roster marker handling is unchanged. The generated-agent legacy directive cannot replace a wire selector matching the configured forced alias. `src/claude/subagent-force-status.ts` performs bounded, read-only server-local version and settings-key inspection; unknown is not supported. +`src/claude/subagent-model.ts` shares the roster alias and authoritative context-marker resolver with routed launch force. `claudeCode.subagentModelForce` is opt-in and default-off. Force availability excludes retained unavailable roster entries. Caller-added `[1m]` suffixes require a finite authoritative long window (`src/claude/long-context.ts`: 1M, or at least the 829,800 default compact window; a genuine 1M for Claude models on either Anthropic pool or a bare `claude-*` id) or an exact advertised marked identity; generated markers follow the same rule; exact upstream ids remain literal, while legacy roster marker handling is unchanged. The generated-agent legacy directive cannot replace a wire selector matching the configured forced alias. `src/claude/subagent-force-status.ts` performs bounded, read-only server-local version and settings-key inspection; unknown is not supported. Explicit gateway selectors outrank generated-agent `ocx-route` fallback independently of saved force state. `src/claude/inbound-model-options.ts` shares this precedence across Messages and count-tokens; bare Claude fallback and requests without directives retain their existing behavior. Native Claude force targets use reversible native aliases, restored before existing credential/model-map checks. Connected launch exposure comes from fresh authenticated gateway rows, not cached context-window keys; acquisition is independent of cache-write success. diff --git a/tests/claude-integration/claude-long-context-eligibility.test.ts b/tests/claude-integration/claude-long-context-eligibility.test.ts new file mode 100644 index 00000000000..ed41dc2e72f --- /dev/null +++ b/tests/claude-integration/claude-long-context-eligibility.test.ts @@ -0,0 +1,89 @@ +/** + * Long windows (>= the default compact window) are 1M on every Claude surface whose runner may + * lack CLAUDE_CODE_AUTO_COMPACT_WINDOW; Claude models on either Anthropic pool still need a genuine + * 1M (devlog/_plan/261009_claude_1m_default/020_long_context_eligibility.md). + */ +import { describe, expect, test } from "bun:test"; +import { claudeSurfaceSupportsOneMillion, isLongContextWindow, routeSupportsOneMillion } from "../../src/claude/long-context"; +import { AUTO_CONTEXT_OFF, UNPAIRED_AUTO_CONTEXT } from "../../src/claude/context-windows"; +import { buildAnthropicModelInfos } from "../../src/claude/model-info"; +import { generateDesktop3pModels } from "../../src/claude/desktop-3p"; +import { withSubagentContextMarker } from "../../src/claude/subagent-model"; + +const ASTRA_872K = { modelWindows: { "gpt-6-astra": 872_000 } }; + +describe("isLongContextWindow / claudeSurfaceSupportsOneMillion", () => { + test("the floor is the default compact window, never a configurable one", () => { + for (const window of [undefined, 0, 200_000, 262_144, 400_000, 829_799]) expect(isLongContextWindow(window)).toBe(false); + for (const window of [829_800, 872_000, 922_000, 1_000_000, 2_000_000]) expect(isLongContextWindow(window)).toBe(true); + }); + + test("Claude models on either Anthropic pool need a genuine 1M; a gateway that shares the name does not", () => { + expect(claudeSurfaceSupportsOneMillion("anthropic", "claude-sonnet-5", 872_000)).toBe(false); + expect(claudeSurfaceSupportsOneMillion("anthropic2", "claude-sonnet-5", 872_000)).toBe(false); + expect(claudeSurfaceSupportsOneMillion("anthropic2", "claude-sonnet-5", 1_000_000)).toBe(true); + // A configured gateway named anthropic2 serving another vendor's model is not an Anthropic pool. + expect(claudeSurfaceSupportsOneMillion("anthropic2", "kimi-k3", 900_000)).toBe(true); + expect(routeSupportsOneMillion("native/gpt-6-astra", 872_000)).toBe(true); + expect(routeSupportsOneMillion("kimi/k3", 262_144)).toBe(false); + }); +}); + +describe("Anthropic discovery 1M variants", () => { + const variants = (infos: ReturnType) => infos.filter(info => info.id.endsWith("[1m]")); + + test("an 872k native gets a variant that reports its real input ceiling", () => { + const infos = buildAnthropicModelInfos(["gpt-6-astra"], [], UNPAIRED_AUTO_CONTEXT, "readable", undefined, ASTRA_872K); + const [variant] = variants(infos); + expect(variant?.display_name).toBe("gpt-6-astra (native) · 1M"); + expect(variant?.max_input_tokens).toBe(872_000); + expect(variants(buildAnthropicModelInfos(["gpt-6-astra"], [], AUTO_CONTEXT_OFF, "readable", undefined, ASTRA_872K))).toHaveLength(0); + }); + + test("a custom compact window does not lower the floor (#854)", () => { + const routed = [{ provider: "example", id: "mid", contextWindow: 400_000 }]; + expect(variants(buildAnthropicModelInfos([], routed, { enabled: true, compactWindow: 350_000 }, "readable"))).toHaveLength(0); + }); + + test("a long routed row without an input ceiling advertises its window, not 1e6", () => { + const infos = buildAnthropicModelInfos([], [{ provider: "example", id: "long", contextWindow: 900_000 }], UNPAIRED_AUTO_CONTEXT, "readable"); + expect(variants(infos).map(info => info.max_input_tokens)).toEqual([900_000]); + }); + + test("Anthropic rows and short windows get no widened variant", () => { + const routed = [ + { provider: "anthropic", id: "claude-x", contextWindow: 872_000 }, + { provider: "anthropic2", id: "claude-y", contextWindow: 872_000 }, + { provider: "kimi", id: "k3", contextWindow: 262_144 }, + ]; + expect(variants(buildAnthropicModelInfos([], routed, UNPAIRED_AUTO_CONTEXT, "readable"))).toHaveLength(0); + }); +}); + +describe("Desktop 3P", () => { + test("an 872k native offers and prefers 1M; a 262k route does not", () => { + const [astra] = generateDesktop3pModels(["gpt-6-astra"], [], undefined, ASTRA_872K); + expect(astra).toMatchObject({ supports1m: true, prefer1m: true }); + const [k3] = generateDesktop3pModels([], [{ provider: "kimi", id: "k3", contextWindow: 262_144 }]); + expect(k3!.supports1m).toBeUndefined(); + expect(k3!.prefer1m).toBeUndefined(); + }); +}); + +describe("generated subagent markers", () => { + const alias = "ocx-claude-native--gpt-6-astra"; + + test("an 872k window is marked, a 262k window is not, and an unsafe marker is stripped", () => { + expect(withSubagentContextMarker(alias, { [alias]: 872_000 })).toBe(`${alias}[1m]`); + expect(withSubagentContextMarker(alias, { [alias]: 262_144 })).toBe(alias); + expect(withSubagentContextMarker(`${alias}[1m]`, { [alias]: 262_144 })).toBe(alias); + }); + + test("a capped Claude row on Pool 2 stays unmarked even when a connected window map lists it", () => { + // Connected launches read the published catalog, which keeps capped Anthropic rows. + const pool2 = "ocx-claude-anthropic2--claude-sonnet-5"; + expect(withSubagentContextMarker(pool2, { [pool2]: 900_000 })).toBe(pool2); + expect(withSubagentContextMarker(`${pool2}[1m]`, { [pool2]: 900_000 })).toBe(pool2); + expect(withSubagentContextMarker(pool2, { [pool2]: 1_000_000 })).toBe(`${pool2}[1m]`); + }); +}); diff --git a/tests/claude-integration/claude-picker-models.test.ts b/tests/claude-integration/claude-picker-models.test.ts index fd1ad3445c6..7fee7abe587 100644 --- a/tests/claude-integration/claude-picker-models.test.ts +++ b/tests/claude-integration/claude-picker-models.test.ts @@ -79,25 +79,30 @@ test("picker preserves real million-token routed windows without promoting small expect(rows[1]?.contextWindow).toBe(1_048_576); }); -test("picker leaves sub-million native opt-ins unmarked without a runner compaction guarantee", () => { +test("picker marks long native opt-ins on the prompt-too-long recovery and leaves 272k unmarked", () => { + // Desktop runners lack CLAUDE_CODE_AUTO_COMPACT_WINDOW; an 872k window is marked anyway because an + // overflow comes back as `prompt is too long`, which Claude Code compacts on (261009 020). const rows = buildPickerModels({ nativeSlugs: ["gpt-5.5", "gpt-6-astra", "gpt-6-sol", "gpt-6-luna", "gpt-5.6-terra"], routedModels: [], nativeContextCap: { modelWindows: { "gpt-6-astra": 872_000, "gpt-6-sol": 872_000, "gpt-6-luna": 872_000, "gpt-5.6-terra": 922_000, } }, }); - expect(rows.every(row => !row.id.endsWith("[1m]"))).toBe(true); + expect(rows.map(row => row.id.endsWith("[1m]"))).toEqual([false, true, true, true, true]); expect(rows.map(row => row.contextWindow)).toEqual([272_000, 872_000, 872_000, 872_000, 922_000]); }); -test("picker marks the million-token boundary and leaves smaller routed windows unmarked", () => { +test("picker marks windows from the default compact floor up and leaves smaller routed windows unmarked", () => { const rows = buildPickerModels({ nativeSlugs: [], routedModels: [ - { provider: "example", id: "below", contextWindow: 999_999 }, + { provider: "example", id: "short", contextWindow: 262_144 }, + { provider: "example", id: "below", contextWindow: 829_799 }, + { provider: "example", id: "floor", contextWindow: 829_800 }, { provider: "example", id: "exact", contextWindow: 1_000_000 }, { provider: "example", id: "larger", contextWindow: 2_000_000 }, + { provider: "anthropic2", id: "claude-pool-two", contextWindow: 872_000 }, ] }); expect(rows.map(row => row.id)).toEqual([ - "ocx-claude-example--below", "ocx-claude-example--exact[1m]", "ocx-claude-example--larger[1m]", + "ocx-claude-example--short", "ocx-claude-example--below", "ocx-claude-example--floor[1m]", + "ocx-claude-example--exact[1m]", "ocx-claude-example--larger[1m]", "ocx-claude-anthropic2--claude-pool-two", ]); - expect(rows.map(row => row.contextWindow)).toEqual([999_999, 1_000_000, 2_000_000]); }); diff --git a/tests/claude-integration/claude-subagent-model-force.test.ts b/tests/claude-integration/claude-subagent-model-force.test.ts index be3c07403ca..670beb458ca 100644 --- a/tests/claude-integration/claude-subagent-model-force.test.ts +++ b/tests/claude-integration/claude-subagent-model-force.test.ts @@ -39,11 +39,12 @@ test("exported values independently win and native launches retain exports", () } }); -test("alias resolution reuses roster provider/native semantics and authoritative million boundary", () => { +test("alias resolution reuses roster provider/native semantics and the long-context boundary", () => { for (const entry of deps.forceAvailable) { const c = config(entry); const { alias } = entryParts(entry, c); - expect(resolveSubagentForceModel(c, { [alias]: 999999 }, { entries: deps.forceAvailable })).toBe(alias); + expect(resolveSubagentForceModel(c, { [alias]: 829_799 }, { entries: deps.forceAvailable })).toBe(alias); + expect(resolveSubagentForceModel(c, { [alias]: 829_800 }, { entries: deps.forceAvailable })).toBe(`${alias}[1m]`); expect(resolveSubagentForceModel(c, { [alias]: 1000000 }, { entries: deps.forceAvailable })).toBe(`${alias}[1m]`); } }); diff --git a/tests/clients/desktop-3p.test.ts b/tests/clients/desktop-3p.test.ts index 8e61aeaf48a..0156ee1ab12 100644 --- a/tests/clients/desktop-3p.test.ts +++ b/tests/clients/desktop-3p.test.ts @@ -212,19 +212,18 @@ describe("Claude Desktop 3P models", () => { }); test("an openai context cap reaches the Desktop writer, not just the dashboard", () => { - // No surviving native advertises a 1M window (gpt-5.4 was the last). Sol's - // opt-in ceiling is 922k, so even a 1M provider cap must not invent - // supports1m — nativeOpenAiContextWindow clamps it under the threshold. - // A 272k cap has to take the same path, or the written Desktop config - // would promise a window the proxy will not serve (#854's effective-window + // Sol's default window is 272k: no 1M. A 1M provider cap opts it into its 922k + // ceiling, a long window, so the written config now offers and prefers 1M; an + // overflow comes back as `prompt is too long`, which Claude compacts on (261009 020). + // A 272k cap has to take the same path as the default (#854's effective-window // contract). const uncapped = generateDesktop3pModels(["gpt-5.6-sol"], []); expect(uncapped[0]!.supports1m).toBeUndefined(); expect(uncapped[0]!.prefer1m).toBeUndefined(); const optedIn = generateDesktop3pModels(["gpt-5.6-sol"], [], undefined, 1_000_000); - expect(optedIn[0]!.supports1m).toBeUndefined(); - expect(optedIn[0]!.prefer1m).toBeUndefined(); + expect(optedIn[0]!.supports1m).toBe(true); + expect(optedIn[0]!.prefer1m).toBe(true); const capped = generateDesktop3pModels(["gpt-5.6-sol"], [], undefined, 272_000); expect(capped[0]!.supports1m).toBeUndefined(); diff --git a/tests/fixtures/test-layout-expected.json b/tests/fixtures/test-layout-expected.json index 2ec78f200e9..a3c47be575c 100644 --- a/tests/fixtures/test-layout-expected.json +++ b/tests/fixtures/test-layout-expected.json @@ -303,6 +303,7 @@ "claude-model-info.test.ts": "claude-integration", "claude-models-discovery.test.ts": "claude-integration", "claude-native-affinity.test.ts": "claude-integration", "claude-native-passthrough.test.ts": "claude-integration", "claude-native-rate-limit-headers.test.ts": "claude-integration", "claude-outbound.test.ts": "claude-integration", + "claude-long-context-eligibility.test.ts": "claude-integration", "claude-prompt-too-long.test.ts": "claude-integration", "claude-shell-hook.test.ts": "claude-integration", "claude-sidecar-override.test.ts": "claude-integration", "claude-source-envelope.test.ts": "claude-integration", "claude-system-env-auto.test.ts": "claude-integration",