codex app-server hardcodes SessionSource::VSCode; no --session-source flag on the umbrella subcommand

Open 💬 4 comments Opened May 19, 2026 by Bohan-J
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

Summary

The codex app-server subcommand (in the umbrella codex binary) hardcodes the session source to SessionSource::VSCode. There is no CLI flag to override this, even though clientInfo.name is honored for the originator field. As a result, any third‑party host that embeds Codex via codex app-server --listen stdio:// produces sessions that:

  • carry a faithful originator (e.g. multica-agent-sdk), but
  • are also tagged source=vscode, which the default threadList allowlist treats as a normal interactive VSCode session.

These third‑party sessions then show up in Codex Desktop's recent/project history and, at high session rates, push real user chats out of the visible window.

The standalone codex-app-server binary already exposes --session-source (default vscode) and parses arbitrary values into SessionSource::Custom(...). The fix is to forward the same flag from the umbrella codex app-server subcommand.

Repro

  1. Start Codex via the umbrella binary the way embedders do today:

``
codex app-server --listen stdio://
`
Pass
clientInfo = { name: "my-agent-sdk", version: "..." }` over the JSON-RPC handshake.

  1. Inspect the resulting ~/.codex/sessions/.../rollout-*.jsonl session_meta line:

``json
{ "originator": "my-agent-sdk", "source": "vscode", ... }
``

  1. Open Codex Desktop. The third‑party session appears in the recent threads/project list, even though it was never a VSCode‑extension chat.

Root cause (confirmed against current main)

  • The umbrella codex app-server subcommand calls run_main_with_transport_options(..., SessionSource::VSCode, ...) directly — the value is hardcoded, not read from a flag:
  • codex-rs/cli/src/main.rs (AppServerCommand invocation, ~L986)
  • AppServerCommand does not declare a --session-source field:
  • codex-rs/cli/src/main.rs (AppServerCommand struct, ~L435–477) — fields are subcommand, strict_config, listen, remote_control, analytics_default_enabled, auth. No session source.
  • The standalone codex-app-server binary already has the flag and parser:
  • codex-rs/app-server/src/main.rs (~L31–37): clap long = "session-source", default = "vscode", parsed via SessionSource::from_startup_arg.
  • SessionSource::from_startup_arg accepts any string and folds unknown values into SessionSource::Custom(normalized):
  • codex-rs/protocol/src/protocol.rs (from_startup_arg).
  • arg0‑style dispatch in codex does not alias to app-server, so calling codex under a different basename is not a workaround:
  • codex-rs/arg0/src/lib.rs.

Why hardcoding to vscode is load-bearing for the bug

The default threadList allowlist in rollout includes SessionSource::VSCode, which is why these sessions surface in Desktop in the first place:

  • Default allowlist: INTERACTIVE_SESSION_SOURCES = [Cli, VSCode, Custom("atlas"), Custom("chatgpt")]
  • codex-rs/rollout/src/lib.rs (~L24–31)
  • Default filter when client doesn't pass source_kinds:
  • codex-rs/app-server/src/filters.rs (~L9–15)
  • Sessions whose source is not in the allowlist are dropped from threadList results:
  • codex-rs/rollout/src/list.rs (~L748–754)

So if an embedder could pass --session-source my-agent-sdk, the resulting SessionSource::Custom("my-agent-sdk") would naturally fall outside the default allowlist and Desktop would correctly hide it — same convention the codebase already uses for non‑interactive sources.

Proposed fix

Mirror the standalone binary's flag on the umbrella subcommand:

  1. Add --session-source to AppServerCommand (codex-rs/cli/src/main.rs), default = "vscode" so existing VSCode‑extension behavior is unchanged.
  2. Parse it with SessionSource::from_startup_arg (same as codex-rs/app-server/src/main.rs).
  3. Pass the parsed SessionSource into run_main_with_transport_options(...) instead of the hardcoded SessionSource::VSCode.

This is additive, opt-in, and matches the existing convention used by the standalone codex-app-server binary. No allowlist or Desktop‑side change is required: Custom(...) sources are already filtered out of the default interactive list.

Workarounds today

  • Build/install the standalone codex-app-server binary and pass --session-source <name> directly. Not viable for end users who only have the official @openai/codex distribution (which ships only codex), and Desktop's bundled codex-app-server isn't on PATH.

Context

  • Reported downstream as a noisy Codex Desktop history flood when Multica agents run concurrently with the Desktop app: https://github.com/multica-ai/multica/issues/2746
  • Closely related symptoms from the same root: https://github.com/openai/codex/issues/23408 (mobile Projects list scraping Multica workdirs)
  • The diagnosis above was first sketched by @truffle-dev in the Multica issue thread; this report confirms it against the current upstream tree and proposes the minimal fix.

View original on GitHub ↗

4 Comments

github-actions[bot] contributor · 3 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #23437

Powered by Codex Action

truffle-dev · 3 months ago

Not a duplicate of #23437. Same surface symptom (source=vscode), different root causes.

This issue is the app-server subcommand at codex-rs/cli/src/main.rs:986 constructing the session with codex_protocol::protocol::SessionSource::VSCode hardcoded. The app-server's own session is misclassified at construction time. Fix is plumbing a source through AppServerCommand (a --session-source flag, or detecting CLI invocation vs Desktop launch).

#23437 is a separately-launched TUI session in a project directory getting written with source=vscode, originator=Codex Desktop, and cwd=$HOME after codex app-server has run. The TUI session never enters the :986 codepath; its metadata is being polluted through shared state outside that file (env var, session-store, lockfile, or originator detection reading state set by the app-server). Patching :986 leaves that codepath untouched.

Worth keeping both open. The fixes are independent.

truffle-dev · 3 months ago

Sharing the fix outline per docs/contributing.md. Happy to PR it if a Codex maintainer wants it.

The umbrella app-server subcommand already mirrors the standalone codex-app-server binary in every other respect (--listen, --remote-control, --analytics-default-enabled, strict config). Adding the same --session-source flag with the same defaults closes the gap. The change is 42 lines in codex-rs/cli/src/main.rs (1 import, 1 struct field, 1 destructure, 1 hardcoded-replacement, 3 unit tests), compiles clean on top of b3ae3de40, cargo test -p codex-cli passes locally.

// codex-rs/cli/src/main.rs

use codex_protocol::protocol::SessionSource;        // <-- add

// AppServerCommand: place after the existing `analytics_default_enabled` flag
/// Session source used to derive product restrictions and metadata.
///
/// Mirrors the `--session-source` flag on the standalone `codex-app-server`
/// binary so third-party embedders launching `codex app-server --listen ...`
/// can classify their sessions (e.g. `multica-agent-sdk`) instead of being
/// hardcoded as `vscode`. Defaults to `vscode` to preserve the first-party
/// VSCode IDE extension flow.
#[arg(
    long = "session-source",
    value_name = "SOURCE",
    default_value = "vscode",
    value_parser = SessionSource::from_startup_arg
)]
session_source: SessionSource,

// in cli_main, the AppServerCommand destructure:
//   analytics_default_enabled,
//   session_source,                                // <-- add
//   auth,

// at the run_main_with_transport_options call (line ~986):
//   transport,
// - codex_protocol::protocol::SessionSource::VSCode,
// + session_source,
//   auth,

The default stays vscode so first-party VSCode launches keep their current classification with no change. SessionSource::from_startup_arg is the same parser the standalone binary uses (codex-rs/protocol/src/protocol.rs:2613), which already accepts cli/vscode/exec/mcp/app-server aliases and wraps anything else as SessionSource::Custom(_), so downstream embedders get their identifier through without needing a new enum variant.

Three unit tests in the existing #[cfg(test)] mod tests block cover default, custom, and alias normalization:

#[test]
fn app_server_session_source_defaults_to_vscode() {
    let app_server = app_server_from_args(["codex", "app-server"].as_ref());
    assert_eq!(app_server.session_source, SessionSource::VSCode);
}

#[test]
fn app_server_session_source_accepts_custom_value() {
    let app_server = app_server_from_args(
        ["codex", "app-server", "--session-source", "multica-agent-sdk"].as_ref(),
    );
    assert_eq!(
        app_server.session_source,
        SessionSource::Custom("multica-agent-sdk".to_string())
    );
}

#[test]
fn app_server_session_source_normalizes_known_values() {
    let app_server =
        app_server_from_args(["codex", "app-server", "--session-source", "cli"].as_ref());
    assert_eq!(app_server.session_source, SessionSource::Cli);
}

This is upstream-only and leaves the separate TUI-classification-via-shared-state question on #23437 untouched.

innoiso · 2 months ago

Still reproducible in current Codex builds.

I verified rust-v0.141.0 still hardcodes SessionSource::VSCode in the umbrella codex app-server path:

  • codex-rs/cli/src/main.rs:1146
  • codex-rs/app-server/src/lib.rs:398

The standalone codex-app-server binary has --session-source, but the distributed codex app-server command does not expose it, so custom clients using codex app-server --listen stdio:// still create threads with source: "vscode".