Summary
#377 made cwd comparison case-insensitive for Windows paths. The normalisation only recognises native Windows forms, so /mnt/c/... paths — what a Windows client passes when the agent runs inside WSL — still compare case-sensitively. Because Codex stores those paths lowercased in its thread index, session/list cannot find a session created moments
earlier with the very same cwd string.
Environment
@agentclientprotocol/codex-acp 1.6.2 (vendored @openai/codex 0.148.0, x86_64-unknown-linux-musl)
- WSL2, Ubuntu 22.04, Node 22.23.2
- cwd on a DrvFs mount, e.g.
/mnt/c/Users/Me/Notes/MyVault
Reproduction
Over stdin, keeping stdin open for each reply:
initialize
session/new → {"cwd": "/mnt/c/Users/Me: []} — succeeds
session/list → {"cwd": "/mnt/c/Users/Me/Notes/MyVault", "cursor": null} — 0 sessions
session/list → {"cwd": "/mnt/c/users/me/notes/myvault", "cursor": null} — returns the session from step 2
Only the casing differs between steps 3 and
Cause
listSessions filters with arePathsEqual(thread.cwd, requestedCwd). thread.cwd comes from the Codex thread index, which stores /mnt/ paths lowercased. The same session ends up recorded twice with different casing:
| Location |
cwd |
~/.codex/sessions/.../rollout-*.jsonl, payload.cwd |
/mnt/c/Users/Me/Notes/MyVault |
~/.codex/state_5.sqlite, threads.cwd |
lt` |
normalizePathForComparison lowercases only when isWindowsAbsolutePath is true:
function isWindowsAbsolutePath(value) {
const portableValue = value.replace(/\\/g, "/");
return /^[A-Za-z]:\//.test(portableValue) || /^\/\/[^/]+\/[^/]+/.test(portableValue);
}
/mnt/c/Users/... matches neither, so it takes the POSIX branch and keeps its capitals, while the stored value has
none.
Paths under /home are unaffected, being lowercase already. WSL is not the cause: getcwd, realpath and
os.path.realpath all preserve the capitals
Suggested fix
Treat /mnt/<drive>/... as case-insensitive too — either by extending shouldComparePathCaseInsensitive with a ^/mnt/[a-z]/ test, or by lowercasing in normalizePathForComparison whenever the path resolves onto a DrvFs mount.
Downstream impact
The Obsidian Agent Client plugin passes the for its "Show current vault only" filter.Under WSL mode that filter is always empty for any vault on a Windows drive.
Note
@agentclientprotocol/claude-agent-acp 0.70.0 shows the same symptom, so the fix may be worth mirroring there.
Summary
#377 made
cwdcomparison case-insensitive for Windows paths. The normalisation only recognises native Windows forms, so/mnt/c/...paths — what a Windows client passes when the agent runs inside WSL — still compare case-sensitively. Because Codex stores those paths lowercased in its thread index,session/listcannot find a session created momentsearlier with the very same
cwdstring.Environment
@agentclientprotocol/codex-acp1.6.2 (vendored@openai/codex0.148.0,x86_64-unknown-linux-musl)/mnt/c/Users/Me/Notes/MyVaultReproduction
Over stdin, keeping stdin open for each reply:
initializesession/new→{"cwd": "/mnt/c/Users/Me: []}— succeedssession/list→{"cwd": "/mnt/c/Users/Me/Notes/MyVault", "cursor": null}— 0 sessionssession/list→{"cwd": "/mnt/c/users/me/notes/myvault", "cursor": null}— returns the session from step 2Only the casing differs between steps 3 and
Cause
listSessionsfilters witharePathsEqual(thread.cwd, requestedCwd).thread.cwdcomes from the Codex thread index, which stores/mnt/paths lowercased. The same session ends up recorded twice with different casing:cwd~/.codex/sessions/.../rollout-*.jsonl,payload.cwd/mnt/c/Users/Me/Notes/MyVault~/.codex/state_5.sqlite,threads.cwdnormalizePathForComparisonlowercases only whenisWindowsAbsolutePathis true:/mnt/c/Users/...matches neither, so it takes the POSIX branch and keeps its capitals, while the stored value hasnone.
Paths under
/homeare unaffected, being lowercase already. WSL is not the cause:getcwd,realpathandos.path.realpathall preserve the capitalsSuggested fix
Treat
/mnt/<drive>/...as case-insensitive too — either by extendingshouldComparePathCaseInsensitivewith a^/mnt/[a-z]/test, or by lowercasing innormalizePathForComparisonwhenever the path resolves onto a DrvFs mount.Downstream impact
The Obsidian Agent Client plugin passes the for its "Show current vault only" filter.Under WSL mode that filter is always empty for any vault on a Windows drive.
Note
@agentclientprotocol/claude-agent-acp0.70.0 shows the same symptom, so the fix may be worth mirroring there.