diff --git a/CHANGELOG.md b/CHANGELOG.md index c99c014b..2fb600a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,47 @@ +## [7.64.0](https://github.com/kaitranntt/ccs/compare/v7.63.1...v7.64.0) (2026-04-01) + +### Features + +* add first-class CCS WebSearch runtime ([e260df7](https://github.com/kaitranntt/ccs/commit/e260df7178dd030efe3d490b4cd9421b386c43c2)) +* **cliproxy:** support duplicate-email codex accounts ([c73f338](https://github.com/kaitranntt/ccs/commit/c73f33872abc165aaaba8a240c06cc4ccbb6f5b4)) +* **ui:** unify duplicate-email account surfaces ([80341f1](https://github.com/kaitranntt/ccs/commit/80341f18c3c93dd4cdca92f6965136cff90be6f7)) +* **websearch:** add managed mcp runtime and provider cooldowns ([7f83e04](https://github.com/kaitranntt/ccs/commit/7f83e041b7fa8907a5d9484acd21ea88edac67e7)) +* **websearch:** finish managed third-party rollout ([de7171d](https://github.com/kaitranntt/ccs/commit/de7171d81076ecfd4e5e6474ce062c44a4c8be9a)), closes [#862](https://github.com/kaitranntt/ccs/issues/862) + +### Bug Fixes + +* bootstrap explicit CODEX_HOME for ccsxp ([b6717c5](https://github.com/kaitranntt/ccs/commit/b6717c55295dadeeff87ec356bc19d7b1b1d3733)) +* **ci:** harden ai review output ([4950be7](https://github.com/kaitranntt/ccs/commit/4950be7fc025877dd6744b9c8c87b4714ec49de2)) +* **codex:** harden duplicate-email account actions ([22f0916](https://github.com/kaitranntt/ccs/commit/22f091689fadf485d24fc46510f211a489680a2b)) +* **codex:** harden native runtime detection ([ca54bad](https://github.com/kaitranntt/ccs/commit/ca54bad2aa774c9dc3de9429d3e1440d179796a7)) +* **dashboard:** use shared base time for connection timeline events ([d76ef5b](https://github.com/kaitranntt/ccs/commit/d76ef5bcb30bd87ea487894586978473fcc8fead)), closes [#856](https://github.com/kaitranntt/ccs/issues/856) +* **docker:** harden cliproxy local proxy with auth guard, dynamic port, and body handling ([6471cc5](https://github.com/kaitranntt/ccs/commit/6471cc55d72deeebcbca70d54983b90ae32c999a)) +* **docker:** harden proxy with timeout, Bun compat, and test coverage ([27409b7](https://github.com/kaitranntt/ccs/commit/27409b789b29a1d008f621f55aab7eed9861d6d4)) +* **docker:** proxy CLIProxy management panel through dashboard to avoid cross-origin errors ([881b061](https://github.com/kaitranntt/ccs/commit/881b061dfec497d7120b15d2045cf8e1a8f04bcc)) +* harden ai review comment formatting ([fa37f39](https://github.com/kaitranntt/ccs/commit/fa37f391c17c51c74a5bfe109225caf01798dc96)) +* merge existing disallowed tool flags for websearch ([b087c02](https://github.com/kaitranntt/ccs/commit/b087c02738c7e588491f84c8f2318c2ef5b03eca)) +* normalize websearch hook output for local providers ([a5d71e3](https://github.com/kaitranntt/ccs/commit/a5d71e35606ebaf16f97d860405800e9a9454659)) +* probe Codex config override support directly ([014a844](https://github.com/kaitranntt/ccs/commit/014a844844c76fe5dfce7763ed62e071ee063f58)) +* **ui:** normalize grouped account audience order ([498448a](https://github.com/kaitranntt/ccs/commit/498448a43181280dc06035d80b694f7522a1b28a)) +* use type-only import in instance manager ([f0cd44e](https://github.com/kaitranntt/ccs/commit/f0cd44e016c2c4a33ad06edcf6b13d8cbf1920bf)) +* **websearch:** close review gaps in managed runtime ([cae5b71](https://github.com/kaitranntt/ccs/commit/cae5b710b86a6bd985424cce028a502265051d09)) + +### Hotfixes + +* restore prepack packaging contract ([78f6c5d](https://github.com/kaitranntt/ccs/commit/78f6c5d7156e1fb447e523b355c302a45d5b71bd)) + +### Documentation + +* **architecture:** document codex duplicate-email identity ([be0c597](https://github.com/kaitranntt/ccs/commit/be0c597d255a21a7e935f033dfcc4de4e19c8c8e)) + +### Code Refactoring + +* remove duplicate copilot websearch sync ([2c15304](https://github.com/kaitranntt/ccs/commit/2c153045f33e02cc821a623f6594fb4a6bebb321)) + +### Tests + +* **dashboard:** add regression tests for connection timeline event generation ([4ab5194](https://github.com/kaitranntt/ccs/commit/4ab51945a412321e3e56d0c6860d0121761dcbfa)) + ## [7.63.1](https://github.com/kaitranntt/ccs/compare/v7.63.0...v7.63.1) (2026-04-01) ### Hotfixes diff --git a/docs/project-roadmap.md b/docs/project-roadmap.md index 5ff51ac3..182449dc 100644 --- a/docs/project-roadmap.md +++ b/docs/project-roadmap.md @@ -1,6 +1,6 @@ # CCS Project Roadmap -Last Updated: 2026-03-30 +Last Updated: 2026-04-01 Forward-looking roadmap documenting current priorities, GitHub issues, and future feature plans. @@ -41,6 +41,7 @@ All major modularization work is complete. The codebase evolved from monolithic ### Recent Fixes +- **2026-04-01**: The `Compatible -> Codex CLI` dashboard now exposes manual long-context controls for `model_context_window` and `model_auto_compact_token_limit`. CCS reads and patches those upstream Codex config keys directly, adds official guidance that GPT-5.4 long context is experimental and opt-in, and keeps the behavior manual-only so the dashboard never auto-fills or auto-saves long-context values for the user. - **2026-03-30**: **#862** Third-party WebSearch now uses a first-class CCS-managed MCP tool path instead of relying on a denied native Anthropic `WebSearch` call as the normal UX. CCS provisions `ccs-websearch` into `~/.claude.json`, syncs it into isolated account configs when needed, suppresses native `WebSearch` on third-party launches, preserves the provider order `Exa -> Tavily -> Brave -> DuckDuckGo -> legacy CLI fallback`, and keeps the old hook runtime only as shared provider plumbing plus compatibility fallback. Uninstall cleanup now also removes the managed WebSearch MCP runtime. - **2026-03-28**: **#773** CCS now ships a dedicated `Compatible -> Codex CLI` dashboard route with a real split-view control center. The page detects the local Codex binary, keeps overview/docs guidance, and adds guided editors for the user-owned `~/.codex/config.toml` layer: top-level runtime defaults, project trust, profiles, model providers, MCP servers, and supported feature flags. Structured saves intentionally normalize TOML formatting and drop comments, so the raw editor remains the fidelity escape hatch. Follow-up fixes added immediate raw snapshot refresh, refresh/discard recovery for stale raw drafts, dirty raw-editor guarding for structured controls, project-trust path validation, read-only handling for unreadable config files, preservation of unsupported upstream values such as granular `approval_policy`, and feature reset-to-default support. CCS still warns that transient runtime overrides such as `codex -c key=value` and `CCS_CODEX_API_KEY` may change effective behavior without persisting into the file. - **2026-03-27**: WebSearch dashboard cards now manage Exa, Tavily, and Brave API keys inline instead of relying on a separate manual env step. CCS stores those secrets through `global_env`, reflects masked key state in `/api/websearch`, and counts dashboard-managed keys as ready in the WebSearch status flow. diff --git a/package.json b/package.json index 515af049..e92eab14 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kaitranntt/ccs", - "version": "7.63.1", + "version": "7.64.0", "description": "Claude Code Switch - Instant profile switching between Claude, GLM, Kimi, and more", "keywords": [ "cli", diff --git a/src/shared/compatible-cli-contracts.ts b/src/shared/compatible-cli-contracts.ts index 0c0f1b42..7433d322 100644 --- a/src/shared/compatible-cli-contracts.ts +++ b/src/shared/compatible-cli-contracts.ts @@ -91,6 +91,8 @@ export interface CodexSupportMatrixEntry { export interface CodexUserConfigDiagnostics { model: string | null; modelReasoningEffort: string | null; + modelContextWindow: number | null; + modelAutoCompactTokenLimit: number | null; modelProvider: string | null; activeProfile: string | null; approvalPolicy: string | null; @@ -137,6 +139,8 @@ export interface CodexRawConfigResponse { export interface CodexTopLevelSettingsPatch { model?: string | null; modelReasoningEffort?: string | null; + modelContextWindow?: number | null; + modelAutoCompactTokenLimit?: number | null; modelProvider?: string | null; approvalPolicy?: string | null; sandboxMode?: string | null; diff --git a/src/web-server/services/codex-dashboard-service.ts b/src/web-server/services/codex-dashboard-service.ts index 725c0bc1..8bba991f 100644 --- a/src/web-server/services/codex-dashboard-service.ts +++ b/src/web-server/services/codex-dashboard-service.ts @@ -243,6 +243,18 @@ function applyTopLevelSettingsPatch( 'model_reasoning_effort' ); } + if (hasOwn(values, 'modelContextWindow')) { + setNumberField(target, 'model_context_window', values.modelContextWindow, { + integer: true, + min: 1, + }); + } + if (hasOwn(values, 'modelAutoCompactTokenLimit')) { + setNumberField(target, 'model_auto_compact_token_limit', values.modelAutoCompactTokenLimit, { + integer: true, + min: 1, + }); + } if (hasOwn(values, 'modelProvider')) { setStringField(target, 'model_provider', values.modelProvider); } @@ -778,6 +790,8 @@ export async function getCodexDashboardDiagnostics(): Promise { path.join(codexHome, 'config.toml'), `model = "gpt-5.4" profile = "work" +model_context_window = 800000 +model_auto_compact_token_limit = 700000 model_provider = "cliproxy" approval_policy = "never" sandbox_mode = "danger-full-access" @@ -217,6 +219,8 @@ model = "gpt-5.4" expect(diagnostics.binary.installed).toBe(true); expect(diagnostics.binary.supportsConfigOverrides).toBe(true); expect(diagnostics.config.model).toBe('gpt-5.4'); + expect(diagnostics.config.modelContextWindow).toBe(800000); + expect(diagnostics.config.modelAutoCompactTokenLimit).toBe(700000); expect(diagnostics.config.activeProfile).toBe('work'); expect(diagnostics.config.modelProvider).toBe('cliproxy'); expect(diagnostics.config.profileCount).toBe(1); @@ -376,6 +380,8 @@ bearer_token = "secret" values: { model: 'gpt-5.4', modelReasoningEffort: 'high', + modelContextWindow: 800000, + modelAutoCompactTokenLimit: 700000, approvalPolicy: 'never', sandboxMode: 'workspace-write', webSearch: 'cached', @@ -394,10 +400,14 @@ bearer_token = "secret" const diagnostics = await getCodexDashboardDiagnostics(); expect(diagnostics.config.model).toBe('gpt-5.4'); expect(diagnostics.config.modelReasoningEffort).toBe('high'); + expect(diagnostics.config.modelContextWindow).toBe(800000); + expect(diagnostics.config.modelAutoCompactTokenLimit).toBe(700000); expect(diagnostics.config.toolOutputTokenLimit).toBe(12000); expect(diagnostics.config.personality).toBe('friendly'); expect(diagnostics.config.projectTrust[0]?.path).toBe('/tmp/workspace-a'); expect(result.rawText).toContain('model = "gpt-5.4"'); + expect(result.rawText).toContain('model_context_window = 800000'); + expect(result.rawText).toContain('model_auto_compact_token_limit = 700000'); expect(result.config?.model).toBe('gpt-5.4'); }); @@ -665,4 +675,24 @@ bearer_token = "secret" }) ).rejects.toThrow(CodexRawConfigValidationError); }); + + it('rejects invalid long-context values in structured top-level patches', async () => { + await expect( + patchCodexConfig({ + kind: 'top-level', + values: { + modelContextWindow: 0, + }, + }) + ).rejects.toThrow(CodexRawConfigValidationError); + + await expect( + patchCodexConfig({ + kind: 'top-level', + values: { + modelAutoCompactTokenLimit: 1.5, + }, + }) + ).rejects.toThrow(CodexRawConfigValidationError); + }); }); diff --git a/ui/src/components/account/flow-viz/account-card.tsx b/ui/src/components/account/flow-viz/account-card.tsx index b77f6dd7..b4f65156 100644 --- a/ui/src/components/account/flow-viz/account-card.tsx +++ b/ui/src/components/account/flow-viz/account-card.tsx @@ -3,9 +3,16 @@ */ import { AccountSurfaceCard } from '@/components/account/shared/account-surface-card'; +import { QuotaTooltipContent } from '@/components/shared/quota-tooltip-content'; import { Button } from '@/components/ui/button'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'; -import { formatQuotaPercent, getProviderMinQuota, getQuotaFailureInfo, cn } from '@/lib/utils'; +import { + cn, + formatQuotaPercent, + getProviderMinQuota, + getProviderResetTime, + getQuotaFailureInfo, +} from '@/lib/utils'; import { GripVertical, Loader2, Pause, Play } from 'lucide-react'; import { useAccountQuota, @@ -207,36 +214,45 @@ export function AccountCard({ hasGroupedVariants && showQuota ? (account.variants ?? []).map((variant, index) => { const quotaQuery = variantQuotaQueries[index]; - const minQuota = getProviderMinQuota(account.provider, quotaQuery?.data); + const quota = quotaQuery?.data; + const minQuota = getProviderMinQuota(account.provider, quota); + const resetTime = getProviderResetTime(account.provider, quota); const quotaLabel = minQuota !== null ? formatQuotaPercent(minQuota) : null; const quotaValue = quotaLabel !== null ? Number(quotaLabel) : null; - const failureInfo = getQuotaFailureInfo(quotaQuery?.data); + const failureInfo = getQuotaFailureInfo(quota); const label = variant.audienceLabel ?? variant.detailLabel ?? cleanEmail(variant.email); return ( -
-
- {label} - - {quotaQuery?.isLoading - ? t('accountCard.quotaLoading') - : quotaValue !== null - ? `${quotaLabel}%` - : failureInfo?.label || t('accountCard.quotaUnavailable')} - -
- {quotaValue !== null && ( -
-
+ + +
+
+ {label} + + {quotaQuery?.isLoading + ? t('accountCard.quotaLoading') + : quotaValue !== null + ? `${quotaLabel}%` + : failureInfo?.label || t('accountCard.quotaUnavailable')} + +
+ {quotaValue !== null && ( +
+
+
+ )}
- )} -
+
+ + + +
); }) : null; diff --git a/ui/src/components/compatible-cli/codex-control-center-tab.tsx b/ui/src/components/compatible-cli/codex-control-center-tab.tsx index 9b701521..727a6e8d 100644 --- a/ui/src/components/compatible-cli/codex-control-center-tab.tsx +++ b/ui/src/components/compatible-cli/codex-control-center-tab.tsx @@ -1,11 +1,10 @@ -import { Route } from 'lucide-react'; +import { FileCode2, History, PenLine, Settings2 } from 'lucide-react'; import { CodexFeaturesCard } from '@/components/compatible-cli/codex-features-card'; import { CodexMcpServersCard } from '@/components/compatible-cli/codex-mcp-servers-card'; import { CodexModelProvidersCard } from '@/components/compatible-cli/codex-model-providers-card'; import { CodexProfilesCard } from '@/components/compatible-cli/codex-profiles-card'; import { CodexProjectTrustCard } from '@/components/compatible-cli/codex-project-trust-card'; import { CodexTopLevelControlsCard } from '@/components/compatible-cli/codex-top-level-controls-card'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { ScrollArea } from '@/components/ui/scroll-area'; import type { CodexConfigPatchInput, @@ -54,26 +53,60 @@ export function CodexControlCenterTab({ }: CodexControlCenterTabProps) { return ( -
- - - - - Structured controls boundary - - - -

- Guided controls write only the user-layer config.toml. They do not model - the full effective Codex runtime once trusted repo layers and CCS transient{' '} - -c overrides are involved. -

-

- Structured saves normalize TOML formatting and strip comments. Use the raw editor on - the right when exact layout matters. -

-
-
+
+
+
+ +
+
+
+
+ +
+
+

+ Structured controls boundary +

+
+
+ +
    +
  • + + + Writes exclusively to user-layer{' '} + + config.toml + + +
  • +
  • + + + Does not reflect repo trust layers or CLI overrides + +
  • +
+
+ +
+
+
+ +
+

+ Formatting Note +

+

+ Saves normalize TOML formatting and strip comments. Switch to the raw editor + if exact layout matters. +

+
+
+
+
+
+
-

- Use ccsxp if you want the built-in CCS Codex provider shortcut on native - Codex. Use the saved recipe below if you want plain codex or a personal - alias like cxp to default to CLIProxy. -

+
+

+ Built-in: Use ccsxp for the CCS provider shortcut. +

+

+ Native: Configure the recipe below to use CLIProxy directly with{' '} + codex. +

+
               {CLIPROXY_NATIVE_CODEX_RECIPE}
             
-
-

- 1. Save the cliproxy provider in your user config. -

-

- 2. Set top-level model_provider to cliproxy. -

-

- 3. Export CLIPROXY_API_KEY in your shell before launching native Codex. -

-
+
    +
  1. + Save the cliproxy provider in your user config. +
  2. +
  3. + Set top-level model_provider to cliproxy. +
  4. +
  5. + Export CLIPROXY_API_KEY before launching native Codex. +
  6. +
@@ -137,11 +141,13 @@ export function CodexDocsTab({ diagnostics }: CodexDocsTabProps) { - {docsReference.notes.map((note, index) => ( -

- - {renderTextWithLinks(note)} -

- ))} + {docsReference.notes.length > 0 && ( +
    + {docsReference.notes.map((note, index) => ( +
  • {renderTextWithLinks(note)}
  • + ))} +
+ )}

Codex docs

diff --git a/ui/src/components/compatible-cli/codex-overview-tab.tsx b/ui/src/components/compatible-cli/codex-overview-tab.tsx index f74cd945..eb73f2eb 100644 --- a/ui/src/components/compatible-cli/codex-overview-tab.tsx +++ b/ui/src/components/compatible-cli/codex-overview-tab.tsx @@ -74,31 +74,23 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) { How Codex works in CCS - -

Codex is a first-class runtime target in CCS, but it stays runtime-only in v1.

-

- ccs-codex and ccsx launch native Codex against your saved - native config, while ccsxp is the opinionated shortcut for{' '} - ccs codex --target codex. -

-

- Plain codex or a personal alias like cxp needs{' '} - model_provider = "cliproxy" plus a matching{' '} - [model_providers.cliproxy] entry if you want CLIProxy as the saved native - default. -

-

- Built-in openai and oss providers are also valid native - defaults and do not need a custom [model_providers] stanza. -

-

- Saved default targets for API profiles and variants still remain on Claude or Droid. -

-

- CCS-backed Codex launches can apply transient -c overrides and inject - CCS_CODEX_API_KEY, so effective runtime values may not match this file - exactly. -

+ +
    +
  • Codex is a first-class, runtime-only target in CCS v1.
  • +
  • + Native config: ccs-codex and ccsx launch + native Codex using your saved defaults. +
  • +
  • + Transient overrides: ccsxp (or{' '} + ccs codex --target codex) uses the CCS provider shortcut. +
  • +
  • + CLIProxy default: To make plain codex use CLIProxy, + set model_provider = "cliproxy" and add the recipe below. +
  • +
  • API profiles continue to default to Claude or Droid.
  • +
@@ -145,32 +137,40 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) { {supportsManagedRouting ? ( <> -

- There are two supported paths. Use ccsxp if you want the built-in CCS - Codex provider shortcut. Use the saved recipe below if you want plain{' '} - codex or a personal alias like cxp to default to - CLIProxy. -

+
+

+ Two supported paths: +

+
    +
  • + Built-in: Use ccsxp for the CCS provider + shortcut. +
  • +
  • + Native: Configure the recipe below to use CLIProxy directly + with codex. +
  • +
+

Saved native Codex recipe

                     {CLIPROXY_NATIVE_CODEX_RECIPE}
                   
-
-

- 1. Save a provider named cliproxy with the base URL and env key - above. -

-

- 2. In Top-level settings, set Default provider{' '} - to cliproxy. -

-

- 3. Export CLIPROXY_API_KEY in your shell before launching native +

    +
  1. + Save a provider named cliproxy with the base URL and env key above. +
  2. +
  3. + In Top-level settings, set Default provider to{' '} + cliproxy. +
  4. +
  5. + Export CLIPROXY_API_KEY in your shell before launching native Codex. -

    -
+ + ) : (

@@ -320,30 +320,44 @@ export function CodexOverviewTab({ diagnostics }: CodexOverviewTabProps) { -

-

Native Codex runtime

-

- Use ccs-codex, ccsx, or --target codex when - you want the local Codex CLI to honor your saved native user config. -

+
+

Native Codex runtime

+
    +
  • + ccs-codex +
  • +
  • + ccsx +
  • +
  • + --target codex +
  • +
+ + Honors saved native user config +
-
-

CCS Codex provider / bridge

-

- {supportsManagedRouting ? ( - <> - Use ccsxp or ccs codex --target codex when you want - the built-in CCS Codex provider on native Codex. That path uses transient - CCS-managed overrides and is separate from the saved cliproxy{' '} - recipe above. - - ) : ( - <> - The CCS Codex provider route is currently unavailable because the detected Codex - build does not expose --config overrides. - - )} -

+
+

CCS Codex provider / bridge

+ {supportsManagedRouting ? ( + <> +
    +
  • + ccsxp +
  • +
  • + ccs codex --target codex +
  • +
+ + Uses transient overrides + + + ) : ( +

+ Unavailable (Codex build lacks --config support). +

+ )}
diff --git a/ui/src/components/compatible-cli/codex-top-level-controls-card.tsx b/ui/src/components/compatible-cli/codex-top-level-controls-card.tsx index 15dc5927..406835e5 100644 --- a/ui/src/components/compatible-cli/codex-top-level-controls-card.tsx +++ b/ui/src/components/compatible-cli/codex-top-level-controls-card.tsx @@ -1,5 +1,6 @@ import { useState } from 'react'; -import { Loader2, SlidersHorizontal } from 'lucide-react'; +import { CircleAlert, Loader2, SlidersHorizontal } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { @@ -14,6 +15,11 @@ import type { CodexTopLevelSettingsView } from '@/lib/codex-config'; import { CodexConfigCardShell } from './codex-config-card-shell'; const UNSET = '__unset__'; +const GPT_54_MAX_CONTEXT_WINDOW = 1_050_000; +const GPT_54_STANDARD_CONTEXT_WINDOW = 272_000; +const CCS_GPT_54_STARTER_CONTEXT_WINDOW = 800_000; +const CCS_GPT_54_STARTER_AUTO_COMPACT_TOKEN_LIMIT = 700_000; +const INTEGER_FORMATTER = new Intl.NumberFormat('en-US'); interface CodexTopLevelControlsCardProps { values: CodexTopLevelSettingsView; @@ -32,6 +38,14 @@ function withCurrentValue(options: string[], current: string | null | undefined) return current && !options.includes(current) ? [current, ...options] : options; } +function formatInteger(value: number) { + return INTEGER_FORMATTER.format(value); +} + +function isGpt54ModelId(value: string | null | undefined) { + return value?.trim().toLowerCase().startsWith('gpt-5.4') ?? false; +} + function buildTopLevelPatch( initialValues: CodexTopLevelSettingsView, draft: CodexTopLevelSettingsView @@ -42,6 +56,12 @@ function buildTopLevelPatch( if (draft.modelReasoningEffort !== initialValues.modelReasoningEffort) { patch.modelReasoningEffort = draft.modelReasoningEffort; } + if (draft.modelContextWindow !== initialValues.modelContextWindow) { + patch.modelContextWindow = draft.modelContextWindow; + } + if (draft.modelAutoCompactTokenLimit !== initialValues.modelAutoCompactTokenLimit) { + patch.modelAutoCompactTokenLimit = draft.modelAutoCompactTokenLimit; + } if (draft.modelProvider !== initialValues.modelProvider) { patch.modelProvider = draft.modelProvider; } @@ -91,6 +111,11 @@ function TopLevelControlsForm({ const personalityOptions = withCurrentValue(['none', 'friendly', 'pragmatic'], draft.personality); const patch = buildTopLevelPatch(initialValues, draft); const hasChanges = Object.keys(patch).length > 0; + const isGpt54Selected = isGpt54ModelId(draft.model); + const parseOptionalInteger = (value: string) => { + const trimmed = value.trim(); + return trimmed.length > 0 ? Number(trimmed) : null; + }; return ( <> @@ -266,6 +291,216 @@ function TopLevelControlsForm({
+
+
+
+
+ +

Long context override

+ + Manual opt-in only + + + {isGpt54Selected ? 'GPT-5.4 selected' : 'GPT-5.4 reference'} + +
+

+ Draft values only. Nothing applies until Save. +

+
+ +
+ + + +
+
+ +
+
+

+ Official max +

+

1.05M / 1M

+

GPT-5.4 context cap

+
+
+

+ Standard window +

+

+ {formatInteger(GPT_54_STANDARD_CONTEXT_WINDOW)} +

+

Normal usage window

+
+
+

+ Above 272K +

+

Counts 2x

+

Usage-limit cost above 272K

+
+
+ +
+
+
+

+ One cautious pair +

+
+ Context {formatInteger(CCS_GPT_54_STARTER_CONTEXT_WINDOW)} +
+
+ Auto-compact {formatInteger(CCS_GPT_54_STARTER_AUTO_COMPACT_TOKEN_LIMIT)} +
+
+
+ + Not official + + + Draft only + +
+
+
+ Quick-fill only. Review before saving. + {!isGpt54Selected && draft.model ? ( + + {draft.model} should be checked separately. + + ) : null} +
+
+ +
+
+

Model context window

+ + setDraft((current) => ({ + ...current, + modelContextWindow: parseOptionalInteger(event.target.value), + })) + } + placeholder="Unset" + disabled={disabled} + /> +

+ Writes model_context_window. Leave unset to keep Codex defaults. +

+
+ +
+

Auto-compact token limit

+ + setDraft((current) => ({ + ...current, + modelAutoCompactTokenLimit: parseOptionalInteger(event.target.value), + })) + } + placeholder="Unset" + disabled={disabled} + /> +

+ Writes model_auto_compact_token_limit. Leave unset to keep model + defaults. +

+
+
+ + +
+