Skip to content

fix: harden native Windows runtime - #1994

Open
sethkarten wants to merge 17 commits into
feat/windows-supportfrom
feat/windows-hardening
Open

fix: harden native Windows runtime#1994
sethkarten wants to merge 17 commits into
feat/windows-supportfrom
feat/windows-hardening

Conversation

@sethkarten

@sethkarten sethkarten commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Kevin handoff — Windows2 published, validation remains

Published head e1ebab8d389c60283c58f9e6aa7c2675efe5cd41, tree f946db82b2600cd185380ad1f586af79cf4c715c. Ordinary merge parents: the previously local Windows2 work 4396b2fd and published Windows repair 9282f0e8. Review order remains #1970#1982#1994. This is a handoff, not a merge-ready claim.

Current residual scope

The cleanup removes duplicated production fixes already covered by the integrated main/Windows parent. The retained residual is 21 files: five production files, fourteen test files, CI and a changelog fragment. All 20 non-CI residual files and their patch are byte-identical to the prepared Windows2 version; CI combines the existing Windows2 checks with the updated parent.

  • Session-owned scratch directories, with host daemon/worker runtime locations captured before scratch environment changes and preserved through nested kernels.
  • Trusted automatic Windows Git Bash selection; nonstandard shells require explicit shellPath.
  • Shutdown-client capture across awaits, so disconnect/replacement cannot select the wrong client.
  • Regression coverage for canonical settings, leases/bootstrap, recovery/deletion, shell trust and lifecycle behavior. Bounded native named-pipe lifecycle checks retain cleanup/failure evidence.
  • CPython .cmd/.bat overrides remain rejected. Earlier proposed batch-shim support and duplicate persistence/display implementations are not current residual features.

Validation and limitations

  • This ordinary parent integration passed one read-only project check and normal commit hooks: pinned Bun 1.4.0+34cbb9a40, 1,008 files, no automatic fixes; types, installer rendering and browser smoke passed. The inherited informational noUselessStringRaw remains unchanged. Local pwsh parsing is unavailable.
  • Earlier focused local scratch/runtime-directory, shell-trust and lifecycle proofs are retained. No broad tests or build were rerun for this handoff integration. Those results do not certify fresh native Windows behavior on this head.
  • Parent feat: add native Windows support #1982 now contains the signal-launch and uninstall-host repairs. The earlier f812bdf3 NUL startup/installer/native-build results are not proof that those repairs or this Windows2 combination pass. Arm64 has only cross-compilation/PE-inspection evidence, not native execution.

Kevin's remaining plan

  • Ensure CI actually starts and inspect the exact-head results. At the publication snapshot the parent Windows native CI result was not yet available; do not infer a pass from missing checks or GitHub UNKNOWN mergeability.
  • Validate bare/compiled Windows identity and tree termination, caller-exit behavior, exec cancellation, uninstall-host continuation, terminal and persistent CPython checks; then Windows2 scratch/nested-kernel and named-pipe lifecycle/hardening cases. Address real failures without weakening ownership/trust/cleanup assertions.
  • Integrate newer main parent-first: last integrated bf8894afa55832f7cfa2094c8a0d041bc680a691; snapshot 4ec05a0969825a32261ffed43ab5740071a73ae6 includes fix(ai): build from the committed model catalog instead of refetching it #2038 committed-catalog builds, fix(coding-agent): always render inactive sessions in the agents view #2091 inactive agents view and default prime inference to glm 5.3 #2123 GLM 5.3 default. These were deliberately left for takeover.
  • Recheck residual scope against refreshed main, exact-head CI/reviews and stacked ancestry; resolve remaining Windows review threads with suitable proof. Keep three PRs, Bun-only runtime scope, CPython batch rejection and all canonical security/persistence behavior. No force-push/history rewrite, PR approval/merge or release action was performed during handoff.

Refs RES-1268.

Historical description and old-source validation — superseded scope, not current-head proof

The original description below includes implementations later deduplicated or withdrawn. Use the current residual scope above, particularly for CPython batch-shim behavior.

Summary

This PR hardens native Windows support after #1982. It keeps Git Bash as the default shell and does not use WSL.

  • prevents background daemon, worker, catalog, and update processes from flashing console windows
  • gives slow Windows worker launches a bounded 90-second supervisor handshake and a 120-second client create timeout
  • makes settings, session leases, kernel bootstrap locks, and RLM display tombstones survive common Windows sharing violations
  • keeps RLM deletion durable and retryable without duplicate terminal notices or late state resurrection
  • executes .cmd and .bat shims through a controlled cmd.exe command string with exact metacharacter tests
  • forces UTF-8 mode for the persistent CPython process on Windows and keeps CPython 3.11
  • selects only canonical Git for Windows installs by default; nonstandard installs require shellPath
  • adds Windows named-pipe support for Herdr, BOM-tolerant frontmatter, native tilde path handling, and raw multiline paste handling
  • gives persisted sessions an owned scratch directory through PRIME_AGENT_SESSION_TMP, TMP, TEMP, and TMPDIR

Safety boundaries

  • no WSL, Cygwin, or MSYS2 fallback from PATH
  • no shell: true
  • no guessed cleanup of user files
  • no IPython, Jupyter, ZeroMQ, or ipykernel
  • foreground interactive consoles remain visible
  • macOS and Linux keep their existing worker deadline and retry cadence

Local validation

  • ~/.bun/bin/bun run check
  • 208 Windows-hardening behavior tests in the CI test set: 208 passed, 2 Windows-only tests skipped locally
  • focused RLM lifecycle set: 4 passed
  • full RLM/display/daemon set: 306 passed
  • daemon named-pipe lifecycle and batch-shim set: 3 passed, 1 Windows-only test skipped locally
  • settings: 45 passed
  • session leases and recovery journal: 24 passed
  • terminal input: 67 passed
  • real persistent CPython/ACP kernel: 4 passed
  • git diff --check

Native Windows validation

Final run: https://github.com/PrimeIntellect-ai/prime-agent/actions/runs/33650350794

  • all 14 PR checks passed
  • Windows behavior: 200 passed, 6 platform-specific skips, 0 failed across 19 files
  • RLM deletion/finalization races: 4 passed
  • real supervisor-to-worker named-pipe lifecycle: passed in 8.15 seconds
  • terminal/TUI: 224 passed
  • persistent CPython at D:\a\_temp\Prime Agent ø\kernel-venv: 19 passed, 2 skipped
  • Win32 job and interrupt runtime: 25 ran, 1 expected skip, result OK
  • x64 executable PE machine: 0x8664
  • Arm64 executable PE machine: 0xAA64
  • PowerShell installer install, version 0.9.1, update, and uninstall: passed
  • compiled pi.exe --version (0.9.1) and pi.exe --help: passed

Stack

Base: feat/windows-support from #1982. This PR does not modify #1982 and should be reviewed after it.

Current stack integration

Ordinary merges include the updated Windows-support base. The daemon launch resolution retains launcher-aware subprocesses and windowsHide. Root checks passed. Isolated Linux Docker validation passed 445 targeted tests and 19 process stress tests, with one Windows-only case deferred to the native runner. Native Windows CI is rerunning on the updated head.

Related tracking: RES-1268 covers the overlapping Windows runtime and kernel compatibility work.

Note

Harden Windows runtime: bootstrap locks, shell selection, batch shims, and rename retries

  • Rewrites acquireBootstrapLock to atomically publish UUID-named candidate directories, reclaim stale locks through a proper-lockfile guard, and return an idempotent release that only removes a lock still owned by the current process
  • Reworks acquireSessionLease to fail closed on unreadable or corrupt owner files, retry candidate-vanishing races, and quarantine stale lease directories before deletion
  • Routes Windows .cmd/.bat Python shims through cmd.exe with arguments passed via validated environment variables, rejecting unsafe metacharacters
  • Limits Windows automatic shell selection to Git Bash in standard Program Files locations; removes PATH-based fallback for Cygwin/MSYS2/WSL
  • Adds bounded retry handling for transient Windows EBUSY/EPERM/EACCES rename failures across bootstrap locks, session leases, settings writes, and RLM display entries
  • Introduces a per-session scratch directory wired into RLM child environments (PRIME_AGENT_SESSION_TMP, TMP, TEMP, TMPDIR) and system prompt guidance
  • Adds raw multiline paste detection in tui.StdinBuffer so embedded-newline input without escape characters emits as a single paste event
  • Hides console windows for non-interactive Windows child processes across daemon, supervisor, and CLI launch paths
  • Risk: getShellConfig now throws on Windows when no Git Bash is found in standard locations, where it previously fell back to PATH-resolved bash; users with non-standard Git Bash installs must set an explicit shell path

Macroscope summarized 9063904.


Note

Medium Risk
Changes RLM/kernel temp layout and daemon env inheritance on all platforms, and Windows now errors instead of picking bash from PATH—both need careful rollout despite broad new tests.

Overview
Adds session-owned scratch directories for persisted sessions (artifactDir/tmp), exposes them to the kernel via PRIME_AGENT_SESSION_TMP and standard temp env vars, and documents them in the system prompt—while pinning daemon socket dir/endpoint to inherited defaults so nested scratch hosts do not relocate worker IPC.

Daemon runtime paths now honor PRIME_AGENT_INTERNAL_DAEMON_SOCKET_DIR and the supervisor socket env (with absolute-path validation and normalization), and worker stop only clears worker.client if it is still the client that was shut down, avoiding races on disconnect or replacement.

On Windows, automatic shell selection is limited to Git Bash under standard Program Files locations—PATH fallback for Cygwin/MSYS2/WSL is removed; nonstandard installs must set shellPath. CI gains isolated/long-timeout Windows runs for scratch isolation, RLM deletion races, and a full supervisor→worker named-pipe lifecycle test, plus coverage for settings atomic writes, session-lease EPERM handling, and bootstrap lock reclaim/wait behavior.

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

Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Fixed
@sethkarten
sethkarten marked this pull request as ready for review September 6, 2026 21:51

@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.

Stale Bugbot comment from a previous run.

Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Outdated
Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Outdated

@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.

Reviewed by Cursor Bugbot for commit bde87f1. Configure here.

Comment thread packages/coding-agent/src/core/session-lease.ts
Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Fixed
Comment thread packages/tui/src/stdin-buffer.ts
Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-supervisor.ts Outdated
Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts
Comment thread packages/coding-agent/src/modes/daemon/rlm-subagent-display.ts Outdated
Comment thread packages/coding-agent/src/utils/shell.ts Outdated
Comment thread packages/coding-agent/src/utils/shell.ts
Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Outdated
Comment thread packages/coding-agent/src/core/kernel/bootstrap.ts Outdated
@sethkarten

Copy link
Copy Markdown
Contributor Author

Fixed the native Windows shutdown failure in 906390483. The disconnect callback could clear worker.client while shutdown awaited its response, causing worker.client.close() to throw and leave the supervisor running. Shutdown now retains the original client reference and only clears the worker field if it still references that client.

Validation:

  • Both normal/archive disconnect regressions fail on ce2d04f87.
  • Corrected isolated Docker tests: 40 passed, including the supervisor-worker lifecycle test.
  • Full isolated Docker process stress: 19/19 passed.
  • Root and pre-commit checks passed.

The failing Windows log is from https://github.com/PrimeIntellect-ai/prime-agent/actions/runs/34067874964/job/101579752358. Fresh native Windows validation is required on the new head. No installer, shell-discovery, or protocol changes were made.

@sethkarten

Copy link
Copy Markdown
Contributor Author

Native Windows passed on current head 906390483: https://github.com/PrimeIntellect-ai/prime-agent/actions/runs/34068337321/job/101580997854

Verified in the hosted log:

  • The previously failing supervisor-worker shutdown lifecycle now passes.
  • Old release handles cannot remove a newer lease; concurrent release calls share cleanup; a failed release can retry while retaining successful-release idempotence.
  • Real .cmd and .bat Python overrides accept the current runtime and reject a stale runtime.
  • Windows behavior, deletion races, CPython, PowerShell 5.1 installer, and executable smoke tests pass.

This is native Windows x64 execution; Windows Arm64 is cross-compilation and PE inspection only. All current functional CI and both review bots have passed. JavaScript/TypeScript CodeQL is still running; final readiness is not yet claimed.

@sethkarten

Copy link
Copy Markdown
Contributor Author

Final stack readiness audit passed.

PR Verified head Status
#1970 5d685c425 Mergeable; all latest checks pass
#1982 9680ac3df Mergeable; all latest checks pass
#1994 906390483 Mergeable; all latest checks pass

JavaScript/TypeScript CodeQL has completed successfully. Current-head native Windows, both review bots, root checks, isolated Docker behavioral tests, and all 19 process-stress tests passed. Full review threads, review summaries, and issue comments were audited; no unresolved actionable findings remain. Design-conflicting shell-discovery advice was rejected with the trust-boundary rationale rather than reverted.

Fresh fetch confirms current main → #1970 → #1982 → #1994 ancestry. All three worktrees are clean and match their published heads. Native Windows x64 evidence: https://github.com/PrimeIntellect-ai/prime-agent/actions/runs/34068337321/job/101580997854. Arm64 evidence remains cross-compilation and PE inspection, not native execution.

Only human approval remains. No PR was merged. Automated monitoring is being stopped.

sethkarten added a commit that referenced this pull request Sep 7, 2026
Import selective Bun-independent fixes from #1994 at
ce2d04f, relative to declared
#1982 base 9680ac3.

Preserve #2036 Windows seams and current Node runtime. Keep Bun,
installer, shell-policy rollout, scratch directories, and CPython
batch overrides outside this consolidation. #1994 remains partial.
Atomic writer and bootstrap-lock overlap awaits canonical #2035
reconciliation after parent stacks land.
sethkarten added a commit that referenced this pull request Sep 7, 2026
* fix(coding-agent): resolve the kernel venv python under Scripts on win32

uv creates <venv>/Scripts/python.exe on Windows, but both bootstrap sites
joined <venv>/bin/python unconditionally, so first-run kernel bootstrap
always failed on native Windows. Mirror the existing win32 uv.exe branch
with one kernelVenvPython helper used at both sites.

* fix(coding-agent): expand ~/ with the platform path separator

Tilde expansion concatenated homedir() with the posix remainder at three
sites (expandPath, expandTildePath, and an inline PI_PACKAGE_DIR copy),
producing mixed-separator paths like C:\Users\u/rest on Windows. Join
through the platform path module instead and route PI_PACKAGE_DIR through
expandTildePath.

* fix(coding-agent): prefer non-System32 bash over the WSL trampoline on PATH

findBashOnPath took the first where-match, and System32\bash.exe (the WSL
launcher, which executes Linux-side) precedes per-user Git Bash installs on
default PATHs. Order matches so %SystemRoot% entries are only a last
resort.

* fix(coding-agent): dial the Herdr socket inside the named-pipe namespace on win32

The built-in Herdr reporter connected to the exported unix-style socket
path verbatim; on Windows a local-domain connection must target
\\.\pipe\, and the reporter swallows connect errors by design, so the
mismatch was invisible. Map unmapped paths into the pipe namespace before
dialing.

* fix(coding-agent): cache the win32 process start id per pid for a short TTL

getWindowsProcessStartId spawns a synchronous powershell query that blocks
the event loop, and supervisor liveness checks plus daemon-ps poll loops
call it repeatedly for the same pid. Memoize successful ids for 5s so hot
paths reuse one query.

* fix(coding-agent): derive handshake budgets from the outer connect deadline

connectWorker granted every hello/worker_auth attempt a fixed 1s clock, so
a handshake that consistently needs longer (slow win32 boxes) failed every
retry regardless of the 30s outer budget, and the adopt/recover paths
shrank even the outer budget to 1.5-2s. Per-attempt waits now consume the
remaining outer deadline, all connectWorker callers use the one 30s
constant, and the downward 1s hello/auth overrides in daemon-mode and the
routed client fall back to the client defaults.

* fix(coding-agent): route background spawns through hidden-window wrappers

windowsHide defaults to false, so console-subsystem children of a
windowless daemon or detached worker allocate a fresh console and flash or
steal focus on every spawn; only one call site in the repo set the flag.
Add spawnHidden/spawnSyncHidden/execSyncHidden/execFileHidden/
execFileSyncHidden to utils/child-process.ts and migrate all 49
non-interactive coding-agent spawn/exec sites; the tui package's two
background spawns set windowsHide inline. Interactive spawns (editors,
inherit-stdio installers and relaunches) intentionally keep their console.

* chore(coding-agent): add windows-seams changelog fragments; tighten new comments

* test(coding-agent): align clipboard and login-dialog spawn assertions with hidden-window wrappers

* revert(coding-agent): drop the win32 start-id TTL cache

The per-pid cache could hand a recycled pid the dead process's start id
for up to 5s. The start id exists solely to detect pid recycling, and its
consumers act on that identity: the orphan-process journal decides
taskkill /T from it and daemon-ps shutdown waits poll it for change, so a
stale match can kill a replacement process tree or mask an exited owner.
No caller is both hot and staleness-tolerant; the handshake latency issue
is already addressed by deriving budgets from the outer deadline.

* fix(coding-agent): match Herdr pipe-namespace prefixes case-insensitively

Windows pipe names are case-insensitive, but the pass-through check was
case-sensitive, so a valid \\.\PIPE\... target was re-joined into a
different pipe name and the error-swallowing reporter failed silently.
Lowercase only the prefix check; the returned path keeps its casing.

* refactor(coding-agent): tighten windows-seam comments and consolidate pins

Collapse the wrapper doc block to one line, drop test comments that restate
assertions, share one recording wrapper in the child-process mock, unwrap
single-test describes, and remove one posix assertion already covered by
its sibling. No behavior coverage removed; all pins keep their failing
assertions.

* fix(coding-agent,tui): harden current-runtime Windows sessions

Import selective Bun-independent fixes from #1994 at
ce2d04f, relative to declared
#1982 base 9680ac3.

Preserve #2036 Windows seams and current Node runtime. Keep Bun,
installer, shell-policy rollout, scratch directories, and CPython
batch overrides outside this consolidation. #1994 remains partial.
Atomic writer and bootstrap-lock overlap awaits canonical #2035
reconciliation after parent stacks land.

* fix(coding-agent): normalize Windows bash candidate paths

---------

Co-authored-by: Seth <seth@primeintellect.ai>
@sethkarten

Copy link
Copy Markdown
Contributor Author

Kevin takeover: the prepared work is published across Bun #1970 → Windows #1982 → Windows2 #1994. This PR is at e1ebab8d389c60283c58f9e6aa7c2675efe5cd41.

Full handoff, evidence limits and remaining plan/TODO. Native repair/hardening CI and newer-main integration remain to be verified; this is not a full-stack readiness claim. No further fix rounds are running.

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