Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7269be2
feat(codex): opt into client-side compaction
cb8010d6 Sep 8, 2026
3c3307e
test(gui): preserve client compaction on sync failure
cb8010d6 Sep 8, 2026
cc49bd6
fix(codex): keep client compaction future-only
cb8010d6 Sep 8, 2026
3c86cfd
docs(codex): tighten compaction guidance
cb8010d6 Sep 8, 2026
ad60bc0
docs(codex): add client compaction UI screenshot
cb8010d6 Sep 8, 2026
dce2b50
docs(codex): scope client compaction guidance
cb8010d6 Sep 8, 2026
178ba36
docs(codex): clarify V2 quota behavior
cb8010d6 Sep 8, 2026
d94c369
fix(codex): tighten compaction review coverage
cb8010d6 Sep 8, 2026
35c0349
test(codex): lock voice-sideband and history-retag contracts for clie…
invalid-email-address Sep 9, 2026
29e6e78
chore(carry): record attribution for the #3987 carry
invalid-email-address Sep 9, 2026
ed89af5
fix(codex): keep existing threads routed when enabling client compaction
lidge-jun Sep 9, 2026
1bb5e93
fix(codex): keep the root override beside the client-compaction table
lidge-jun Sep 9, 2026
066ce4c
docs(codex): scope the routing guarantee to the managed root override
lidge-jun Sep 9, 2026
65d5483
docs(codex): separate the compaction-only contract from the authless …
lidge-jun Sep 9, 2026
8a0b67f
fix(codex): journal the root override the compaction form retains
lidge-jun Sep 9, 2026
49efae6
docs(codex): condition the migrate-back claim on the form you return to
lidge-jun Sep 9, 2026
97841a7
fix(codex): report the mixed compaction result instead of the root-on…
lidge-jun Sep 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 68 additions & 0 deletions docs-site/src/content/docs/guides/codex-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,74 @@ The cache remains bounded; this does not extend retention or recover history the
longer has. HTTP clients must handle the error explicitly and resend their full context without
`previous_response_id`. Retrying only the same ID cannot recover missing state.

### Client-side compaction (opt-in)

Authenticated loopback routing normally keeps Codex on its built-in `openai` provider identity.
That preserves native thread identity, but it also makes Codex request native remote compaction.
When a routed provider cannot return a native compaction blob, OpenCodeX stores the summary in its
own `ocx1:` envelope. Native ChatGPT cannot verify that envelope if OpenCodeX is later removed from
the request path.

On an authenticated loopback route, enable client-side compaction to keep V2 sub-agent routing while preventing new `ocx1:` compaction summaries. Non-loopback and API-key routes retain their existing provider and authentication behavior:

```bash
ocx system settings --client-compaction on # or "codexClientCompaction": true in config.json
ocx sync # rewrites the active config (default: ~/.codex/config.toml); restart Desktop
```

The setting defaults to off. For authenticated loopback routing, OpenCodeX selects its existing
dedicated provider form with `requires_openai_auth = true`. If `codexDesktopAuthless` is also
enabled, that stronger compatibility setting takes precedence and writes
`requires_openai_auth = false`:

```toml
model_provider = "opencodex"

[model_providers.opencodex]
name = "OpenCodex Proxy"
base_url = "http://127.0.0.1:10100/v1"
wire_api = "responses"
requires_openai_auth = true
```

Codex then owns compaction and stores a portable plaintext summary rather than a new OpenCodeX
envelope. The compacting request still routes through OpenCodeX and can consume quota on the
selected provider. V2 sub-agent requests keep their existing provider selection and quota
accounting. Client-side compaction does not change plaintext delivery, encrypted task passthrough
through `allowEncryptedV2AgentTasks`, or configured recovery and fallback behavior.

This preference affects future compactions only, and it rewrites no existing `ocx1:` payload in
any configuration, so use the explicit history recovery workflow for a thread that needs one.

Whether resume-history metadata is re-tagged depends on which form the injection takes. On its
own, on an authenticated loopback bind, client-side compaction re-tags nothing: it keeps the root
override instead, as described below. Enabled together with `codexDesktopAuthless`, or on a
non-loopback bind, the stronger form wins and those forms behave exactly as they do today,
including their existing forward-tagging of resume history with originals backed up for restore.

Going back from one of those forms to plain Design B migrates the re-tagged threads back. Turning
off `codexDesktopAuthless` while leaving client-side compaction on does not: that lands on the
compaction-only form, which skips the history unit, so threads already tagged `opencodex` keep
that tag. They still reach this proxy, through the provider table rather than the root override.

On the compaction-only form, existing threads keep working because the injection keeps the root
`openai_base_url` override alongside the provider table. New threads default to `opencodex` and
get client-side compaction, while a thread already tagged `openai` still resolves to Codex's
built-in provider — which the retained override still points at this proxy. Without it that
thread would resume against OpenAI directly, taking configured routing with it. The authless and
non-loopback forms cannot use the root key, which is why they keep re-tagging instead.

That guarantee covers the override OpenCodeX manages. A root `openai_base_url` you wrote
yourself is never replaced, and in that case the built-in provider keeps the destination you
chose, so an `openai`-tagged thread follows your configuration rather than this proxy. Turning
the setting off and syncing removes the table and returns to the plain Design B override, unless
`codexDesktopAuthless` or non-loopback admission still requires the provider-table form; those
two forms cannot use the root key and are unchanged.

While the mode is active, the realtime voice sideband override
(`experimental_realtime_ws_base_url`) is not injected — the dedicated provider-table form cannot
carry it — so Codex Desktop voice uses its native endpoint rather than the proxy.

### Authless Codex Desktop (opt-in)

In **Dashboard → Overview**, **Open Codex without signing in** controls this existing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ runs helper features around provider requests.
| `codexAutoStart?` | `boolean` | `true` | Let the Codex shim run `ocx ensure` before launching Codex. False makes ensure a no-op. |
| `codexShimAutoRestore?` | `boolean` | `true` | Restore an installed shim after a completed external Codex update replaces it. Environment opt-out: `OPENCODEX_CODEX_SHIM_AUTO_RESTORE=0`. |
| `codexDesktopAuthless?` | `boolean` | `false` | Opt-in authless Codex Desktop routing on a loopback bind: inject the dedicated `opencodex` provider with `requires_openai_auth = false` so Desktop opens without a ChatGPT login. Ignored on non-loopback binds. `ocx system settings --desktop-authless on`. See [Codex integration](/guides/codex-integration/#authless-codex-desktop-opt-in). |
| `codexClientCompaction?` | `boolean` | `false` | Opt into Codex client-side compaction on an authenticated loopback bind. Uses the dedicated `opencodex` provider identity with `requires_openai_auth = true`, preventing new routed compactions from storing OpenCodeX-owned `ocx1:` state. `codexDesktopAuthless` takes precedence when both are enabled and keeps `requires_openai_auth = false`. V2 sub-agent routing is unchanged. `ocx system settings --client-compaction on`. See [Codex integration](/guides/codex-integration/#client-side-compaction-opt-in). |
| `resetCreditAutoRedeem?` | `{ enabled?: boolean; leadTimeMinutes?: number }` | off | Opt-in: redeem the main Codex account's soonest-expiring reset credit `leadTimeMinutes` (1–60, default 10) before it expires. Every attempt re-reads the upstream credit list first and skips when the credit is gone (for example, redeemed by hand); the `redeem_request_id` is journaled in `$OPENCODEX_HOME/reset-credit-auto-redeem.json` before the call so a crash replays the same idempotent request instead of spending a second credit. Servers sharing this configuration directory coordinate reservations and settlements so one process does not replace another's request record. Logs carry a hashed account key only. |
| `syncResumeHistory?` | `boolean` | `true` | Reversible Codex App history compatibility. Original metadata is backed up and restored by `ocx stop` / `ocx restore`. |
| `shadowCallIntercept?` | `{ enabled?: boolean; model?: string; sourceModels?: string[] }` | off | Redirect recognized Codex helper/shadow calls to a chosen model while preserving the request's configured reasoning effort. The default source prefix is `gpt-5.6-luna`; older clients through 0.144.x used `gpt-5.4-mini`, which `sourceModels` can restore. |
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/de.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2486,6 +2486,8 @@ export const de: Record<TKey, string> = {
"dash.visionAdvancedPopover": "Erweiterte Vision-Einstellungen",
"dash.codexDesktopAuthless": "Codex ohne Anmeldung öffnen",
"dash.codexDesktopAuthlessHint": "Standardmäßig aus. Überspringt die separate Desktop-Anmeldung bei geeigneten lokalen Verbindungen. Zugangsdaten für den Anbieter bleiben erforderlich. Codex nach einer Änderung neu starten. Kontogebundene Desktop-Funktionen können fehlen.",
"dash.codexClientCompaction": "Clientseitige Komprimierung verwenden",
"dash.codexClientCompactionHint": "Standardmäßig aus; nur für authentifiziertes Loopback-Routing. Künftige Komprimierungen speichern portable Klartext-Zusammenfassungen, während das OpenCodeX-Provider-Routing und die V2-Subagent-Zustellung aktiv bleiben; der konfigurierte Anbieter kann sie verarbeiten und Kontingent verbrauchen. Vorhandene ocx1-Verläufe müssen weiterhin wiederhergestellt werden. Codex nach einer Änderung neu starten.",
"models.newPolicyGlobal": "Neue Modelle zunächst deaktivieren", "models.newPolicyProvider": "Richtlinie für neue Modelle",
"models.newPolicy_inherit": "Übernehmen", "models.newPolicy_off": "Aus", "models.newPolicy_on": "An", "models.newBadge": "NEU", "models.newCount": "{count} neu, aus",
"models.aliases": "Aliase",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,8 @@ export const en = {
"dash.multiAgent": "Sub-agent",
"dash.codexDesktopAuthless": "Open Codex without signing in",
"dash.codexDesktopAuthlessHint": "Off by default. Skip the separate Desktop sign-in for eligible local connections. Upstream credentials are still required. Restart Codex after changing this setting. Account-gated Desktop features may be unavailable.",
"dash.codexClientCompaction": "Use client-side compaction",
"dash.codexClientCompactionHint": "Off by default; authenticated loopback only. Future compactions store portable plaintext summaries while OpenCodeX and V2 provider routing stay active; the configured provider may process them and consume quota. History is left untouched, and existing threads keep routing through the proxy via the openai_base_url override OpenCodeX manages; if you set that line yourself it is kept, and those threads follow your destination instead. Existing ocx1 history stays recoverable; recover a thread separately only before replaying it in native Codex. Restart Codex after changing this setting.",
"models.v2Conflict": "[agents] max_threads is set — codex will refuse to start; remove it from config.toml",
"models.v2Applied": "Sub-agent mode updated — applies to new sessions (restart the Codex app to refresh the picker)",
"models.v2ThreadsLabel": "Max threads",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/fr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,8 @@ export const fr: Record<TKey, string> = {
"dash.multiAgent": "Sous-agent",
"dash.codexDesktopAuthless": "Ouvrir Codex sans se connecter",
"dash.codexDesktopAuthlessHint": "Désactivé par défaut. Ignore la connexion Desktop séparée pour les connexions locales admissibles. Les identifiants du fournisseur restent nécessaires. Redémarrez Codex après toute modification. Certaines fonctions Desktop liées au compte peuvent être indisponibles.",
"dash.codexClientCompaction": "Utiliser la compaction côté client",
"dash.codexClientCompactionHint": "Désactivé par défaut, uniquement pour le routage loopback authentifié. Les compactages futurs stockent des résumés portables en texte clair tout en conservant le routage OpenCodeX/V2 ; le fournisseur configuré peut les traiter et consommer son quota. L'historique ocx1 existant doit toujours être restauré. Redémarrez Codex après modification.",
"models.v2Conflict": "[agents] max_threads est défini — codex refusera de démarrer ; supprimez-le de config.toml",
"models.v2Applied": "Mode sous-agent mis à jour — s’applique aux nouvelles sessions (redémarrez l’application Codex pour actualiser le sélecteur)",
"models.v2ThreadsLabel": "Nombre maximal de fils",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/ja.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2507,6 +2507,8 @@ export const ja: Record<TKey, string> = {
"dash.visionAdvancedPopover": "詳細なビジョン設定",
"dash.codexDesktopAuthless": "ログインせずに Codex を開く",
"dash.codexDesktopAuthlessHint": "既定ではオフです。対象のローカル接続で Desktop の個別ログインを省略します。上流プロバイダーの認証情報は引き続き必要です。変更後は Codex を再起動してください。アカウントに依存する Desktop 機能が利用できない場合があります。",
"dash.codexClientCompaction": "クライアント側コンパクションを使用",
"dash.codexClientCompactionHint": "既定ではオフで、認証済みループバックルーティング専用です。今後のコンパクションは、OpenCodeX と V2 プロバイダーのルーティングを維持したまま移植可能な平文要約を保存します。設定済みプロバイダーが要約を処理し、割り当てを消費する場合があります。既存の ocx1 履歴は別途復旧が必要です。変更後は Codex を再起動してください。",
"models.newPolicyGlobal": "新しいモデルを無効で追加", "models.newPolicyProvider": "新しいモデルのポリシー",
"models.newPolicy_inherit": "継承", "models.newPolicy_off": "オフ", "models.newPolicy_on": "オン", "models.newBadge": "新着", "models.newCount": "新着 {count} 件、オフ",
"models.aliases": "エイリアス",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/ko.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2508,6 +2508,8 @@ export const ko: Record<TKey, string> = {
"dash.visionAdvancedPopover": "고급 비전 설정",
"dash.codexDesktopAuthless": "로그인 없이 Codex 열기",
"dash.codexDesktopAuthlessHint": "기본값은 꺼짐입니다. 지원되는 로컬 연결에서 별도의 Desktop 로그인을 건너뜁니다. 업스트림 인증 정보는 여전히 필요합니다. 변경 후 Codex를 다시 시작하세요. 계정에 연결된 Desktop 기능을 사용하지 못할 수 있습니다.",
"dash.codexClientCompaction": "클라이언트 측 컴팩션 사용",
"dash.codexClientCompactionHint": "기본값은 꺼짐이며 인증된 루프백 라우팅에만 적용됩니다. 향후 컴팩션은 OpenCodeX 및 V2 제공자 라우팅을 유지하면서 이식 가능한 평문 요약을 저장합니다. 설정된 제공자가 요약을 처리하고 할당량을 사용할 수 있습니다. 기록은 건드리지 않으며, 기존 스레드는 OpenCodeX가 관리하는 openai_base_url override를 통해 프록시 경로를 유지합니다. 그 줄을 직접 설정해 두셨다면 그대로 보존하므로 해당 스레드는 설정하신 목적지를 따릅니다. 기존 ocx1 기록은 그대로 복구할 수 있고, 네이티브 Codex에서 해당 스레드를 재개하기 전에만 별도로 복구하세요. 변경 후 Codex를 다시 시작하세요.",
"models.newPolicyGlobal": "새 모델을 비활성화 상태로 추가", "models.newPolicyProvider": "새 모델 정책",
"models.newPolicy_inherit": "상속", "models.newPolicy_off": "끔", "models.newPolicy_on": "켬", "models.newBadge": "신규", "models.newCount": "신규 {count}개, 꺼짐",
"models.aliases": "별칭",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/ru.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2509,6 +2509,8 @@ export const ru: Record<TKey, string> = {
"dash.visionAdvancedPopover": "Дополнительные настройки изображений",
"dash.codexDesktopAuthless": "Открывать Codex без входа",
"dash.codexDesktopAuthlessHint": "По умолчанию выключено. Пропускает отдельный вход в Desktop для допустимых локальных подключений. Учётные данные провайдера по-прежнему нужны. После изменения перезапустите Codex. Функции Desktop, связанные с аккаунтом, могут быть недоступны.",
"dash.codexClientCompaction": "Использовать сжатие на стороне клиента",
"dash.codexClientCompactionHint": "По умолчанию выключено; только для аутентифицированной loopback-маршрутизации. Будущие сжатия сохраняют переносимые текстовые сводки, а маршрутизация OpenCodeX и V2 остаётся активной; настроенный провайдер может обрабатывать сводки и расходовать квоту. Существующую историю ocx1 всё равно нужно восстановить. После изменения перезапустите Codex.",
"models.newPolicyGlobal": "Добавлять новые модели выключенными", "models.newPolicyProvider": "Политика новых моделей",
"models.newPolicy_inherit": "Наследовать", "models.newPolicy_off": "Выкл.", "models.newPolicy_on": "Вкл.", "models.newBadge": "НОВАЯ", "models.newCount": "Новых: {count}, выкл.",
"models.aliases": "Псевдонимы",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/tr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2509,6 +2509,8 @@ export const tr: Record<TKey, string> = {
"dash.visionAdvancedPopover": "Gelişmiş görsel ayarları",
"dash.codexDesktopAuthless": "Codex’i oturum açmadan başlat",
"dash.codexDesktopAuthlessHint": "Varsayılan olarak kapalıdır. Uygun yerel bağlantılarda ayrı Desktop oturum açma adımını atlar. Sağlayıcı kimlik bilgileri yine gereklidir. Değişiklikten sonra Codex’i yeniden başlatın. Hesaba bağlı Desktop özellikleri kullanılamayabilir.",
"dash.codexClientCompaction": "İstemci tarafı sıkıştırmayı kullan",
"dash.codexClientCompactionHint": "Varsayılan olarak kapalıdır ve yalnızca kimliği doğrulanmış geri döngü yönlendirmesinde geçerlidir. Gelecekteki sıkıştırmalar, OpenCodeX ve V2 sağlayıcı yönlendirmesi etkin kalırken taşınabilir düz metin özetleri kaydeder; yapılandırılmış sağlayıcı bunları işleyip kotasını tüketebilir. Mevcut ocx1 geçmişi yine ayrıca kurtarılmalıdır. Değişiklikten sonra Codex’i yeniden başlatın.",
"models.newPolicyGlobal": "Yeni modeller devre dışı başlasın", "models.newPolicyProvider": "Yeni model ilkesi",
"models.newPolicy_inherit": "Devral", "models.newPolicy_off": "Kapalı", "models.newPolicy_on": "Açık", "models.newBadge": "YENİ", "models.newCount": "{count} yeni, kapalı",
"models.aliases": "Takma adlar",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/zh-TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2471,6 +2471,8 @@ export const zhTW: Record<TKey, string> = {
"dash.visionAdvancedPopover": "進階視覺設定",
"dash.codexDesktopAuthless": "無需登入即可開啟 Codex",
"dash.codexDesktopAuthlessHint": "預設關閉。為符合條件的本機連線略過獨立的 Desktop 登入。仍需上游供應商憑證。變更後請重新啟動 Codex。依賴帳戶的 Desktop 功能可能無法使用。",
"dash.codexClientCompaction": "使用用戶端壓縮",
"dash.codexClientCompactionHint": "預設關閉,僅適用於已驗證的 loopback 路由。未來壓縮會儲存可攜的純文字摘要,同時保留 OpenCodeX 與 V2 提供方路由;已設定的提供方可能處理摘要並消耗其額度。既有 ocx1 歷程仍須另行復原。變更後請重新啟動 Codex。",
"models.newPolicyGlobal": "新模型預設停用", "models.newPolicyProvider": "新模型策略",
"models.newPolicy_inherit": "繼承", "models.newPolicy_off": "關閉", "models.newPolicy_on": "開啟", "models.newBadge": "新增", "models.newCount": "{count} 個新增,已關閉",
"models.aliases": "別名",
Expand Down
2 changes: 2 additions & 0 deletions gui/src/i18n/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2507,6 +2507,8 @@ export const zh: Record<TKey, string> = {
"dash.visionAdvancedPopover": "高级视觉设置",
"dash.codexDesktopAuthless": "无需登录即可打开 Codex",
"dash.codexDesktopAuthlessHint": "默认关闭。为符合条件的本地连接跳过单独的 Desktop 登录。仍需上游提供商凭据。更改后请重启 Codex。依赖账户的 Desktop 功能可能不可用。",
"dash.codexClientCompaction": "使用客户端压缩",
"dash.codexClientCompactionHint": "默认关闭,仅适用于已认证的 loopback 路由。未来压缩会保存可移植的明文摘要,同时保留 OpenCodeX 与 V2 提供方路由;已配置的提供方可能处理摘要并消耗其额度。已有 ocx1 历史仍需单独恢复。更改后请重启 Codex。",
"models.newPolicyGlobal": "新模型默认停用", "models.newPolicyProvider": "新模型策略",
"models.newPolicy_inherit": "继承", "models.newPolicy_off": "关闭", "models.newPolicy_on": "开启", "models.newBadge": "新增", "models.newCount": "{count} 个新增,已关闭",
"models.aliases": "别名",
Expand Down
Loading
Loading