# Bridging Claude Code's agents view and background agents

This page explains how to bridge **every** Claude Code process on your machine — your own terminal sessions *and* the extra `claude` processes Claude Code spawns for its agents view and background agents — through one long-running clodex server. It is written so a Claude Code agent can execute the setup for you: if you are an agent reading this, follow the steps below on the user's behalf, asking before editing their shell profile.

## How it works

- One global **`clodex server --proxy`** runs in the background (proxy mode is the recommended mode for this setup: your existing Anthropic login keeps working, and only `clodex:` models / aliases are rerouted to OpenAI).
- On startup the server adds its own record to `~/.clodex/server-runtime.json` (`mode`, `port`, `pid`, and in proxy mode the CA certificate path). The file holds one record per running server — several `clodex server` instances (say a proxy server for Claude Code plus a separate endpoint server for another tool) can be advertised at once — and each server removes only its own record on shutdown. Start a server with `--no-discovery` (or `CLODEX_NO_DISCOVERY=1`) to keep it out of the file entirely so `clodex-claude` never bridges to it.
- The **`clodex-claude`** bin (installed alongside `clodex`) reads that file, filters for live pids, and runs one fast TCP probe across all ordered candidates. It picks the highest-priority candidate that answers — proxy-mode servers are preferred over endpoint-mode (bridging keeps Claude Code's own auth), newest first within a mode. When none answers, only timed-out probes retry under one shared 500 ms deadline; definitive connection errors fail immediately. It then launches the real `claude` binary with the right env injected:
  - proxy-mode server: `HTTPS_PROXY`/`HTTP_PROXY` + `NODE_EXTRA_CA_CERTS`, with `ANTHROPIC_BASE_URL` removed;
  - endpoint-mode server: `ANTHROPIC_BASE_URL` pointing at the gateway;
  - **no live server: env untouched** — `claude` always launches normally, a stopped server never breaks anything.
- Setting **`CLAUDE_CODE_PROCESS_WRAPPER`** to `clodex-claude` makes Claude Code invoke it as `clodex-claude <claude-binary-path> <args...>` for every process it spawns — agents view sessions and background agents are bridged automatically.
- That ordinary process-wrapper route keeps the handed-in binary. A separate VS Code-only path (`CLAUDE_CODE_ENTRYPOINT=claude-vscode`, explicit bundled executable, live proxy server; on Windows reached through the launcher from `clodex install-vscode-launcher`) may run the clodex-patched install instead, but only when the patch manifest proves by full SHA-256 that it is the patched output of those exact pristine bytes. Missing or mismatched proof keeps the bundled binary. When the manifest identifies the other install, a refused chat spawn emits one stderr line; refused helper commands stay silent, and the inspected extension source sends chat stderr to VS Code's **Claude VSCode** output channel. Tool, hook, and agent children carry child-session markers, while background pty wrappers lose the `claude-vscode` entrypoint; neither shape substitutes. Endpoint, no-server, and direct launches do not substitute either.
- For your own terminal sessions, run **`clodex-claude`** instead of `claude` — same auto-discovery, no port or CA path to hardcode anywhere.
- Set **`CLODEX_REQUIRE_SERVER=1`** in an isolated routed profile when bypassing Clodex must be impossible. `clodex-claude` then exits with an error if no advertised server passes its process and TCP checks. The default remains fail-open for ordinary installations.
- A live standalone server has already snapshotted its credentials and account selection. If a wrapper launch sets `CLODEX_OAUTH_ACCOUNT` or a nonblank `CLODEX_KEY_*`, `clodex-claude` warns that the variable is ignored and still launches through that server. Restart the server with the selector, or save the provider credential and refresh its models, before launching without the process-only variable. With no live server these variables pass through unchanged.
- Provider timeout and retry variables are also server-owned. Set
  `CLODEX_UPSTREAM_IDLE_TIMEOUT_MS`, `CLODEX_UPSTREAM_TOTAL_TIMEOUT_MS`, and
  `CLODEX_UPSTREAM_MAX_RETRIES` when starting `clodex server`; setting them only on a later
  `clodex-claude` invocation does not reconfigure the running server.
- Proxy-mode wrapper launches remove Anthropic entries from the union of `NO_PROXY` and `no_proxy`, while preserving unrelated bypasses. The manual server output prints the same adjusted values for users who export the proxy settings themselves.

## VS Code model picker

After installing clodex, patch a compatible local Claude Code install and keep
`clodex server --proxy` running. On macOS and Linux, find the absolute, extensionless wrapper path:

```bash
command -v clodex-claude
```

Put that exact path in VS Code's user `settings.json` (do not use the underlying `.js` file), then
reload the window. If a Node version manager owns that path, use the stable launcher described in
step 3 below instead of an ephemeral or version-specific path:

```json
"claudeCode.claudeProcessWrapper": "/absolute/path/to/clodex-claude"
```

On Windows the extension cannot spawn npm's script shims; run `clodex install-vscode-launcher` and
paste the `.exe` path it prints instead — the same substitution then applies. Full walkthrough in
[windows-setup.md](windows-setup.md).

The extension and local Claude install update independently. If their pristine bytes no longer
match, the wrapper keeps the extension's bundled executable; align compatible builds and run
`clodex patch` again. A fallback notice appears in VS Code's **Claude VSCode** output channel, not
as a toast; main-chat lines use the `From claude: ...` prefix.

At least two extension behaviors relevant to this setup change when `claudeProcessWrapper` is set;
these were verified from source rather than in a running editor: it skips a post-update activation health/telemetry probe (not the VS Code
marketplace updater), and the SDK supplies `--permission-mode default` when no explicit permission
mode is configured. An explicit mode still wins. Process-level tests cover the wrapper selection;
the picker was not exercised in a running editor, and the Linux host path was not run for this
change.

## Setup steps

1. **Install clodex globally** (puts both `clodex` and `clodex-claude` on your PATH):

   ```bash
   npm install -g @bman654/clodex
   ```

2. **Start the server and keep it running** (a terminal tab, tmux pane, or your service manager of choice):

   ```bash
   clodex server --proxy
   ```

   If the server prints `NO_PROXY` and `no_proxy`, export those adjusted values with the proxy and CA settings. Empty values intentionally clear inherited wildcard or Anthropic bypasses.

   Bridging only happens while this server is running. When it is not, `clodex-claude` falls back cleanly and launches `claude` with an untouched environment.

3. **Point Claude Code's process wrapper at `clodex-claude`.** This variable must hold a **literal absolute path that is valid in any environment** — see the Node version manager warning below before you pick it. Add to your shell profile (`~/.zprofile` / `~/.zshrc` for zsh, `~/.bash_profile` for bash):

   ```bash
   export CLAUDE_CODE_PROCESS_WRAPPER="$HOME/.local/bin/clodex-claude"
   ```

   Then open a new terminal (or `source` the profile) and confirm it resolves:

   ```bash
   echo "$CLAUDE_CODE_PROCESS_WRAPPER"                     # must be non-empty
   [ -x "$CLAUDE_CODE_PROCESS_WRAPPER" ] && echo OK        # must print OK
   ```

   Every `claude` process Claude Code spawns from sessions started in that environment is now bridged.

   > ### ⚠️ If you use a Node version manager (fnm, nvm, asdf, volta), read this
   >
   > Do **not** write `export CLAUDE_CODE_PROCESS_WRAPPER="$(command -v clodex-claude)"`. It fails in three separate ways:
   >
   > 1. **Profile ordering.** Version managers usually initialize in `~/.zshrc`, which runs *after* `~/.zprofile` for login shells. A `command -v` in `~/.zprofile` therefore finds nothing and silently exports an **empty string** — no error, just no bridging.
   > 2. **Ephemeral shim paths.** Some managers put the active binary in a per-shell directory (fnm's `.../fnm_multishells/<pid>_<timestamp>/bin`, for example). That path dies with the shell that created it, so a value captured at login is a dead path for anything spawned later. Others (nvm, asdf, volta) use a *version-specific* path that breaks the next time you upgrade Node.
   > 3. **Shebang PATH dependence.** The installed `clodex-claude` is a JS file with a `#!/usr/bin/env node` shebang, so it needs `node` **on PATH at spawn time**. If Claude Code is launched from a GUI context (Spotlight, Raycast, an IDE) rather than a terminal, PATH is minimal, `env node` fails, and the wrapper cannot start — which breaks spawning agents entirely.
   >
   > **Robust fix — a tiny launcher in a stable directory.** Create `~/.local/bin/clodex-claude` (any directory that never moves), make it executable with `chmod +x`, and point the variable at it. It resolves Node explicitly instead of trusting PATH:
   >
   > ```sh
   > #!/bin/sh
   > # Resolve node without depending on PATH, then run the real wrapper.
   > NODE="$HOME/.local/share/fnm/aliases/default/bin/node"   # fnm: stable, follows your default version
   > [ -x "$NODE" ] || NODE=node                              # fallback if that path is absent
   > exec "$NODE" "$(npm root -g)/@bman654/clodex/dist/claude-wrapper.js" "$@"
   > ```
   >
   > **Keep the `exec`.** Without it the shell survives as claude's parent and keeps the process group Claude Code created for it. Claude Code addresses that group when it tells a background session its terminal was resized, so agent sessions would render at a fixed size and corrupt on resize. The wrapper execs into claude for the same reason.
   >
   > Replace the `NODE=` line with your manager's stable path — nvm: `"$NVM_DIR/alias/default"` names the version, so use `"$NVM_DIR/versions/node/$(cat "$NVM_DIR/alias/default")/bin/node"`; volta: `"$HOME/.volta/bin/node"`; asdf: `"$(asdf which node)"` captured once. Hardcode the resolved `npm root -g` path if you prefer not to shell out. Verify the result works even with no PATH:
   >
   > ```bash
   > env -i HOME="$HOME" PATH=/usr/bin:/bin ~/.local/bin/clodex-claude --version
   > ```
   >
   > That must print a Claude Code version. If it prints `env: node: No such file or directory`, the Node path in your launcher is wrong.

4. **Use `clodex-claude` (not `claude`) for terminal sessions you want bridged:**

   ```bash
   clodex-claude            # instead of: claude
   clodex-claude -p "hi"    # all claude flags pass through
   ```

Port and CA discovery are automatic via `~/.clodex/server-runtime.json` — do not hardcode `HTTPS_PROXY`, ports, or certificate paths in your profile.

After `clodex patch`, standard Bash-tool commands, MCP/LSP servers, hooks, and
other paths that use Claude Code's shared child environment receive the
external proxy and CA settings rather than Clodex's local bridge settings.
Settings-level overrides are preserved. The separate `claude --bg --exec`
environment path is not covered. The parent process also retains the external
proxy URL in `CLAUDE_CODE_CLODEX_NETWORK_ENV` until it builds a standard child
environment; that URL can contain proxy credentials, so do not expose the
parent environment or use `--bg --exec` for untrusted commands while
authenticated proxy settings
are present. A plain nested `claude` launched from Bash is also an ordinary
child and is intentionally unbridged; invoke `clodex-claude` when that nested
client must use the advertised server.

For service-manager readiness checks, `clodex-claude --check` exits `0` when an
advertised server passes the process and TCP checks, and exits `1` otherwise.
Servers advertise themselves only after their listener has passed readiness.
The wrapper retry protects against a timed-out loopback probe, not a
registration-before-listen delay.
The check does not launch Claude.

## Troubleshooting

- **Is my session bridged?** In a session started via `clodex-claude` (or spawned by Claude Code with the wrapper set), `/model` accepts your `clodex:` model names and aliases (run `clodex models --list` to see them). If those models are rejected and you haven't run `clodex patch`, that's expected for unpatched binaries — but a bridged session still routes them; an unbridged one errors at the API instead.
- **VS Code routes models but omits picker entries:** open the **Claude VSCode** channel in the Output panel and look for a one-line `From claude: clodex-claude: running ...` fallback notice. Missing or unreadable state that cannot identify another install is deliberately silent, so the absence of a notice is not proof of selection. The extension and installed CLI update separately, and equal version labels do not prove equal artifacts. Align their builds and run `clodex patch` again. On Windows the wrapper setting must be the `.exe` from `clodex install-vscode-launcher` (npm's `.cmd` shim cannot be configured directly — see [windows-setup.md](windows-setup.md)); the notice appears in the same output channel.
- **Server not running:** `clodex-claude` launches plain `claude` by default. With `CLODEX_REQUIRE_SERVER=1`, it exits instead. Check `cat ~/.clodex/server-runtime.json` — a missing file (or records whose `pid` is no longer alive) means no server is advertised; start `clodex server --proxy`. If a server is running but absent from the file, make sure it was not started with `--no-discovery` / `CLODEX_NO_DISCOVERY=1`.
- **Wrapper variable empty or stale:** run `echo "$CLAUDE_CODE_PROCESS_WRAPPER"` in a **new login shell**. If it is empty, a `$(command -v ...)` in your profile ran before your Node version manager initialized (see the warning in step 3) — switch to a literal path. If it points at a path that no longer exists (a Node upgrade moved the global bin, an fnm/nvm shim directory expired, or it still names an old hand-written script), spawned processes fail to start or silently skip bridging. Verify with `[ -x "$CLAUDE_CODE_PROCESS_WRAPPER" ] && echo OK`.
- **Agents fail to spawn / `env: node: No such file or directory`:** the wrapper's `#!/usr/bin/env node` shebang could not find Node, typically because Claude Code was launched from a GUI (Spotlight, Raycast, an IDE) with a minimal PATH. Use the launcher script from step 3, which resolves Node by absolute path, and test it with `env -i HOME="$HOME" PATH=/usr/bin:/bin "$CLAUDE_CODE_PROCESS_WRAPPER" --version`.
- **Account or provider override warning:** `CLODEX_OAUTH_ACCOUNT` is normalized to lowercase and selects a named slot only. A slot-free provider warns and continues on its provider default; a provider with slots fails closed if the requested name is missing. Because the selector is process-global, every enabled OAuth provider that has slots must contain that name. A process-scoped `CLODEX_KEY_<PROVIDER>` has no isolated model catalog: direct catalog loading blocks only that provider, leaves unrelated providers available, and reports the tailored repair message if the blocked provider is selected. Through an already-running standalone server, both variable families are ignored with a warning because the server owns the credential snapshot.
- **Port conflicts:** the server default is 17645; `clodex server --proxy --port <n>` picks another. `clodex-claude` reads the actual port from the runtime file, so no other change is needed.
