Reviewed by Codex
PR #1025 board brief

Native Windows Support in Agent Orchestrator

Baseorigin/main
Headpr-1025 / feat/windows-platform-adapter
Diff+9,846 / -1,360
What changed

Windows runs AO natively

  • ao start, ao stop, ao dashboard, ao spawn, ao doctor, and ao update now have Windows paths.
  • Windows defaults to runtime: process instead of tmux.
  • Dashboard terminal works through the direct PTY server, not iTerm/tmux attach.
How it changed

ConPTY is wrapped like tmux

  • Each Windows session gets a detached pty-host process.
  • The host owns node-pty + ConPTY and exposes \\.\pipe\ao-pty-<sessionId>.
  • The mux WebSocket server relays xterm data to the named pipe.
Guardrail

Platform checks moved to helpers

  • isWindows, getShell, killProcessTree, findPidByPort, and getEnvDefaults centralize OS behavior.
  • pathsEqual and canonicalCompareKey stop Windows path identity bugs.
  • CI now runs Linux and Windows matrices for typecheck, tests, and web tests.

How The Windows Path Works

The key design is not "make tmux work on Windows." It is "keep AO's outer session model unchanged, then swap the terminal transport underneath."

Runtime architecture
1. AO CLIao start loads config and uses getDefaultRuntime().
2. process runtimeWindows selects runtime-process instead of runtime-tmux.
3. pty-hostDetached Node helper owns one ConPTY session and a scrollback buffer.
4. named pipeBinary frames over \\.\pipe\ao-pty-id: data, input, resize, status, kill.
5. mux WSBrowser terminal frames are relayed to the pipe, scoped by project and session.
6. xterm dashboardThe same dashboard terminal UI renders output on Windows, Linux, and macOS.

Behavior: Before vs Now

These are the user-visible outcomes of the branch. No repeated claims: each card maps to a specific implementation area.

1. Platform Adapter

The corrected direction is centralization: Windows branching lives in packages/core/src/platform.ts, not scattered call-site checks.

Before, origin/main

Platform assumptions were implicit

  • Runtime selection and CLI startup were effectively tmux-first.
  • Shell commands assumed Unix-ish execution more often than Windows PowerShell.
  • Process tree kill, PID-by-port lookup, and env defaults were not one tested adapter surface.
After, PR head

One adapter owns OS behavior

  • getDefaultRuntime(): Windows returns process, Unix returns tmux.
  • getShell(): AO_SHELL, pwsh, absolute Windows PowerShell, then cmd.exe.
  • killProcessTree(), findPidByPort(), and getEnvDefaults() cover Windows/POSIX differences behind tested helpers.

2. Runtime: tmux vs Process

This is the architectural center of PR #1025: keep AO's session model, replace the Windows terminal substrate.

New Windows runtime lane

ConPTY via node-pty + named pipes

1. Spawn

runtime-process creates a detached pty-host per session with windowsHide: true.

2. Own ConPTY

The pty-host owns node-pty and ConPTY, preserving scrollback and terminal status.

3. Relay

Clients connect over \\.\pipe\ao-pty-<sessionId> using framed binary messages.

CapabilityBeforeAfterImplication
Terminal durabilityUnix durability came from tmux. Native Windows had no equivalent daemon.Detached pty-host mirrors the tmux-daemon role for Windows sessions.Agent terminal survives parent/server restarts more predictably.
Input/outputDashboard terminal paths expected tmux attach semantics.Pipe protocol sends data, input, resize, output request, status, and kill frames.The browser terminal works without tmux or WSL.
ShutdownForce-killing during ConPTY startup could leave helper processes and error dialogs.Cooperative MSG_KILL_REQ, 500ms poll, then killProcessTree() fallback.ao stop can clean detached hosts safely.

3. Dashboard Terminal

The web UI still speaks the same mux protocol externally, but the server swaps tmux PTY management for a Windows named-pipe relay.

Before, origin/main

Terminal manager assumed tmux

  • TerminalManager attached to tmux sessions through node-pty.
  • Session lookup was tmux-name oriented.
  • Windows direct terminal behavior was limited by the lack of native tmux.
After, PR head

Mux relays to Windows pipes

  • handleWindowsPipeMessage() maps (projectId, sessionId) to a pipe socket.
  • resolvePipePath(sessionId, projectId) reads session metadata to find the pty-host pipe.
  • Outbound terminal frames echo projectId so project pages route xterm output correctly.

4. CLI Commands

The CLI changes are practical Windows work: startup defaults, stop cleanup, PowerShell scripts, and ao open no longer depending on tmux list-sessions.

CommandBeforeAfterWhy it matters
ao startUnix dashboard process-group behavior leaked into Windows assumptions.Uses platform-aware runtime fallback and avoids detached dashboard child on Windows.Ctrl+C reaches the console group naturally on Windows.
ao stopParent process teardown could miss detached Windows pty-hosts.Calls sweepWindowsPtyHosts() and then process-tree kill.Stops ConPTY helpers that taskkill /T on the parent cannot see.
ao doctor / ao updateShell script flow was Unix-oriented.Ships ao-doctor.ps1 and ao-update.ps1.Windows users get native setup and update checks.
ao openSession discovery depended on tmux sessions.Reads sm.list(); Windows opens Terminal/cmd attach or dashboard fallback.Process-runtime sessions appear and can be opened on Windows.

5. Agent Plugin Compatibility

The Windows port is only real if agents launch, detect, and update metadata correctly. This PR touched Claude Code, Codex, Aider, OpenCode, Kimicode, Cursor, worktree/clone, and desktop notifier paths.

Plugin hardening

PATH wrappers

setupPathWrapperWorkspace() generates .cjs + .cmd wrappers for gh/git interception on Windows.

PATHEXT detection

Agent detect() calls use shell: isWindows() so npm .cmd shims resolve.

PowerShell launch

Quoted absolute binaries are called with PowerShell's & operator where needed.

Metadata hooks

Claude/Codex metadata update paths understand Windows-safe Node wrappers and V2 .json metadata.

Drive slugs

Claude Code preserves Windows drive-letter slug encoding for session lookup.

Notifier

Desktop notifications gain Windows toast support while network notifiers remain available.

6. Filesystem, Shell, and Path Safety

Most Windows bugs in this PR were not glamorous: path case, junctions, file-handle races, PATHEXT, PowerShell, and process liveness edge cases.

Path-safe helpers
  • pathsEqual() resolves real paths and lowercases compare keys on Windows.
  • canonicalCompareKey() creates stable Map/Set keys for paths.
  • Worktree removal retries rmSync around Windows file-handle drain races.
  • Symlink fallback uses Windows-friendly junctions/hardlinks where appropriate.
Shell-safe helpers
  • PowerShell is resolved by absolute path if PATH is degraded.
  • Git Bash auto-detection walks PATH and avoids WSL bash for Windows cwd flows.
  • windowsHide: true suppresses console flashes for background subprocesses.
  • EPERM is treated as alive during Windows liveness probes; ESRCH means gone.

7. Documentation and CI

The final commits turn the port into a maintainable contract: documentation says what to do, CI proves both OS lanes still work.

Docs
  • docs/CROSS_PLATFORM.md adds the golden rule: use isWindows(), never new inline process.platform === "win32".
  • docs/ARCHITECTURE.md documents pty-hosts, pipe protocol, registry, sweep, and mux WS Windows branching.
  • website/content/docs/platforms.mdx reflects Windows process runtime and dashboard terminal support.
CI
  • Typecheck runs on ubuntu-latest and windows-latest.
  • Core/package tests run on Linux and Windows.
  • Web tests run on Linux and Windows; Linux installs tmux, Windows covers named-pipe mux relay tests.
  • CI permissions are reduced to contents: read.

Implementation Readout

The PR landed as several tight systems. The chronology included merges and QA passes; the final architecture is clean.

Core mechanics

Platform adapter

packages/core/src/platform.ts owns OS branching: runtime defaults, shell resolution, process-tree kill, port PID lookup, and env defaults.

Pipe protocol

pty-host.ts and pty-client.ts define framed messages for terminal data, input, resize, status, scrollback, and cooperative kill.

PTY registry

windows-pty-hosts.json lets ao stop find detached ConPTY hosts that parent-process teardown cannot reach.

Agent wrappers

setupPathWrapperWorkspace creates Windows .cjs and .cmd shims for gh and git interception.

CLI scripts

ao-doctor.ps1, ao-update.ps1, Git Bash detection, and PowerShell -File handling make repo scripts usable on Windows.

Open command

ao open now reads sessions from sm.list() and launches Windows Terminal/cmd attach or dashboard fallback.

Commit Story

All 120 commits are grouped below so the presentation can show the narrative without repeating the same Windows claim.

Full commit ledger

LOC Explanation

The numbers tell the shape of the work: runtime/process code, CLI hardening, agent plugin compatibility, docs, and test coverage.

146files
9,846additions
1,360deletions
+8,486net lines
Largest changed files

How To Start AO On Windows

This is the manual runbook validated during the May 5 Windows PR test: first the intended clean path, then the exact field notes from this machine.

New user sequence

Run these in order for your own repo

From your project root, create or replace agent-orchestrator.yaml with the flat PR #1025 format below. Replace OWNER/REPO, main, and the default agent only if your project needs different values. Do not put projects: in this local file.

repo: OWNER/REPO defaultBranch: main runtime: process agent: codex workspace: worktree agentRules: | Always run tests before pushing. Use conventional commits. Link issue numbers in commit messages. Write clear commit messages that explain WHY, not just WHAT.

PowerShell one-shot if you want to write the file from terminal:

@' repo: OWNER/REPO defaultBranch: main runtime: process agent: codex workspace: worktree agentRules: | Always run tests before pushing. Use conventional commits. Link issue numbers in commit messages. Write clear commit messages that explain WHY, not just WHAT. '@ | Set-Content -Encoding utf8 agent-orchestrator.yaml

Then run the install, build, doctor, and startup flow. On this Windows machine we use corepack pnpm so the repo's pinned pnpm version runs instead of the older global pnpm on PATH.

corepack prepare pnpm@9.15.4 --activate corepack pnpm install --frozen-lockfile corepack pnpm build corepack pnpm typecheck corepack pnpm ao doctor corepack pnpm ao start

When AO starts, use the exact dashboard URL printed in the terminal. On Windows the healthy signal includes named-pipe PTY logs and Lifecycle: supervised.

Actual way

Use the PR's flat local config and process runtime

In PR #1025, the local agent-orchestrator.yaml is behavior config only. The global registry at ~/.agent-orchestrator/config.yaml owns projects:, project IDs, paths, repo metadata, and registration. If the local file has a wrapped projects: map, the dashboard can show a degraded project warning.

repo: ComposioHQ/agent-orchestrator defaultBranch: main runtime: process agent: codex workspace: worktree agentRules: | Always run tests before pushing. Use conventional commits.

Run AO through the repo package manager. On a clean Windows setup with Corepack active and pnpm 9 on PATH, this is the normal flow:

pnpm install --frozen-lockfile pnpm build pnpm typecheck pnpm ao doctor pnpm ao status pnpm ao start

Expected startup signal: Windows mode - using named pipe relay to PTY hosts, WebSocket server listening, Orchestrator session ready, and Startup complete.

Why Corepack was used here

The repo wants pnpm 9.15.4; this machine had global pnpm 8.15.5

The root package.json declares packageManager: pnpm@9.15.4. The first install used global pnpm 8.15.5, which treated the lockfile as incompatible and rewrote pnpm-lock.yaml to an older lockfile format. That lockfile change must not be committed.

git restore pnpm-lock.yaml corepack prepare pnpm@9.15.4 --activate corepack pnpm -v corepack pnpm install --frozen-lockfile

corepack enable failed with EPERM because it tried to write shims under C:\Program Files\nodejs. But corepack pnpm still worked and ran pnpm 9.15.4 directly, so the test used:

corepack pnpm build corepack pnpm typecheck corepack pnpm ao doctor corepack pnpm ao status corepack pnpm ao start corepack pnpm ao stop

Avoid corepack ao start; that is invalid Corepack syntax. Use corepack pnpm ao start.

What happened in this test

Field notes from the Windows smoke run

StepObservedMeaning
Doctortmux not installed passed because Windows default runtime is process.Confirms the PR does not require native tmux on Windows.
Local configA wrapped projects: file caused the dashboard to show degraded project state.Use flat local config; the global registry owns project identity.
StartDirect terminal logged named-pipe relay mode, Next.js became ready, and startup completed.ConPTY/pipe dashboard path is alive on Windows.
Stale worktreesOld ao-166...ao-170 and ao-orchestrator worktrees blocked checkout.Environment residue from earlier AO sessions; remove with git worktree remove --force and git worktree prune.
Windows file handlesgit worktree remove repeatedly failed until prune cleared the broken registration.Windows file-handle cleanup is a real manual-testing hazard; stop AO/dashboard first.
Node 24better-sqlite3 native bindings were missing, so activity-events DB and FTS tests failed.Not blocking ConPTY smoke testing, but retest on Node 20 because CI uses Node 20.
Troubleshooting commands

Clean stale AO state without changing source code

Inspect stale Git worktrees:

git worktree list

Remove a stale worktree that blocks checkout:

git worktree remove --force "C:\Users\abuma\.agent-orchestrator\projects\agent-orchestrator\worktrees\ao-orchestrator" git worktree prune

Stop AO and verify process-runtime cleanup:

corepack pnpm ao stop Get-Content "$env:USERPROFILE\.agent-orchestrator\windows-pty-hosts.json" -ErrorAction SilentlyContinue git worktree list

If the dashboard still shows an old hash project, open the startup URL printed by ao start, for example /projects/agent-orchestrator_1438835b40/sessions/ao-orchestrator.

Next manual checks

After startup, test the runtime surface

corepack pnpm ao status corepack pnpm ao spawn --agent codex --prompt "Create WINDOWS_AO_TEST.md with one line saying Windows AO works." corepack pnpm ao open --browser corepack pnpm ao open all corepack pnpm ao session attach ao-orchestrator corepack pnpm ao stop

Also test from Git Bash after PowerShell passes. The important checks are: no tmux error, terminal is not blank, pipe reconnect works after dashboard refresh, long prompt input is not truncated, and ao stop leaves no stale pty-host registry entries.

Source Trail

This site is based on current branch commits plus the files below. No PPT, blog draft, PDF, DOCX, or existing deck was found in the repo.

docs/CROSS_PLATFORM.md

Developer rules, helper inventory, EPERM handling, shell/path/network checklist.

docs/ARCHITECTURE.md

Windows runtime architecture: pty-host, pipe protocol, mux relay, registry, sweep.

platforms.mdx

Platform-facing docs: Windows uses process runtime, dashboard terminal, supported CLI flows.

native-windows-support.md

Changeset release note for native Windows support and cross-platform ao open.