Skip to content

1.10.0 — remote control and --tmux on Windows, and a session list that folds away - #30

Merged
treeleaves30760 merged 4 commits into
mainfrom
feat/windows-remote-tmux
Sep 18, 2026
Merged

treeleaves30760 merged 4 commits into
mainfrom
feat/windows-remote-tmux

Conversation

@treeleaves30760

Copy link
Copy Markdown
Owner

--tmux could not be used on Windows. It turned out that nothing about remote control could: --share, the hub, sessions and attach were all switched off there in 7890d6d, after Windows CI hung on the hub tests. This lifts that, makes --tmux work on Windows through the native port of tmux, and lets the page's session list fold away so the terminal - and under --tmux, the agent - gets the width.

Why remote control was off, and what it actually was

The note on the gate said the hub "does not come up and does not go away" on Windows. On a real Windows machine it came up in a second and stopped cleanly. What hung was the caller: Rust's Command::spawn hands a child every inheritable handle, and the caller's own stdout is one of them whenever something captured it, so assert_cmd - or any script reading alc hub start - waited for an end of file that came only when the hub died. Reproduced with a pipe-capturing caller, fixed, and the formerly unix-only hub tests now run on Windows.

CREATE_NO_WINDOW replaces DETACHED_PROCESS while at it: with no console at all, every console program the hub runs (a tmux probe, twice a second) would open a window of its own.

What a Windows pseudo-console needed

Each of these stopped a session outright, and each was measured:

What happened Fix
Cursor query portable-pty opens ConPTY with INHERIT_CURSOR; conhost prints ESC[6n and draws nothing until answered. A --tmux mirror never attached. The hub answers it once and keeps conhost's opening requests (?9001h, ?1004h) from viewers - under --tmux the page's focus reports would have been typed into the agent.
End of session ConPTY output never ends when the process does, only when the console is closed. Sessions stayed "running" after the agent exited. A watcher closes it when the child exits.
Local console Escape sequences printed literally, arrows did nothing, Ctrl+Z read as end of file (keyboard dead), half a CJK character failed the write on code page 950, Ctrl+Break left raw mode behind. VT modes asked for and restored (also on Ctrl+Break / close), the keyboard read with ReadConsoleW, whole characters written.

--tmux on Windows

tmux has a native Windows port, arndawg/tmux-windows - real tmux 3.6 over ConPTY and named pipes. Measured, it behaves exactly as alc needs for the option sequence, remain-on-exit and the pane's exit status, send-keys -H (the prefix opens no prompt), ignore-size, and kill-server. It also:

  • refuses to start a pane given -c, whatever the path;
  • joins the pane's argv with spaces and splits it again;
  • hands the pane TMUX, which -e TMUX= cannot undo;
  • passes argv, environment and cwd through the ANSI code page: non-ASCII arguments and values arrive mangled, a non-ASCII cwd silently becomes the home directory, and a program under a non-ASCII path does not start.

So on Windows the pane runs alc __tmux-pane <port> <token>, all ASCII. It collects the exact launch from the hub over the loopback control port with a one-time 256-bit token, removes TMUX, runs the agent in a job tied to itself, and exits with its status. The provider key never enters tmux's environment on Windows. If alc itself lives under a non-ASCII path, its 8.3 short path is used.

psmux also installs a tmux.exe and reports itself as tmux 3.3 on the first line of -V, but cannot run the set-option … ; new-session sequence a session is created with. find recognises it and looks further down PATH, so the two can coexist. alc doctor says whether the native port was found.

Not Windows-only

  • Detach did nothing until the agent next drew: the relay stayed blocked reading a connection that nothing closed. The client now half-closes on the detach keys, and the hub lets a departed terminal go and half-closes its end when the session ends - so the terminal also gets its shell back without a key press.
  • A reattached terminal could start blank: the attach reply was read through a BufReader, which took the screen the hub sent straight after it.
  • The control channel caps and times out the unauthenticated first line; on Windows it is a loopback port any local account can reach.

The session list folds away

On a wide screen with a session open, a button beside Back hides the list and the terminal slides over. A --tmux session gets the columns (239 → 284 in Chrome at 1400px); a plain one is drawn larger. It refits once where it comes to rest, slides only when pressed and never under reduced motion, makes the hidden list inert immediately, remembers the choice per browser, and has no keyboard shortcut - every key belongs to the terminal. The docs site's sidebar is now collapsible too.

Verified

  • Windows: cargo test --all-targets 574 + 67, including 7 new live tests against tmux-windows (sizing vote, exit status, kill-server, prefix key, and the launcher end to end with non-ASCII args/cwd/env).
  • A 33-check end-to-end run through a real ConPTY (pywinpty): --share --tmux and plain --share - typing, arrows, Ctrl+Z, detach, reattach with the screen intact, exit statuses, alc kill, no processes left behind.
  • In Chrome against a live Windows hub: the page sets the tmux window size, keystrokes reach the agent, the fold resizes the agent and survives a reload.
  • cargo clippy -D warnings for Windows and for x86_64-unknown-linux-gnu; node --test web/*.test.js 64/64; the docs site builds in both locales with broken links and anchors fatal.
  • An adversarial review of the Rust diff: eight confirmed defects, all fixed above.

Not verified here: the unix runtime tests (Linux was type-checked, not run - CI covers it), and typing by hand in Windows Terminal itself.

🤖 Generated with Claude Code

treeleaves30760 and others added 4 commits September 19, 2026 01:10
Remote control was switched off on Windows because the hub "did not come
up and did not go away" on CI. Measured on a real Windows machine, the hub
came up in a second and stopped cleanly. What hung was whatever started
it: `Command::spawn` hands the child every inheritable handle, and the
caller's own stdout was one of them, so a test harness or a script reading
`alc hub start` waited for an end of file that only came when the hub died.
The hub is now started with the caller's std handles made non-inheritable
for the length of the spawn, and with CREATE_NO_WINDOW rather than
DETACHED_PROCESS, so the console programs it runs (a tmux probe, twice a
second) share one hidden console instead of each opening a window. The
gate is gone, and the hub tests that were unix-only run on Windows too.

Three things a Windows pseudo-console does that a pty does not, each of
which stopped a session outright:

- portable-pty opens it with PSEUDOCONSOLE_INHERIT_CURSOR, so conhost asks
  for the cursor position and draws nothing until it hears. The hub now
  answers that one question itself, and keeps conhost's opening requests
  for win32 input and focus reports away from viewers - under --tmux the
  page's focus reports would otherwise have been typed into the agent.
- Its output never ends when the process in it does; only closing it ends
  it. A watcher now closes it when the child exits, so a session ends when
  its agent does.
- The local terminal needs virtual-terminal processing and input asked for
  explicitly, reads Ctrl+Z as end of file through std (so the keyboard is
  read with ReadConsoleW), refuses to write half a character on a
  non-UTF-8 code page (so whole characters are written), and is put back
  on Ctrl+Break or when its window closes.

--tmux drives the native Windows port of tmux (arndawg.tmux-windows).
It is real tmux and behaves the same for everything alc relies on, but it
cannot start a pane given -c, re-splits the command line on spaces, hands
the pane TMUX, and passes arguments, environment and working directory
through the ANSI code page. So on Windows the pane runs `alc __tmux-pane
<port> <token>`, which collects the exact launch from the hub over the
loopback control port with a one-time token and runs the agent itself.
Non-ASCII paths and values work, and the provider key never enters tmux's
environment. psmux, which also installs a tmux.exe, is recognised and
looked past; `alc doctor` says whether the native port was found.

Found on the way, and not Windows-only:

- Detaching (ctrl-\ then d) did nothing until the agent next drew: the
  relay was still blocked reading a connection nothing closed. The client
  now half-closes it, and the hub lets a departed terminal go and closes
  its end when the session ends, so the terminal is not left waiting for a
  key press either.
- A reattached terminal could start blank: the attach reply was read
  through a buffer that also took the screen sent straight after it.
- The control channel now caps and times out the request it reads before
  checking the secret, since on Windows it is a loopback port any local
  account can reach.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
On a wide screen with a session open, the list sat beside the terminal at
320px whether it was wanted or not. A button beside Back now folds it
away, and the terminal slides over to take the width. For a --tmux session
that width is the agent's - the page owns its size - so it gets the extra
columns (measured: 239 to 284); a plain session's screen is drawn larger.

The terminal is refitted once, where it comes to rest, rather than
partway through the slide, so tmux is not told a width it keeps for a
fifth of a second. The slide plays only in answer to the button, never
when a folded page is widened or a session is attached, and not at all
under reduced motion. The folded list is inert from the moment of the
press, so a Tab straight afterwards cannot land on a card that is about to
vanish. The choice is remembered per browser; with storage blocked the
page still works, and reading `sessionStorage` at boot no longer takes the
page down there either. There is no keyboard shortcut: every key on this
page belongs to the terminal, and ctrl-b is tmux's prefix.

The panes' landmark names are now translated too, since the new button
points at one of them by name.

The documentation site's left sidebar can be collapsed as well.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Both READMEs and both locales of the remote-control page said Windows
refused `--share` and the hub. They now say it works, and the --tmux
section says what Windows needs: `winget install arndawg.tmux-windows`,
that psmux is looked past rather than used, that non-ASCII paths work and
the provider key stays out of tmux, and that a session stopped there ends
without an exit status because Windows has no hangup signal. The
troubleshooting page gains the two refusals a Windows user can meet.

The page's description mentions the button that folds the session list
away.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Remote control on Windows, --tmux there too, and a session list that
folds away. A feature, so a minor release.

On Windows, `alc <agent> --share`, the hub, `alc sessions`, `alc attach`
and `alc kill` now work instead of refusing. What kept them off turned out
to be the hub holding open the output of whatever started it, which made
CI wait for ever; the hub itself was fine. Getting a session to actually
run under a Windows pseudo-console took three more fixes - conhost waits
for an answer to its cursor query before drawing anything, never reports
the end of a session on its own, and the local console needs asking for
the modes a unix terminal has in raw mode.

`--tmux` on Windows uses the native port of tmux: `winget install
arndawg.tmux-windows`, then a new terminal. The pane runs a small launcher
that collects the agent's launch from the hub, because the port mangles
anything that is not ASCII on the way to a pane; non-ASCII folders and
values work, and the provider key never enters tmux there.

Everywhere: detaching lets go straight away rather than when the agent
next draws, a reattached terminal gets its screen back, and the terminal
returns to its shell when the agent exits without waiting for a key. On
the page, a button beside Back folds the session list away, and a --tmux
session gets those columns.

Nothing to do when upgrading. On Windows, `alc doctor` says whether
--tmux has what it needs. A hub still running the previous version is
named by `alc update`; stop it with `alc hub stop` when its sessions are
done.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
@treeleaves30760
treeleaves30760 merged commit a81e5c5 into main Sep 18, 2026
7 checks passed
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.

1 participant