Skip to content

Windows native support - #978

Closed
Anroshka wants to merge 3 commits into
PrimeIntellect-ai:mainfrom
Anroshka:windows-native-support
Closed

Windows native support#978
Anroshka wants to merge 3 commits into
PrimeIntellect-ai:mainfrom
Anroshka:windows-native-support

Conversation

@Anroshka

@Anroshka Anroshka commented Aug 8, 2026

Copy link
Copy Markdown

Summary

This PR makes Windows a first-class, fully supported runtime for prime-agent and fixes fundamental Windows asyncio subprocess constraints inside the IPython kernel.

Previously, running Prime Agent on a native Windows environment failed to reach a stable session due to compounding platform assumptions (venv script path resolution, zip extraction stream hangs, flashing console windows, fsync/rename EPERM errors, and ipykernel subprocess limitations).

With these changes, Prime Agent bootstraps cleanly, provisions binaries reliably without flashing console windows, and allows kernel code to spawn subprocesses (e.g. Playwright, Cargo, external CLI tools) and isolate its python environment.


Key Changes

1. Kernel Subprocess & Venv Isolation (packages/coding-agent/src/core/kernel/index.ts)

  • Asyncio Subprocess Fix: On Windows, ipykernel defaults to WindowsSelectorEventLoopPolicy (required for pyzmq). However, SelectorEventLoop cannot spawn subprocesses, causing asyncio.create_subprocess_exec (and tools like Playwright or Cargo) to fail with NotImplementedError.
    • Fix: Kernel startup re-binds the kernel's existing selector loop to the main thread (keeping ipykernel happy) and resets the default event loop policy back to WindowsProactorEventLoopPolicy. All worker loops created afterward can spawn subprocesses.
  • Venv Environment Activation: Ensures kernel spawns pass VIRTUAL_ENV and update PATH to include <venv>\Scripts, so uv pip install inside cells targets the kernel venv instead of system Python.

2. Windows Runtime & Bootstrap Stability

  • Venv Python Path Resolution: Resolved <venv>\Scripts\python.exe on Windows instead of hardcoded <venv>/bin/python, preventing perpetual venv teardown/rebuild cycles (bootstrap.ts).
  • Zip Extraction Fix: Replaced problematic yauzl/extract-zip stream extraction (which hangs on Node 20+ streams for multi-chunk zips) with Windows system tar (bsdtar.exe), backed by a time-boxed fallback (tools-manager.ts).
  • Console Window Suppression: Added windowsHide: true to background process spawns (daemon-launch.ts, exec.ts, shell.ts) to stop console window flashes during tool execution.
  • EPERM Protection: Guarded fsync directory calls in CommandRecoveryJournal.compact and adjusted directory-over-directory rename logic in session-lease.ts to handle Windows-specific EPERM error codes safely.
  • Path Concatenation Fix: Updated expandTildePath to use path.join instead of string concatenation (config.ts).
  • Git & Tooling Discovery: Expanded Git for Windows path discovery (Scoop, Chocolatey, user installs) and deprioritized WSL System32\bash.exe.

3. CI Pipeline & Documentation

  • Added .github/workflows/windows.yml: Windows CI workflow covering linting, unit tests, and kernel smoke tests.
  • Added scripts/windows-kernel-smoke.mjs: Automated headless test asserting venv resolution, binary caching, and cell subprocess execution.
  • Updated docs/windows.md & docs/quickstart.md with Windows runtime details and asyncio subprocess constraints.

Verification & Testing

  • Windows CI Workflow: All 12 test suites passing on windows-latest.
  • Kernel Smoke Test: Verified WindowsProactorEventLoopPolicy active while _WindowsSelectorEventLoop runs on main thread; top-level await works; asyncio.create_subprocess_exec succeeds.
  • Playwright Integration: Verified Playwright launching Chrome and driving web pages directly from IPython cells.
  • Non-Windows Regression: All POSIX semantics, windowsHide (no-op on Unix), and path resolution remain unchanged for macOS/Linux.

Note

Add native Windows support to the coding agent

  • Adds platform-aware kernel bootstrap: selects Scripts\python.exe (Windows) vs bin/python (POSIX), installs uv via PowerShell on Windows, and sets VIRTUAL_ENV/PATH so in-kernel package installs target the correct venv.
  • Patches asyncio inside the kernel on Windows to restore WindowsProactorEventLoopPolicy for subprocess-spawning event loops, avoiding NotImplementedError.
  • Adds windowsHide: true to all child process spawns across the codebase to suppress console window flashes.
  • Fixes session lease acquisition on Windows by treating EPERM/EACCES on an existing target directory as a taken lease rather than a fatal error, and swallows directory-fsync errors in the command recovery journal.
  • Adds Windows shell resolution that prefers Git Bash over the WSL launcher, with fallback candidates covering common install paths (Program Files, scoop, chocolatey).
  • Adds a Windows CI workflow (windows.yml) with build/typecheck, platform-scoped tests, and an end-to-end IPython kernel smoke test.

Macroscope summarized a68c9da.

Prime Agent could not reach a working session on a stock Windows box.
Launching it produced a burst of console windows that flashed open and
shut, and then nothing: no IPython tool, no search helpers, and a daemon
logging errors nobody saw. Six platform assumptions were compounding.

1. The kernel venv interpreter was hardcoded to `<venv>/bin/python`. uv
   creates `<venv>\Scripts\python.exe` on Windows, so every readiness
   probe failed. The venv was torn down and rebuilt on each launch, and
   the rebuild then failed at
   `uv pip install --python <venv>/bin/python`. The IPython tool — the
   agent's only built-in tool — was never available.

2. Zip archives are the only archive format used on Windows (fd and
   ripgrep ship `.tar.gz` elsewhere), and they were unpacked with
   extract-zip. Its yauzl read streams never settle on current Node
   releases: extraction hangs at the first entry large enough to span
   more than one chunk. `rg`/`fd` never installed, and every attempt
   leaked an `extract_tmp_*` directory. Windows ships bsdtar in System32
   and it reads zip, so `tar` is now the primary path with a time-boxed
   extract-zip fallback. Provisioning cleanup no longer masks the real
   error when antivirus holds a handle on a freshly extracted binary.

3. The daemon and its session workers were spawned detached without
   `windowsHide`, which leaves them with no console at all. Every
   console tool they then ran — git, uv, python, powershell — allocated
   a console of its own, which is the window storm. Spawns whose output
   is piped or discarded now pass `windowsHide`.

4. `CommandRecoveryJournal.compact` fsynced the containing directory.
   That is EPERM on Windows and aborted every supervisor `ack_result`.
   Guarded the way `cron-jobs.ts` already guards the same call.

5. Session leases are claimed by renaming a candidate directory onto the
   lease path, treating EEXIST/ENOTEMPTY as "already held". Windows
   reports a directory-over-directory rename as EPERM, so that branch
   never ran: stale leases were never reclaimed, and a live one surfaced
   as a raw EPERM instead of SessionAlreadyActiveError.

6. `expandTildePath` concatenated instead of joining, yielding
   `C:\Users\me/sessions` — a usable path that never compares equal to
   the same location built with `join()`.

Also on Windows: install uv through its PowerShell installer rather than
piping `install.sh` into a `sh` that does not exist; widen Git for
Windows discovery to per-user installs and Git resolved from PATH (scoop
and Chocolatey shims); and rank `System32\bash.exe` last, since the WSL
launcher resolves a different filesystem than the paths the agent
composes.

Off Windows every change is a no-op or unchanged behaviour:
`windowsHide` is ignored on POSIX, `getVenvPythonPath` returns the
previous path, the zip branch is unreachable where downloads are
tar.gz, and the lease and tilde helpers keep their POSIX semantics.

Coverage: a `Windows` workflow builds, lints, runs the platform-
sensitive suites, and runs an end-to-end kernel smoke test that asserts
the venv resolves to `Scripts\python.exe`, that a second bootstrap is a
cache hit rather than a rebuild, and that a kernel starts and executes a
cell. Symlink and POSIX-permission fixtures now gate on capability
probes instead of failing on Windows. The rest of the suite still
carries POSIX-only fixtures and is out of scope; docs/windows.md says so
explicitly.

Verified on Windows 11 / Node 26: the venv bootstraps once and stays
ready, the IPython kernel starts and executes, rg and fd install, the
daemon log is clean of EPERM, and startup creates no visible console
windows.
… venv

Two things the IPython tool could not do, both invisible until an agent
tried them.

**asyncio subprocesses were unavailable on Windows.** ipykernel installs
a Windows *selector* event loop policy because pyzmq needs `add_reader`,
and a selector loop cannot spawn subprocesses. Anything in the kernel
that shells out through asyncio — playwright, `create_subprocess_exec`,
any async driver that starts a helper binary — failed with a bare
`NotImplementedError`, and the obvious workaround of running it on a
fresh loop in a worker thread failed too, because `new_event_loop()`
inherits the same policy. There was no way out from inside a cell.

Startup now swaps the policy back to the proactor one, so loops created
from then on support subprocesses, and re-binds the kernel's own
already-running selector loop to the main thread so ipykernel keeps
exactly what it needs. Applied through a silent `execute_request` that
neither stores history nor leaves names in the user namespace, and
failure to apply is logged as a kernel diagnostic rather than failing
startup — a kernel without it is still a working kernel.

**The kernel could not see its own virtualenv.** The kernel process
inherited no `VIRTUAL_ENV` and no venv script directory on `PATH`, so a
`uv pip install` or `pip install` issued from a cell resolved against
whatever interpreter `PATH` pointed at — usually a system Python — and
installed packages somewhere the kernel could not import them from. The
spawn env now activates the venv the way `activate` would, and leaves a
non-venv interpreter (`PRIME_AGENT_KERNEL_PYTHON` pointing at a system
or conda Python) untouched. Explicit per-kernel `env` overrides still
win over both.

Verified on Windows 11 / Node 26 / CPython 3.11: policy is
WindowsProactorEventLoopPolicy while the kernel's main-thread loop stays
_WindowsSelectorEventLoop, top-level await still works, an
`asyncio.create_subprocess_exec` on a worker thread succeeds, playwright
launches Chrome and drives a page from inside a cell, and `VIRTUAL_ENV`
matches `sys.prefix` with `uv pip list` resolving to the kernel venv.
@sethkarten

Copy link
Copy Markdown
Contributor

Thank you for the report and proposed work. This root cause is now covered by maintainer-owned stacked PR #1163, authored independently from upstream/main.

We did not inspect or reuse this PR's diff, branch, commits, implementation code, or tests; its public description/comments were used only as a bug report. To keep one review surface, this PR is superseded by #1163 and is being closed.

The complete review stack is #1158#1165. It is being left unmerged for human review after CI and review-bot findings are cleared.

@sethkarten sethkarten closed this Aug 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants