Skip to content

Instantly share code, notes, and snippets.

@brunolemos
Last active June 26, 2026 15:12
Show Gist options
  • Select an option

  • Save brunolemos/7466058059eae140a57a7c6a42f235ae to your computer and use it in GitHub Desktop.

Select an option

Save brunolemos/7466058059eae140a57a7c6a42f235ae to your computer and use it in GitHub Desktop.
Codex Desktop feature flag runtime overrides

Codex Desktop feature flag overrides

Runtime-only scripts for experimenting with hidden Codex Desktop UI feature flags. They launch Codex with a local Chrome DevTools Protocol port and inject renderer feature overrides. They do not modify Codex.app.

Run

First quit Codex Desktop. Only one desktop instance can run at a time, and the launcher needs to reopen it with the override hook.

Option A: ask the codex CLI to run it for you. Paste this into Terminal:

codex 'Run this Codex Desktop feature flag override: https://gist.github.com/brunolemos/7466058059eae140a57a7c6a42f235ae. Do not modify /Applications/Codex.app.'

Option B: run it yourself:

gh gist clone 7466058059eae140a57a7c6a42f235ae codex-feature-overrides
cd codex-feature-overrides
chmod +x launch-codex-feature-overrides.sh
./launch-codex-feature-overrides.sh

To disable the renderer overrides, quit Codex and launch it normally.

Some features also need app-server flags in ~/.codex/config.toml, for example:

[features]
realtime_conversation = true

Important: some UI flags only expose client UI. The backend may still reject unreleased features for your account/workspace.

Flags

This is not an official feature-flag registry. It is a local scan of the installed Codex Desktop build plus strings from the bundled app-server binary. It should be treated as "observed in this build," not a guarantee that every remote-only rollout flag exists here.

The override script intentionally enables only the small useful subset below. Turning on every observed flag would be noisy and risky.

Useful overrides

Kind Flag/config key Friendly name What it probably does Confidence / notes
Statsig gate 2551582477 Message rail / message timeline Shows a vertical rail for jumping to user-sent messages in a long Codex chat. Verified UI gate; this was the feature from the X thread.
Statsig gate 2380644311 Realtime voice conversation Enables the realtime voice entry point in the Codex avatar/pet overlay. Verified UI gate; backend access may still 404.
Statsig gate 3563904085 Native avatar overlay Enables the native floating avatar/pet overlay surface. Verified UI gate.
Statsig gate 451951815 Avatar compact requests Enables compact request/activity cards in the avatar overlay. Inferred from minified source and behavior.
Statsig gate 665486075 Avatar overlay / quick chat Enables the avatar overlay surface and quick chat affordance. Verified UI gate.
Statsig gate 1025755912 Dictation cleanup Enables cleanup/post-processing behavior for dictation text. Inferred from source imports and strings.
Statsig gate codex-app-dictation-streaming Streaming dictation Enables streaming dictation/transcription path. Named flag imported by the dictation/transcription modules.
Dynamic config 1193530394 Realtime voice config Carries realtime collaboration/continuity config and prompt behavior. Verified dynamic config shape.
App-server feature features.realtime_conversation Realtime conversation Enables local app-server handling for thread/realtime/* methods. Verified via experimentalFeature/list; backend can still reject session creation.

Observed renderer Statsig keys

These are gates/configs/layers/experiments referenced by the minified renderer bundle. Most numeric names are Statsig-stable IDs, so the best local clue is the chunk that references them.

98 renderer keys observed in this build
Kind Key Likely area Evidence chunk
Statsig gate 1025755912 Avatar, realtime voice, or dictation UI avatar-overlay-native-page-BQoNWTAx.js, global-dictation-page-BV05fEU5.js, use-dictation-DQROZBbT.js
Statsig gate 1042620455 Remote/cloud environment selection remote-connection-visibility-CzDH6oqC.js
Dynamic config 107580212 Unknown from minified bundle model-queries-CbKX4hvn.js
Statsig gate 1115442235 Composer, conversation, or thread UI composer-external-footer-C2Y9blhm.js, local-conversation-thread-tEA3qvi1.js
Dynamic config 1193530394 Avatar, realtime voice, or dictation UI realtime-conversation-config-ldDeUVCM.js
Statsig gate 1221508807 Unknown from minified bundle use-is-background-subagents-enabled-dspIC_V1.js
Statsig gate 12346831 App shell / boot routing app-main-Dldh3K_n.js, header-DgzE38hF.js
Statsig gate 1244621283 Avatar, realtime voice, or dictation UI app-main-Dldh3K_n.js, general-settings-U7DFIZBC.js, keyboard-shortcuts-settings-BuLGCo4B.js
Statsig gate 1258561229 Composer, conversation, or thread UI pending-request-item-panel-BIpmxdaQ.js
Statsig gate 1269116100 Plugins, MCP apps, or app connections app-prefetch-impl-DEaoqJMj.js, mention-metadata-syncer-CqO3dJsO.js, plugin-detail-page-C-S4JUEx.js
Statsig gate 1304276663 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 1372061905 Settings/profile/personalization app-main-Dldh3K_n.js, general-settings-U7DFIZBC.js, keyboard-shortcuts-settings-BuLGCo4B.js
Statsig gate 1378180112 Composer, conversation, or thread UI pending-request-item-panel-BIpmxdaQ.js
Statsig gate 1420162012 Git, review, or PR UI review-file-source-tab-DGKieCSd.js
Statsig gate 1444479692 Settings/profile/personalization personalization-settings-CgsRnWnO.js, use-personality-BfBCBHKs.js
Statsig gate 1488233300 Automations app-main-Dldh3K_n.js, automation-dialog-BSyEjDd8.js, automations-page-BFQuKuEO.js
Statsig gate 1506311413 Plugins, MCP apps, or app connections use-is-plugins-enabled-Dn_J-WZf.js
Statsig gate 1579719221 Composer, conversation, or thread UI local-conversation-thread-tEA3qvi1.js
Statsig gate 1741944562 App generation or artifacts appgen-gating-C3NQrPIO.js
Statsig gate 1767204071 Plan, usage, or rate limits rate-limit-summary-BUSuPbw3.js
Statsig gate 1823130936 Composer, conversation, or thread UI composer-DlMDPaCL.js
Statsig gate 1823918333 Composer, conversation, or thread UI app-main-Dldh3K_n.js, composer-DlMDPaCL.js, profile-dropdown-DP7ZIWri.js
Statsig gate 1834314516 Composer, conversation, or thread UI app-main-Dldh3K_n.js, browser-profile-import-dialog-D1qMiO6m.js, browser-use-settings-CITKdwyy.js
Statsig gate 1840974662 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 1848317837 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 1857002365 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 1907601843 Remote/cloud environment selection composer-DlMDPaCL.js, local-remote-dropdown-BX0mMvJK.js, local-remote-selection-DRnEOc8g.js
Statsig gate 1991660486 Settings/profile/personalization profile-CLgRQZ35.js
Layer 2096615506 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2106641128 Settings/profile/personalization agent-settings-CvASSGEK.js
Layer 2138468235 Composer, conversation, or thread UI local-conversation-thread-tEA3qvi1.js
Statsig gate 2153867414 Remote/cloud environment selection remote-connections-settings-B6vDfO5t.js
Statsig gate 2171042036 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2212532336 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2248542797 Plugins, MCP apps, or app connections plugin-detail-page-C-S4JUEx.js
Statsig gate 2256010998 Remote/cloud environment selection local-remote-dropdown-BX0mMvJK.js
Statsig gate 2326764170 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2327881676 Composer, conversation, or thread UI app-main-Dldh3K_n.js, thread-side-panel-tabs-CLuB2SaS.js
Statsig gate 2337831332 Unknown from minified bundle sidebar-project-hover-card-source-rows-DtE7St1r.js
Statsig gate 2380644311 Avatar, realtime voice, or dictation UI app-main-Dldh3K_n.js, debug-modal-DFKoe0qr.js, general-settings-U7DFIZBC.js
Statsig gate 2423536643 Settings/profile/personalization app-main-Dldh3K_n.js, general-settings-U7DFIZBC.js
Statsig gate 2425897452 Settings/profile/personalization app-main-Dldh3K_n.js, general-settings-U7DFIZBC.js, home-announcements-ys_LB9Gg.js
Statsig gate 2484414311 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2551582477 Composer, conversation, or thread UI local-conversation-thread-tEA3qvi1.js
Statsig gate 2553306736 Settings/profile/personalization git-settings-Bv6LnZbi.js
Statsig gate 2574306096 Unknown from minified bundle chronicle-setup-state-BH1UQGSM.js
Dynamic config 2633172918 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2664309699 Remote/cloud environment selection cloud-environments-settings-page-Cx0Mfko0.js
Statsig gate 2761268526 Plugins, MCP apps, or app connections use-app-connect-flow-B7CQFxUf.js, use-plugin-install-flow-DDuwrr2c.js
Statsig gate 2764989143 Settings/profile/personalization git-settings-Bv6LnZbi.js
Statsig gate 2791276931 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2798711298 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2846336681 Unknown from minified bundle use-permissions-mode-H7h2gKaw.js
Statsig gate 2882842607 Composer, conversation, or thread UI thread-side-panel-tabs-CLuB2SaS.js
Statsig gate 2929582856 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 2957382457 Plugins, MCP apps, or app connections app-main-Dldh3K_n.js, local-conversation-turn-BmqVwWdf.js, markdown-DiCZL58u.js
Statsig gate 3075919032 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 3079718369 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 3162484136 Settings/profile/personalization profile-CLgRQZ35.js
Statsig gate 3194650870 Composer, conversation, or thread UI composer-DlMDPaCL.js, local-conversation-turn-BmqVwWdf.js
Statsig gate 3207467860 Settings/profile/personalization app-main-Dldh3K_n.js, settings-page-m3AjVjtg.js
Statsig gate 3264431617 Composer, conversation, or thread UI app-main-Dldh3K_n.js, keyboard-shortcuts-settings-BuLGCo4B.js, local-conversation-thread-tEA3qvi1.js
Dynamic config 330064871 Plugins, MCP apps, or app connections plugin-detail-page-C-S4JUEx.js
Statsig gate 3314958849 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 3502353992 Settings/profile/personalization profile-dropdown-DP7ZIWri.js
Statsig gate 3563904085 Avatar, realtime voice, or dictation UI app-main-Dldh3K_n.js, avatar-overlay-featuregate-page-CRiP1JP-.js, global-dictation-page-BV05fEU5.js
Statsig gate 3736891373 Unknown from minified bundle use-permissions-mode-H7h2gKaw.js
Statsig gate 3765605143 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 3789238711 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 3903563814 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 3909937021 Composer, conversation, or thread UI composer-DlMDPaCL.js, use-usage-settings-access-Bq_PKiLc.js
Statsig gate 410065390 Plugins, MCP apps, or app connections codex-mobile-setup-dialog-CubQheHr.js, use-is-plugins-enabled-Dn_J-WZf.js
Statsig gate 4100906017 Avatar, realtime voice, or dictation UI app-main-Dldh3K_n.js, general-settings-U7DFIZBC.js, keyboard-shortcuts-settings-BuLGCo4B.js
Statsig gate 410262010 Plugins, MCP apps, or app connections use-is-plugins-enabled-Dn_J-WZf.js
Statsig gate 4114442250 Remote/cloud environment selection remote-connection-visibility-CzDH6oqC.js
Statsig gate 4132970629 Onboarding/home suggestions home-ambient-suggestions-content-0qjD7n9w.js
Layer 4147559047 Onboarding/home suggestions onboarding-page-LcCVsmiu.js
Statsig gate 4214671466 Onboarding/home suggestions home-ambient-suggestions-content-0qjD7n9w.js
Statsig gate 4218407052 Plugins, MCP apps, or app connections plugin-detail-page-C-S4JUEx.js, plugins-page-C7TVG2H9.js, use-plugins-Dex8E4w_.js
Statsig gate 4285716042 Onboarding/home suggestions home-ambient-suggestions-content-0qjD7n9w.js, new-chat-page-suggestion-data-B6nMEWOH.js
Statsig gate 451951815 Avatar, realtime voice, or dictation UI avatar-overlay-native-page-BQoNWTAx.js, avatar-overlay-page-Dvr9Lgpo.js
Statsig gate 459748632 Automations app-main-Dldh3K_n.js, heartbeat-automation-eligibility-C1_JL34W.js
Statsig gate 479474474 Remote/cloud environment selection cloud-environments-settings-page-Cx0Mfko0.js
Statsig gate 489124297 Composer, conversation, or thread UI thread-side-panel-tabs-CLuB2SaS.js
Statsig gate 505458 Remote/cloud environment selection composer-DlMDPaCL.js, hotkey-window-home-page-YLbKpnoq.js, local-remote-dropdown-BX0mMvJK.js
Statsig gate 584954669 Composer, conversation, or thread UI plan-summary-item-content-BsESCXw0.js
Statsig gate 614250066 App shell / boot routing app-main-Dldh3K_n.js
Statsig gate 637432221 App generation or artifacts app-main-Dldh3K_n.js, appgen-gating-C3NQrPIO.js
Statsig gate 665486075 Avatar, realtime voice, or dictation UI avatar-overlay-native-page-BQoNWTAx.js
Dynamic config 71832722 Settings/profile/personalization general-settings-U7DFIZBC.js
Layer 72216192 Settings/profile/personalization app-main-Dldh3K_n.js, general-settings-U7DFIZBC.js
Statsig gate 782640499 Remote/cloud environment selection remote-connections-settings-B6vDfO5t.js
Statsig gate 824038554 Mobile setup app-main-Dldh3K_n.js, codex-mobile-setup-dialog-CubQheHr.js, codex-mobile-setup-flow-NuuQuXft.js
Statsig gate 839469903 App generation or artifacts artifact-tab-content.electron-CyWdVlct.js
Statsig gate 875176429 Composer, conversation, or thread UI composer-DlMDPaCL.js, personalization-settings-CgsRnWnO.js
Statsig gate 896050304 Git, review, or PR UI use-is-pull-request-merge-helper-enabled-CrfHuIjo.js
Statsig gate 900122030 Unknown from minified bundle login-route-Cx8NmtG2.js
Statsig gate 941128545 App shell / boot routing app-main-Dldh3K_n.js

Observed app-server experimental features

These are app-server feature names found in the bundled codex binary or previously observed through the local experimental feature list. Most can be represented as [features] name = true in ~/.codex/config.toml, though account/workspace/backend rollout can still block runtime behavior.

80 app-server feature keys observed in this build
Feature What it probably does
shell_tool Enables shell command execution tooling.
secret_auth_store Stores/loads local auth secrets.
shell_zsh_fork Uses the packaged zsh fork path for shell sessions.
unified_exec_zsh_fork Uses the packaged zsh fork with unified exec.
shell_snapshot Captures shell state/snapshots.
deferred_executor Defers tool/server loading until needed.
js_repl Enables a JS/Node REPL tool path.
code_mode Enables code-focused mode behavior.
code_mode_only Restricts sessions to code-mode behavior.
js_repl_tools_only Restricts/chooses JS REPL tools in supported contexts.
terminal_resize_reflow Reflows terminal output when terminal dimensions change.
web_search_request Allows request-time web search.
web_search_cached Allows cached/indexed web-search mode.
standalone_web_search Enables standalone web-search tooling.
search_tool Enables first-party search tool plumbing.
runtime_metrics Collects runtime/tool/session metrics.
sqlite Enables SQLite-backed local state.
memories Lets Codex create and use memory summaries across conversations.
local_thread_store_compression Compresses local persisted thread data.
chronicle Enables Codex Chronicle/history-related state.
apply_patch_freeform Enables the freeform apply_patch tool schema.
apply_patch_streaming_events Streams patch-application progress/events.
exec_permission_approvals Adds granular approvals for command execution.
request_permissions_tool Lets Codex request additional permissions during a thread.
use_linux_sandbox_bwrap Uses Bubblewrap sandboxing on Linux.
use_legacy_landlock Uses the older Linux Landlock sandbox path.
request_rule Enables request/permission rule handling.
experimental_windows_sandbox Enables experimental Windows sandboxing.
elevated_windows_sandbox Enables elevated Windows sandbox support.
remote_models Enables remote model-provider/model-catalog handling.
enable_request_compression Compresses requests where supported.
network_proxy Applies network proxy restrictions to sandboxed sessions with network access.
respect_system_proxy Uses OS/system proxy settings for network traffic.
multi_agent Original multi-agent orchestration path.
multi_agent_v2 Newer multi-agent orchestration path.
enable_fanout Enables delegation/fan-out behavior for parallel agent work.
apps Enables app/connector plumbing.
enable_mcp_apps Enables MCP-backed app integrations.
apps_mcp_path_override Allows overriding app MCP server paths.
tool_search Enables deferred tool discovery/search.
tool_search_always_defer_mcp_tools Defers MCP tool loading until searched/requested.
non_prefixed_mcp_tool_names Allows MCP tools without namespace prefixes.
unavailable_dummy_tools Represents unavailable tools with placeholders.
plugins Enables Codex plugin system.
plugin_hooks Enables plugin-provided hooks.
in_app_browser Enables built-in browser surface.
browser_use Lets Codex use/control browser contexts.
browser_use_external Enables external browser control paths.
computer_use Enables local computer-use integration.
plugin_sharing Enables remote plugin sharing flows.
external_migration Enables importing/migrating external agent configs/sessions.
imagegenext Experimental image-generation path.
resize_all_images Resizes images before sending/processing.
skill_env_var_dependency_prompt Adds prompts/instructions for skill environment-variable dependencies.
steer Enables steering/system behavior controls.
default_mode_request_user_input Allows request_user_input outside plan-mode-only flows.
terminal_visualization_instructions Adds model instructions for terminal visualizations.
guardian_approval Enables guardian/security approval flow.
goals Enables thread goals.
token_budget Enables explicit thread/goal token budgets.
rollout_budget Adds budget accounting around rollout/session storage.
current_time_reminder Injects current time reminders/context.
sleep_tool Enables wait/sleep behavior for scheduled waits.
artifact Enables the experimental artifact surface.
remote_control Enables remote-control pairing/client functionality.
collaboration_modes Enables plan/default/collaboration-mode handling.
tool_call_mcp_elicitation Enables MCP tool elicitation/user-input prompts.
personality Enables personality profile handling.
fast_mode Enables fast-mode behavior/telemetry.
realtime_conversation Enables local app-server handling for realtime voice conversation.
image_detail_original Allows original-detail image handling.
tui_app_server Enables app-server integration for TUI/CLI.
prevent_idle_sleep Keeps the machine awake while Codex is running work.
workspace_owner_usage_nudge Shows workspace owner usage/credits nudges.
responses_websockets Uses websocket transport for Responses API calls.
responses_websockets_v2 Newer websocket transport path for Responses API calls.
auto_compaction Automatically compacts long conversations.
remote_compaction_v2 Newer remote compaction path.
use_agent_identity Uses agent identity auth/credentials where supported.
workspace_dependencies Enables bundled workspace dependency/runtime discovery.

Observed app-server feature parameters

Config key What it probably controls
features.current_time_reminder.clock_source Chooses the current-time source.
features.current_time_reminder.reminder_interval_model_requests Controls how often current-time reminders are injected.
features.multi_agent_v2.default_wait_timeout_ms Default wait timeout for multi-agent v2.
features.multi_agent_v2.max_concurrent_threads_per_session Max concurrent child threads per multi-agent session.
features.multi_agent_v2.max_wait_timeout_ms Max allowed multi-agent v2 wait timeout.
features.multi_agent_v2.min_wait_timeout_ms Min allowed multi-agent v2 wait timeout.
features.multi_agent_v2.tool_namespace Tool namespace used by multi-agent v2.
features.rollout_budget.limit_tokens Token limit for rollout budget.
features.rollout_budget.reminder_at_remaining_tokens When rollout-budget reminders fire.
features.token_budget.reminder_message_template Template for token-budget reminder messages.
features.token_budget.reminder_threshold_tokens Remaining-token threshold for token-budget reminders.
agents.job_max_runtime_seconds Maximum agent job runtime.

Notes

  • This is an experiment helper, not a supported Codex API.
  • Gating can happen in multiple places: renderer Statsig gates, app-server experimental features, and backend account/workspace rollout.
  • If a feature appears in the UI but fails at runtime, check ~/.codex/logs_2.sqlite for app-server/backend errors.
#!/usr/bin/env node
const port = Number.parseInt(process.argv[2] ?? "9322", 10);
const deadline = Date.now() + Number.parseInt(process.argv[3] ?? "30000", 10);
const endpoint = `http://127.0.0.1:${port}/json/list`;
const openAvatarOverlay = process.env.CODEX_OPEN_AVATAR_OVERLAY === "1";
const overrideSource = String.raw`
(() => {
const overrideVersion = "codex-local-feature-overrides-v1";
const gateOverrides = new Map([
["2551582477", "message-rail"],
["2380644311", "realtime-voice-conversation"],
["3563904085", "native-avatar-overlay"],
["451951815", "avatar-overlay-compact-requests"],
["665486075", "avatar-overlay"],
["1025755912", "dictation-cleanup"],
["codex-app-dictation-streaming", "streaming-dictation"],
]);
const dynamicConfigOverrides = new Map([
["1193530394", "realtime-voice-config"],
]);
function overrideDynamicConfig(config) {
if (!dynamicConfigOverrides.has(config?.name)) return null;
const value = config?.value && typeof config.value === "object" ? config.value : {};
return {
...config,
value: {
...value,
realtime_collaboration: {
...(value.realtime_collaboration && typeof value.realtime_collaboration === "object"
? value.realtime_collaboration
: {}),
enabled: true,
},
realtime_continuity_enabled: true,
},
ruleID: config?.ruleID ?? "local-" + dynamicConfigOverrides.get(config.name) + "-override",
};
}
function getStatsigClients() {
const statsig = globalThis.__STATSIG__;
if (!statsig) return [];
const clients = [];
if (statsig.firstInstance) clients.push(statsig.firstInstance);
if (statsig.instance) clients.push(statsig.instance);
for (const instance of Object.values(statsig.instances ?? {})) {
clients.push(instance);
}
return [...new Set(clients)].filter((client) => {
return client && (
typeof client.checkGate === "function" ||
typeof client.getFeatureGate === "function" ||
typeof client.getDynamicConfig === "function"
);
});
}
function installOverride(client) {
if (!client || client.__codexLocalFeatureOverrideVersion === overrideVersion) return false;
const previous = client.overrideAdapter;
const originalCheckGate = client.checkGate?.bind(client);
const originalGetFeatureGate = client.getFeatureGate?.bind(client);
const originalGetDynamicConfig = client.getDynamicConfig?.bind(client);
client.overrideAdapter = {
getGateOverride(gate, user, options) {
if (gateOverrides.has(gate?.name)) {
return {
...gate,
value: true,
ruleID: "local-" + gateOverrides.get(gate.name) + "-override",
};
}
return previous?.getGateOverride?.(gate, user, options) ?? null;
},
getDynamicConfigOverride(config, user, options) {
return overrideDynamicConfig(config)
?? previous?.getDynamicConfigOverride?.(config, user, options)
?? null;
},
getExperimentOverride: previous?.getExperimentOverride?.bind(previous),
getLayerOverride: previous?.getLayerOverride?.bind(previous),
getParamStoreOverride: previous?.getParamStoreOverride?.bind(previous),
};
if (originalCheckGate) {
client.checkGate = function checkGate(name, options) {
if (gateOverrides.has(name)) return true;
return originalCheckGate(name, options);
};
}
if (originalGetFeatureGate) {
client.getFeatureGate = function getFeatureGate(name, options) {
const result = originalGetFeatureGate(name, options);
if (!gateOverrides.has(name)) return result;
return {
...result,
name,
value: true,
ruleID: result?.ruleID ?? "local-" + gateOverrides.get(name) + "-override",
};
};
}
if (originalGetDynamicConfig) {
client.getDynamicConfig = function getDynamicConfig(name, options) {
const result = originalGetDynamicConfig(name, options);
return overrideDynamicConfig(result) ?? result;
};
}
client.__codexLocalFeatureOverrideVersion = overrideVersion;
client.__codexLocalFeatureOverrideGateNames = [...gateOverrides.keys()];
client.__codexLocalFeatureOverrideDynamicConfigNames = [...dynamicConfigOverrides.keys()];
try {
client.updateUserSync(client.getContext().user, { disableBackgroundCacheRefresh: true });
} catch {}
try {
client.$emt?.({ name: "values_updated" });
} catch {}
return true;
}
function installAllOverrides() {
return getStatsigClients().filter(installOverride).length;
}
if (!installAllOverrides()) {
const interval = setInterval(() => {
if (installAllOverrides()) clearInterval(interval);
}, 250);
setTimeout(() => clearInterval(interval), 30000);
}
globalThis.__codexLocalFeatureOverrideProbe = () => {
const clients = getStatsigClients();
return {
installed: clients.length > 0 && clients.every((client) => client.__codexLocalFeatureOverrideVersion === overrideVersion),
gates: Object.fromEntries([...gateOverrides.keys()].map((gateName) => [
gateName,
clients.map((client) => client.checkGate?.(gateName, { disableExposureLog: true })),
])),
dynamicConfigs: Object.fromEntries([...dynamicConfigOverrides.keys()].map((configName) => [
configName,
clients.map((client) => {
const value = client.getDynamicConfig?.(configName, { disableExposureLog: true })?.value;
return {
realtimeCollaborationEnabled: value?.realtime_collaboration?.enabled ?? null,
realtimeContinuityEnabled: value?.realtime_continuity_enabled ?? null,
hasPrompt: typeof value?.prompt === "string" && value.prompt.length > 0,
};
}),
])),
clientCount: clients.length,
};
};
return true;
})()
`;
async function main() {
const injectedTargets = new Set();
let openedAvatarOverlay = false;
let lastInjectedAt = 0;
let lastError = null;
while (Date.now() < deadline) {
let targets = [];
try {
targets = await listTargets();
} catch (error) {
lastError = error;
await delay(250);
continue;
}
for (const target of targets) {
const targetKey = target.id ?? target.webSocketDebuggerUrl;
if (injectedTargets.has(targetKey)) continue;
await injectTarget(target);
injectedTargets.add(targetKey);
lastInjectedAt = Date.now();
console.log(`Injected Codex local feature overrides into ${target.title || target.url || "Codex"}.`);
if (openAvatarOverlay && !openedAvatarOverlay && isMainCodexTarget(target)) {
openedAvatarOverlay = await requestAvatarOverlay(target).catch(() => false);
}
}
const hasInjectedAvatarOverlay = [...injectedTargets].length > 0
&& targets.some((target) => isAvatarOverlayTarget(target) && injectedTargets.has(target.id ?? target.webSocketDebuggerUrl));
if (injectedTargets.size > 0 && !openAvatarOverlay) return;
if (openAvatarOverlay && openedAvatarOverlay && hasInjectedAvatarOverlay) return;
if (injectedTargets.size > 0 && Date.now() - lastInjectedAt > 3000) return;
await delay(250);
}
if (injectedTargets.size === 0) {
throw new Error(`Timed out waiting for Codex remote debugging on port ${port}: ${lastError?.message ?? "no target"}`);
}
}
async function listTargets() {
const response = await fetch(endpoint);
const targets = await response.json();
return targets.filter(isInjectableCodexTarget);
}
function isInjectableCodexTarget(target) {
if (!target.webSocketDebuggerUrl) return false;
if (target.type !== "page" && target.type !== "webview") return false;
const url = `${target.url ?? ""} ${target.title ?? ""}`;
return /codex|localhost|file:|app:/i.test(url);
}
function isMainCodexTarget(target) {
return target.url === "app://-/index.html";
}
function isAvatarOverlayTarget(target) {
return target.url?.includes("initialRoute=%2Favatar-overlay") === true;
}
async function injectTarget(target) {
await sendDevtoolsCommand(target.webSocketDebuggerUrl, "Page.addScriptToEvaluateOnNewDocument", {
source: overrideSource,
});
await sendRuntimeEvaluate(target.webSocketDebuggerUrl, overrideSource);
}
async function requestAvatarOverlay(target) {
return sendRuntimeEvaluate(target.webSocketDebuggerUrl, `(() => {
if (!window.electronBridge?.sendMessageFromView) return false;
window.electronBridge.sendMessageFromView({ type: "avatar-overlay-open", payload: {} });
return true;
})()`);
}
async function sendRuntimeEvaluate(webSocketUrl, expression) {
return sendDevtoolsCommand(webSocketUrl, "Runtime.evaluate", {
expression,
awaitPromise: false,
includeCommandLineAPI: false,
returnByValue: true,
}).then((result) => result?.result?.value);
}
function sendDevtoolsCommand(webSocketUrl, method, params) {
return new Promise((resolve, reject) => {
const socket = new WebSocket(webSocketUrl);
const timeout = setTimeout(() => {
socket.close();
reject(new Error(`Timed out while running ${method}`));
}, 15000);
socket.addEventListener("open", () => {
socket.send(JSON.stringify({ id: 1, method, params }));
});
socket.addEventListener("message", (event) => {
const message = JSON.parse(event.data);
if (message.id !== 1) return;
clearTimeout(timeout);
socket.close();
if (message.error) {
reject(new Error(message.error.message ?? JSON.stringify(message.error)));
} else if (message.result?.exceptionDetails) {
reject(new Error(message.result.exceptionDetails.text ?? `Runtime exception during ${method}`));
} else {
resolve(message.result);
}
});
socket.addEventListener("error", () => {
clearTimeout(timeout);
reject(new Error("WebSocket error while injecting override"));
});
});
}
function delay(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
main().catch((error) => {
console.error(error.message);
process.exit(1);
});
#!/usr/bin/env bash
set -euo pipefail
APP_PATH="${CODEX_APP_PATH:-/Applications/Codex.app}"
PORT="${CODEX_DEBUG_PORT:-9322}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
INJECTOR="$SCRIPT_DIR/inject-codex-feature-overrides.mjs"
if [[ ! -d "$APP_PATH" ]]; then
echo "Codex app not found at: $APP_PATH" >&2
exit 1
fi
if [[ ! -f "$INJECTOR" ]]; then
echo "Injector not found at: $INJECTOR" >&2
exit 1
fi
echo "Launching Codex with local debugging port $PORT..."
open -na "$APP_PATH" --args "--remote-debugging-port=$PORT"
echo "Injecting local Codex feature overrides..."
CODEX_OPEN_AVATAR_OVERLAY="${CODEX_OPEN_AVATAR_OVERLAY:-1}" node "$INJECTOR" "$PORT" 30000
cat <<EOF
Done. Local Codex renderer feature gates are overridden for this launched process.
Notes:
- This does not modify the Codex app bundle.
- Quit Codex and launch normally to remove renderer overrides.
- Some features also require app-server or backend rollout flags.
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment