Skip to content

feat(coding-agent): add native Windows support - #744

Closed
WDDGDA wants to merge 7 commits into
PrimeIntellect-ai:mainfrom
WDDGDA:feat/windows-native-support
Closed

feat(coding-agent): add native Windows support#744
WDDGDA wants to merge 7 commits into
PrimeIntellect-ai:mainfrom
WDDGDA:feat/windows-native-support

Conversation

@WDDGDA

@WDDGDA WDDGDA commented Aug 6, 2026

Copy link
Copy Markdown

Summary

  • add a PowerShell 5.1 installer with strict SemVer parsing, SHA-256 verification, x64/Git-for-Windows checks, and release-channel integration
  • fix Windows kernel venv resolution, directory-fsync handling, stale lease recovery, hidden child processes, and named-pipe test fixtures
  • add fail-closed Windows Job Object isolation so daemon Bash trees are assigned before commands run and are terminated after worker crashes
  • replace repeated PowerShell PID queries with native GetProcessTimes, and add Windows CI coverage for installer, kernel, daemon, and recovery paths

Process safety

  • Windows daemon isolation supports x64 Node.js and Git for Windows only
  • worker and replacement supervisor processes remain outside the Job; only gated Bash trees are assigned
  • native/assignment failures reject command execution instead of degrading to an unisolated mode
  • no persistent disk or network I/O is added by Job isolation

Validation

  • clean latest-main worktree: fresh npm ci, root build, checks, and Windows-targeted tests (89 passed, 7 platform skips)
  • final shell-contract update: TypeScript and Biome checks plus Windows-targeted tests (90 passed, 7 platform skips)
  • packaged install smoke: global package loaded exact koffi@2.16.2; hard worker kill removed the Bash leaf and a replacement worker became ready
  • public diff scanned for credentials, private keys, personal paths, email addresses, and non-test localhost URLs

Builds on the reports and proposed fixes in #663, #664, and #670.

Closes #660
Closes #665
Closes #666
Closes #667
Closes #668


Note

High Risk
Changes authentication-adjacent daemon lifecycle, session leases, and child-process termination on Windows using memory-unsafe FFI; failures are intended to fail closed but mis-assignment or Win32 errors could block Bash or leave orphans.

Overview
Adds native Windows x64 support: a published PowerShell installer, release/CI wiring, and runtime fixes so daemon workers, the Python kernel, and Bash behave correctly on Windows.

Install & docs: New install.ps1 (channel, SHA-256, x64 Node 22.8+, Git for Windows) is rendered and uploaded beside the shell installers; README/quickstart document irm … | iex. Root check:installer runs check-windows-installer.mjs. koffi@2.16.2 is a direct dependency for Win32 FFI.

Daemon process isolation: Daemon workers create a kill-on-close Job Object and gate Bash through Git’s direct usr\bin\bash.exe so the process is assigned to the job before descendants start; worker crash tears down that Bash tree while supervisors stay outside the job. Session lease identity on Windows uses GetProcessTimes via Koffi instead of spawning PowerShell per PID; lease/supervisor lock renames tolerate Windows EPERM when the destination already exists.

Cross-platform spawn hygiene: windowsHide: true on spawns; signalProcessTree / taskkill for tree kills; kernel bootstrap uses Scripts\python.exe and PowerShell uv install on Windows; command-recovery journal skips directory fsync when unsupported.

CI: New windows-latest job (installer check, Windows-targeted vitest, kernel bootstrap + import smoke) gates build-check-test.

Reviewed by Cursor Bugbot for commit 0ef7b8e. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add native Windows support with PowerShell installer, Job Object isolation, and hidden console windows

  • Adds install.ps1, a PowerShell installer for Windows x64 that requires Node.js ≥22.8.0 and Git Bash, downloads the release tarball, verifies SHA-256, and installs via npm.
  • Introduces windows-job-object.ts using koffi to bind Win32 Job Object APIs, enabling reliable process-tree termination for daemon workers on Windows.
  • Adds windowsHide: true to all spawn/spawnSync calls across the codebase so no console windows appear on Windows.
  • Fixes venvPython to use Scripts/python.exe on Windows and updates ensureUv to invoke the PowerShell-based uv installer instead of sh.
  • Fixes session lease acquisition to treat EPERM on rename (when destination exists) as contention rather than throwing, matching Windows filesystem semantics.
  • Adds new Git-for-Windows shell helpers (gitForWindowsRoot, getDirectWindowsBashPath, getWindowsGitBashLauncherPath) and probes LOCALAPPDATA\Programs\Git for bash detection.
  • Risk: koffi is added as a runtime dependency; Windows-specific paths (Job Objects, named pipes, junction symlinks) are active only on win32.

Macroscope summarized b9581b3.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2aa1a10. Configure here.

Comment thread packages/coding-agent/src/modes/daemon/daemon-supervisor.ts
@FlamesONE

Copy link
Copy Markdown

Tested this branch on real Windows hardware, since CI has no Windows runner for the suite.

Environment: Windows 11 Pro 26200 (x64), Node 24.15.0, npm 11.x, Python 3.11 via uv 0.11.6, Git Bash 2.51.0. Branch pr-744 at 2aa1a10, compared against main at c5991bc, both freshly built with npm run build.

Results

  • npm run check: passes.

  • test/windows-job-object.test.ts and test/bash-close-hang-windows.test.ts: pass (5/5). Note both need the workspace packages built first; with a stale packages/tui/dist the Job Object test fails with Cannot find package '@earendil-works/pi-tui' from the worker fixture, which reads as a Job Object failure but is not one.

  • Full packages/coding-agent suite:

    branch failing tests failing files
    main 137 44
    this PR 93 35

    44 tests fixed, no regressions: every test failing on this branch also fails on main.

Manual end-to-end check: the kernel starts from the built dist, resolves to kernel-venv\Scripts\python.exe, executes cells, and runs subprocess calls. The kernel process has MainWindowHandle=0, so the console window flashing from #668 is gone. doctor reports the daemon on \\.\pipe\prime-agent-daemon, and model list works.

Two things still blocking a green Windows suite

1. test/kernel-bootstrap.test.ts still hardcodes the POSIX path (17 failures).

The production path moved to venvPython(), but the test did not:

test/kernel-bootstrap.test.ts:182
await expect(ensureKernelPython()).resolves.toBe(join(venv, "bin", "python"));

Expected: "...\kernel-venv\bin\python"
Received: "...\kernel-venv\Scripts\python.exe"

These 17 also fail on main, but for the original reason, so this is not a regression — just the last piece of the same change. Exporting venvPython() (or a test-local equivalent) and using it in the assertions should clear all of them.

2. bootstrapVenv deletes a venv whose python.exe is still mapped (about 25 failures).

Every test that needs a live kernel and hits a bootstrap-version mismatch fails with:

Error: Failed to set up the Python kernel runtime.
EPERM: operation not permitted, unlink 'C:\Users\user\.prime\agent\kernel-venv\Scripts\python.exe'

Windows refuses to unlink a running image, so the rm(venv, { recursive: true, force: true }) before a rebuild cannot succeed while any kernel from that venv is alive. Affected files include kernel-attach-image-skill (9), kernel-agent-message-skill (6), kernel-rlm-heartbeat-skill (3), kernel-goal-skill (3), kernel-agent-observe-skill (2). This one is independent of this PR, so I opened #763 against main with a rename-then-delete plus tests. Nothing here needs to change for it.

The remaining failures (config, agents-view-*, package-command-paths, ipython-provisioner, several test/suite/regressions/*) fail identically on main and look out of scope here: path-separator assertions, chmod mode expectations such as expected 438 to be 384, and socket listen errors.

One suggestion

initializeWindowsDaemonWorkerJob() throws on any non-x64 arch:

if (process.arch !== "x64") {
    throw new Error(`Windows daemon process isolation requires x64, received ${process.arch}`);
}

JOBOBJECT_EXTENDED_LIMIT_INFORMATION is 144 bytes on every 64-bit Windows target, including arm64 — each member is either a fixed-width type or pointer-sized, so the x64 layout is also the arm64 layout. koffi ships a win32_arm64 build, and main.ts calls this without a try, so on Windows on ARM the daemon worker would die at startup rather than fall back. Widening the guard to reject only 32-bit arches would cover arm64 for free. I could not verify this on real arm64 hardware.

Separately, I checked whether GetLastError() through koffi returns stale values across calls, since the error codes here would be misleading if it did. It does not — OpenProcess on an invalid pid reports 87, and on pid 4 reports 5, both correct.

@lennix1337

Copy link
Copy Markdown

While testing the latest PR commit on Windows 11, I found one remaining visible-console issue in clipboard image handling: each powershell.exe clipboard probe could open another CMD/console window. This is fixed in follow-up commit 0ef7b8e: windowsHide: true is now passed to the clipboard subprocess, with a regression assertion. Without this, repeated clipboard operations could leave multiple console windows open.

@khanra17

khanra17 commented Aug 8, 2026

Copy link
Copy Markdown

Windows follow-up from a native Windows 11 x64 check:

@SandroHub013

Copy link
Copy Markdown

I reproduced a Windows-specific gap not covered by the current native-support summary. Herdr sets HERDR_SOCKET_PATH to C:\Users\...\AppData\Roaming\herdr\herdr.sock, but its server endpoint is the named pipe \\.\pipe\C:\Users\...\herdr.sock. Passing the env value directly to net.createConnection() fails with ENOTSOCK; prefixing \\.\pipe\ connects successfully.

I added a local adapter in herdr-agent-state.ts that preserves Unix paths and already-normalized pipe paths, plus Windows endpoint and session-path tests. This should be included in the umbrella Windows fix and covered by a native Windows Herdr smoke test.

@grimmjoww

Copy link
Copy Markdown

Validated this branch on real Windows 11 hardware (x64, Node 24.18.0, npm 11.12.1, Git Bash at the standard path) and fixed one remaining gap on top of it: #917. On Windows, reapOrphanProcesses polls journaled orphans but never signals them, so orphans outside the worker's Job Object survive a worker crash and recovery fails after three attempts.

Stacked PR on your fork: WDDGDA#1. One commit: the fix, a win32 regression test (observed failing with the exact #917 signature before the fix, passing after), and a changelog bullet.

Numbers from this machine: targeted Windows files 12/12; daemon-supervisor-process 8/8 with 6 platform skips; full coding-agent suite 87 failed / 3918 passed, mostly kernel/python provisioning and POSIX literals baked into test data — none from this change. Also ran install.ps1 end to end locally against a packed branch release: checksum-verified npm-global install, kernel venv provisioned, prime-agent --version printing from a fresh PowerShell.

@khanra17's two smaller points check out: #763 still reproduces by construction on this branch (bootstrap.ts does rm(venv, { recursive: true }) over a mapped executable), and the Git-Bash-from-git.exe idea can't be exercised on a standard-layout machine like mine, so I left it unpatched rather than guessing. @lennix1337's clipboard fix is in this head and its tests pass.

@BILLKISHORE

Copy link
Copy Markdown

The destinationExists gate on the lease rename is the right call, and it is stricter than the mapping in #727, which accepts EPERM without checking whether the target is actually there.

The path downstream of it is still open in both, though. readLeaseOwner (session-lease.ts:86-102) catches every error and returns undefined, and this branch leaves it unchanged. At :271 an undefined owner skips the isLeaseOwnerAlive guard and falls straight through to reclaimStaleLease. So when the lease directory exists but owner.json is briefly unreadable, which on Windows is a routine antivirus or indexer interaction, a live lease can still be reclaimed and two processes end up believing they own the session.

isProcessAlive at :104-111 already takes the safe side of exactly this question, returning true on EPERM rather than assuming the process is gone. Applying the same convention to readLeaseOwner, so that "unreadable" is distinct from "absent", would close it.

Separately, on the duplication @khanra17 raised for #763: it is wider than that one PR. This branch also reimplements the venv interpreter path (#663, #695), the directory fsync guard (#670), the lease EPERM mapping (#727), and the windowsHide spawn set (#664). However that gets resolved, it is four open PRs plus this one editing the same lines.

Replacing the powershell.exe call in getWindowsProcessStartId with a native query is a good change on its own, since that spawn ran on every lease check.

I have not reproduced the lease case. It needs a held handle on owner.json while a live session owns the lease.

Copy link
Copy Markdown
Member

Hi, thanks for taking the time to contribute to Prime Agent! Since open sourcing the project, we’ve received far more pull requests than we can responsibly review and validate. Prime Agent runs directly on users’ machines, so we need to be deliberate about which changes we accept and how they are reviewed. Rather than leave a large backlog that we cannot meaningfully work through, we’re closing the current PR queue and moving to a discussion-first contribution process.

We have established new contribution guidelines to help us continue iterating on Prime Agent and better manage contributions from the community. Going forward, we won’t review unsolicited pull requests. Instead, please start with a GitHub Discussion. We’ll identify recurring bugs and feature requests, create Issues for work we want to pursue, and invite pull requests from maintainers or vouched contributors when implementation is ready. Please read the full process documented in our contribution guidelines.

While we’re closing this backlog, we’re still reviewing it at a high level to identify recurring bugs, useful ideas, and important problems that we should address ourselves. Thanks again for the time you put into this!

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