fix(codex): drive BrowserOS tools instead of Codex's bundled in-app browser#1973
Merged
Merged
Conversation
Materialize a CODEX_HOME overlay that disables Codex's bundled in-app browser plugin (browser@openai-bundled). Symlinks every real ~/.codex entry except config.toml, which is regenerated with the plugin disabled. Self-healing: the symlink farm re-syncs to ~/.codex each call, and the config edit is committed only when a real TOML round-trip proves it changed exactly that one field. resolveAcpSpawnCommand gains extraEnv to carry CODEX_HOME into both the bundled-bun and npx-fallback commands.
On the acpx chat path, point Codex at the browserless CODEX_HOME overlay so its in-app browser plugin is absent, and reinforce the ACP prompt to route every browser action through mcp.browseros.* and never fall back to an in-app browser, Playwright, chrome-devtools, or the system Chrome.
Contributor
Greptile SummaryThis PR routes Codex browser work exclusively through BrowserOS. The main changes are:
Confidence Score: 4/5The overlay failure paths need fixes before merging because they can launch Codex with missing user state.
packages/browseros-agent/apps/server/src/lib/agents/host-acp/codex-home.ts Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Create ACP language model] --> B[Materialize browserless CODEX_HOME]
B --> C[Mirror the real Codex home]
C --> D[Disable and verify browser plugin config]
D -->|Success| E[Set CODEX_HOME on Codex launcher]
B -->|Failure| F[Leave CODEX_HOME unset]
F --> G[Codex uses the real home]
E --> H[Codex uses BrowserOS MCP tools]
Prompt To Fix All With AIFix the following 2 code review issues. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 2
packages/browseros-agent/apps/server/src/lib/agents/host-acp/codex-home.ts:191-193
**Unreadable Config Becomes Empty**
When `config.toml` exists but cannot be read because of permissions or an I/O error, this catch treats it as an empty file. Verification then succeeds and Codex starts with a minimal overlay that drops the user's model, provider, authentication, and other settings instead of falling back to the real home.
```suggestion
let sourceText: string
try {
sourceText = await readFile(join(source, CONFIG_FILE), 'utf8')
} catch (error) {
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error
sourceText = ''
}
```
### Issue 2 of 2
packages/browseros-agent/apps/server/src/lib/agents/host-acp/codex-home.ts:178-183
**Failed Links Still Mark Success**
When symlink creation is denied or races with another session using the shared overlay, this catch suppresses the failure and synchronization continues. The function can then return the incomplete directory as `CODEX_HOME`, leaving required files such as authentication state unavailable to Codex.
```suggestion
} catch (error) {
logger.warn('Failed to create a Codex home overlay symlink', {
linkPath,
error: error instanceof Error ? error.message : String(error),
})
throw error
}
```
Reviews (1): Last reviewed commit: "fix(codex): drive BrowserOS tools, not t..." | Re-trigger Greptile |
Contributor
✅ Tests passed — 1629/1633
|
Two edge cases in the overlay builder: an unreadable config.toml (permissions/IO, not ENOENT) was treated as empty, producing a minimal config that dropped the user's Codex settings; and a failed symlink was swallowed, so a missing auth link could still be returned as CODEX_HOME. Now a non-ENOENT config read rethrows to the fallback path, and ensureSymlink reports success/failure (a concurrent EEXIST pointing at the right target still counts as success) so the materializer returns null and falls back to the real home when the overlay is incomplete.
Gate the browserless-home materialization on codex being the agent actually spawned, so claude chats do not do the extra fs work (and the workspace-mkdir assertion stays at one call). Import the overlay module lazily inside codexBrowserlessEnv so its fs-heavy imports stay out of the provider factory's static graph, which keeps the partial node:fs/promises mock in the ACP factory tests intact.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
When BrowserOS drives Codex through the ACP chat path, Codex sometimes reaches for its own bundled in-app browser plugin (
browser@openai-bundled, exposed as thecontrol-in-app-browserskill and anode_replbridge) instead of the BrowserOS MCP browser tools. It only falls back tomcp.browseros.*after that bridge fails. The behavior is intermittent: the plugin's own manifest instructs the model to use the in-app browser for "open, navigate, click, type, screenshot ... the current in-app browser tab", which competes directly with the BrowserOS tools.Root cause: the chat path spawns Codex against the real
~/.codex, where that plugin is enabled, and the ACP prompt only steered Codex away from its "file, shell, or fetch tools", never its in-app browser.Fix
Two layers so BrowserOS is the only browser Codex can reach:
CODEX_HOMEoverlay that disables the in-app browser plugin. The overlay symlinks every real~/.codexentry exceptconfig.toml, which is regenerated with[plugins."browser@openai-bundled"] enabled = false. Verified withcodex debug prompt-input: the in-app browser skill and node_repl bridge disappear from the model-visible prompt while every other plugin stays intact. The user's real~/.codexis never modified.mcp.browseros.*and never fall back to an in-app browser, Playwright, chrome-devtools, or the system Chrome.Resilience
The overlay is built to survive future changes to
~/.codex:~/.codexare picked up automatically and removed ones are pruned, so there is no hard-coded file list.config.tomledit is validated by a real TOML round-trip (Bun.TOML.parse). It is written only when the parse proves the edit changed exactly the one plugin field and nothing else. If the config format ever changes in a way the edit cannot handle, the overlay is not written, an error is logged, and Codex falls back to the real home rather than crashing or writing a corrupt config.Validation
extraEnvcoverage.bun test tests/lib/agents/host-acpgreen.apps/servertypecheck clean.~/.codexand confirmed viacodex debug prompt-inputthat the in-app browser is gone and other plugins remain.