Skip to content

Codex on Windows: project dir falls back to the plugin cache 5 minutes after the newest Codex session starts (rollout mtime never updates) #1210

Description

@thelogicmatrix

Environment

  • context-mode 1.0.169 (Codex plugin)
  • codex-cli 0.157.1
  • Windows 11

What happens

Once the most recently started Codex session on the machine is more than 5 minutes old, ctx_execute_file refuses files inside the workspace with File access blocked: "..." resolves outside the project root. The root it reports is the plugin install folder, C:\Users\<user>\.codex\plugins\cache\context-mode\context-mode\1.0.169. From the code path below, the same call should succeed inside that 5-minute window. I have not tested that directly.

Cause

Codex sets no workspace env var, so getProjectDir() (src/server.ts) uses resolveCodexSessionCwd (src/util/project-dir.ts) with transcriptMaxAgeMs: 5 * 60 * 1000. That function takes the rollout with the newest mtime under ~/.codex/sessions/ and returns null if that mtime is older than 5 minutes.

On Windows, Codex does not seem to update a rollout's mtime while it appends. On my machine, 37 of the 40 newest rollouts have an mtime equal to their first entry's timestamp. The other 3 match their last entry, which suggests the mtime updates only when the file is closed. One live rollout had an mtime about 20 minutes older than its last entry. So 5 minutes after a session starts, the resolver returns null and falls through to PWD, then to process.cwd().

start.mjs changes directory into its own folder before loading the server, so process.cwd() is the plugin root. .codex-plugin/mcp.json also spawns the server with "cwd": ".", so the original cwd is the plugin path too, and start.mjs correctly refuses to set CONTEXT_MODE_PROJECT_DIR from it. Nothing else supplies the workspace.

Why dropping the age guard is not enough

Discovery uses the newest rollout across all sessions. Without the guard, a Codex session started later in another directory (a git worktree, say) would become the allowed root for every other running Codex session.

Possible fixes

  1. MCP roots: after initialize, if the client advertises the roots capability, call roots/list and use the result. That is per session and defined by the protocol. I have not verified whether Codex answers roots/list.
  2. Resolve once per server: Codex spawns one server per session. On my machine, each session's rollout was created within a second of its server process starting, sometimes just after it (0.6 s later in one case). So a lookup at boot can miss it. Instead, resolve on the first tool call (or retry briefly after start), using the rollout created within a few seconds of the process start time, and cache the result for the life of the process. If more than one rollout falls in that window, return null rather than guess. This also removes the per-call "newest rollout anywhere" lookup.

Acceptance check

Start Codex session A in directory X. After more than 6 minutes, start session B in directory Y.

  • In A, ctx_execute_file on a file in X succeeds, and on a file in Y it is refused.
  • In B, a file in Y succeeds, and a file in X is refused.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions