fix(coding-agent): create the win32 internal RPC socket dir recursively (fixes #1370) - #1420
fix(coding-agent): create the win32 internal RPC socket dir recursively (fixes #1370)#1420MoerAI wants to merge 2 commits into
Conversation
…ly (fixes code-yeongyu#1370) runHostSupervisor() calls createInternalSocketPath(paths.dir) with paths.dir set to <agentDir>/rpc-host-daemon. On win32 that directory was created with mkdir(..., { recursive: false }), which requires rpc-host-daemon to already exist. ensureHost() creates it before spawning, but a direct '--internal-rpc-host-supervisor' launch does not, so on a fresh Windows profile the supervisor died during bootstrap with ENOENT: no such file or directory, mkdir '<agentDir>\\rpc-host-daemon\\internal-<uuid>'. The win32 branch now creates the directory recursively. createInternalSocketPath is exported and takes an injectable platform, mirroring spawnableChildLaunch in the same module, so the win32 bootstrap path is covered from any host. The posix branch is unchanged: it roots the directory in the OS temp dir, which always exists.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6697b134b2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (platform === "win32") { | ||
| const dir = join(baseDir, `internal-${randomUUID()}`); | ||
| await mkdir(dir, { recursive: false, mode: 0o700 }); | ||
| await mkdir(dir, { recursive: true, mode: 0o700 }); |
There was a problem hiding this comment.
Record the RPC behavior change in its scoped docs
This changes fresh-profile startup behavior in host-lifecycle.ts, but the commit leaves both packages/coding-agent/src/modes/rpc/changes.md and the shared-host lifecycle section of docs/rpc.md untouched. Record the Windows bootstrap behavior in both files in this increment so the fork tracker and documented lifecycle contract remain aligned.
AGENTS.md reference: packages/coding-agent/src/modes/rpc/AGENTS.md:L52-L52
Useful? React with 👍 / 👎.
| // Regression coverage for https://github.com/code-yeongyu/senpi/issues/1370 | ||
| describe("createInternalSocketPath", () => { |
There was a problem hiding this comment.
Move the issue regression out of the legacy root cluster
Because this block is explicitly regression coverage for issue #1370, adding it to the legacy test/*.test.ts cluster violates the scoped test-placement rule. Move the coverage into a suitably named test/suite/regressions/1370-*.test.ts file rather than growing this root test file.
AGENTS.md reference: packages/coding-agent/test/AGENTS.md:L50-L50
Useful? React with 👍 / 👎.
…regressions test/AGENTS.md scopes issue regressions to suite/regressions/<issue>-<slug>.test.ts and says the legacy flat test/*.test.ts cluster must not grow, so the coverage moves out of rpc-host-lifecycle.test.ts, which is restored to its upstream content. src/modes/rpc/AGENTS.md also requires a behavior change to update changes.md and docs/rpc.md in the same increment, so both now record the recursive win32 internal socket directory and note that the public socket secret stays caller-provisioned. Reported by Codex review on code-yeongyu#1420.
Summary
On win32 the RPC host supervisor created its internal socket directory with
mkdir(..., { recursive: false })under<agentDir>/rpc-host-daemon.ensureHost()creates that parent before spawning, but a direct--internal-rpc-host-supervisorlaunch does not, so on a fresh Windows profile the supervisor died during bootstrap.This makes the win32 branch create the directory recursively.
Addresses stage 1 of #1370.
Root cause
runHostSupervisor()callscreateInternalSocketPath(paths.dir)withpaths.dir=<agentDir>/rpc-host-daemon(host-lifecycle.ts). The win32 branch then does:recursive: falserequiresrpc-host-daemonto exist. The posix branch is unaffected because it roots the directory intmpdir(), which always exists — which is why a fresh profile works on macOS but not on Windows.Changes
packages/coding-agent/src/modes/rpc/host-lifecycle.tsrecursive: true;createInternalSocketPathis exported and takes an injectableplatform, mirroringspawnableChildLaunchin the same modulepackages/coding-agent/test/rpc-host-lifecycle.test.tspackages/coding-agent/CHANGELOG.md[Unreleased] > FixedentryThe posix branch is untouched. The injectable
platformis what makes the win32 bootstrap testable from a non-Windows host; the same pattern already exists in this file forspawnableChildLaunch(launch, platform).Reproduction (before fix)
That is the same
ENOENT ... mkdir '<agentDir>\rpc-host-daemon\internal-<uuid>'reported in the issue.Verification (after fix)
Real-CLI QA (
.agents/skills/senpi-qa), each asserting~/.senpi/agent/auth.jsonis unchanged:Evidence captured under
local-ignore/qa-evidence/20260907-1370-rpc-internal-socket-mkdir/(gitignored).Test
packages/coding-agent/test/rpc-host-lifecycle.test.ts(win32 bootstrap + posix guard)bun run check— cleanPre-existing failures, not caused by this change
test/rpc-host-lifecycle.test.ts > ensureHost-spawned host lifecyclehas 6 failures on this machine (RPC socket host exited with code 1 before answering get_protocol_info). They reproduce identically withhost-lifecycle.tsandrpc-host-lifecycle.test.tsrestored verbatim fromorigin/main:Remaining work on #1370 (stage 2)
Stage 2 of the issue —
ENOENT ... open '<agentDir>/rpc/rpc.sock.secret'— is deliberately not addressed here. It comes from the unguarded read inrunHostSupervisor():The public secret is written by
ensureHost()(host-ensure.ts,createSocketSecret(socketSecretPath(socket))before the spawn), andprepareSocketPath()returns early on win32, so a direct supervisor launch has nobody to create it. Making the supervisor create orensureSocketSecret()it would change the bootstrap contract from "requires a caller-created public secret" to "self-provisions one", which is your call rather than a mechanical fix. Happy to follow up in a separate PR once you say which semantics you want.Summary by cubic
Fixes the Windows RPC host supervisor crashing on fresh profiles when launched directly via
--internal-rpc-host-supervisor, because the internal socket directory under<agentDir>/rpc-host-daemonis now created recursively.mkdirwithrecursive: false, which required the parent to already exist;ensureHost()created it, but a direct supervisor launch did not, so themkdirfailed withENOENT.createInternalSocketPathis now exported with an injectableplatformso the win32 path is testable from any OS, mirroring the existingspawnableChildLaunchpattern.test/suite/regressions/1370-rpc-internal-socket-mkdir.test.ts, pertest/AGENTS.md; it covers the win32 bootstrap and guards that the posix branch stays rooted in the OS temp dir.docs/rpc.md, andsrc/modes/rpc/changes.md.Written for commit f9cf4dd. Summary will update on new commits.