fix(ai): isolate cursor agent config dirs for parallel execution#440
fix(ai): isolate cursor agent config dirs for parallel execution#440janhesters merged 1 commit intomasterfrom
Conversation
ianwhitedeveloper
left a comment
There was a problem hiding this comment.
Nice, focused fix — correct for the documented race and tests pass locally (238/238). Requesting a couple of changes before merge; the rest are nits.
Must-fix
1. Temp dirs are never cleaned up. Each spawn creates tmpdir()/riteway-cursor-<cuid> and nothing removes it. macOS purges /var/folders/... only on reboot, and Linux often never cleans /tmp. CI workers and dev machines running riteway ai repeatedly will leak directories indefinitely. Please add a best-effort await rm(dir, { recursive: true, force: true }) in a finally block in runAgentProcess.
Should-fix
2. Cursor detection by literal command string is fragile.
Why this matters and a suggested shape
const isCursorAgent = (command) => command === 'agent';A user-supplied registry (riteway.agent-config.json) or --agent-config file can use any command — /usr/local/bin/agent, cursor-agent, a wrapper script — and silently lose isolation. Conversely, any unrelated tool literally named agent would get a spurious CURSOR_CONFIG_DIR.
The PR description acknowledges Claude Code and Gemini CLI have the same class of bug, so the mechanism wants to be generic. Move the knowledge into agent-config.js:
cursor: {
command: 'agent',
args: ['--print', '--output-format', 'json'],
outputFormat: 'json',
isolateConfigEnv: 'CURSOR_CONFIG_DIR',
isolateConfigPrefix: 'riteway-cursor-'
}Then buildSpawnOptions(agentConfig) becomes generic and self-documenting, and the agentConfigFileSchema gains an explicit opt-in field for custom registries.
3. Verify (or guarantee) the dir exists. The code points CURSOR_CONFIG_DIR at a path that doesn't exist on disk. Works only if the Cursor CLI mkdir -ps it before writing .tmp. Either add a comment documenting the assumption against a specific cursor-cli version, or mkdirSync(dir, { recursive: true }) before spawn to be defensive.
Nits
Test assertions could be tightened
-
'does not set CURSOR_CONFIG_DIR for non-cursor agents'asserts onoptions?.env?.CURSOR_CONFIG_DIR === undefined. The actual contract today is that the third arg isundefinedentirely. Stronger:actual: options, expected: undefined
-
'passes a unique CURSOR_CONFIG_DIR env to each cursor agent spawn'only type-checksdir1. Ifdir2wereundefined,dir1 !== dir2would still betrueand mask the bug. Type-check both.
Docs
executeAgentJSDoc doesn't mention the implicit env mutation when the command is cursor — a one-liner note (or a comment nearbuildSpawnOptionsciting the upstream Cursor CLI bug) would help future readers.- No
RELEASING.md/ changelog entry; this unblocks parallel--agent cursorruns and probably deserves a note.
Out of scope but worth noting
No new untrusted input; spreading process.env into the child is appropriate; trust boundary documented in agent-config.js is preserved. No OWASP concerns from this diff.
|
Forgot to mention: manual testing corroborates the fix — the |
ianwhitedeveloper
left a comment
There was a problem hiding this comment.
Approving with the caveat that requested changes that seem salient are created as follow up action items
8973978 to
d5ccedd
Compare
|
Addressed the review feedback in d5ccedd — here's the rundown: 1. Temp dir cleanup (must-fix) — We don't think this needs explicit cleanup. Cursor's CLI itself cleans up these config dirs (that's actually how we discovered the race — the concurrent cleanup/rename was the crash site). The OS also clears 2. Cursor detection is fragile (should-fix) — Fixed. Isolation is now config-driven via 3. Verify dir exists (should-fix) — Added a comment documenting that the CLI creates the directory on first write. cuid2 makes path collisions effectively impossible (~1.4×10⁻³⁶ probability). 4. Non-cursor test assertion (nit) — Fixed. Now asserts 5. Type-check both dirs (nit) — Fixed. Both 6. JSDoc (nit) — Fixed. Added 7. Changelog (nit) — The changelog is auto-generated by |
…ndition When multiple Cursor CLI agents run concurrently, they all race on the same ~/.cursor/cli-config.json.tmp file during atomic config writes. One process renames the .tmp file while another tries to do the same, causing ENOENT crashes. Fix: set a unique config dir (via cuid2) per spawned agent process when the agent config specifies isolateConfigEnv. The isolation mechanism is generic and config-driven via isolateConfigEnv/isolateConfigPrefix fields in agent-config.js, so custom registries can opt in for any CLI with the same class of bug. Only the built-in cursor config enables it by default.
d5ccedd to
d1efa7f
Compare
Summary
~/.cursor/cli-config.json.tmpduring atomic config writes, causingENOENTcrashes onrenameCURSOR_CONFIG_DIR(using cuid2) pointing to an isolated temp directory, eliminating the raceRoot cause
The Cursor CLI writes config atomically via
cli-config.json.tmp→cli-config.jsonrename. With concurrent processes, one process completes the rename while another tries to rename the same.tmpfile that no longer exists. This is the same class of bug reported in Gemini CLI and Claude Code.Test plan
CURSOR_CONFIG_DIRCURSOR_CONFIG_DIRvaluesCURSOR_CONFIG_DIRset