fix(terminal): psmux wheel scrolling, per-platform tmux upgrade advice, Windows-safe server tests

- Server tests on Windows no longer reach the user's real psmux: psmux
  ignores TMUX_TMPDIR (and 3.3.x PSMUX_DATA_DIR), so quit.test.js's Quit and
  the runner's kill-server ended every live lane pane — the pre-commit hook
  included. scripts/test-server.js now takes tmux off the suites' PATH there.
- Wheel scrolling on Windows: psmux never turns mouse tracking on for its
  client, so xterm.js never reported the wheel and a pane could not scroll.
  A Windows attach is told {type:"wheel"} and the browser sends the SGR wheel
  report itself; psmux scrolls or forwards it to the pane's app.
- tmux.upgradeAdvice(): the oldest tmux AgentHub asks for and how each
  platform upgrades (psmux 3.4.0 -> winget upgrade marlocarlo.psmux; tmux 3.7
  -> brew upgrade tmux / build the release on Linux). Used by the doctor,
  GET /api/run/tmux ({available, version, upgrade}), `agenthub doctor`, and a
  sticky toast on the Workspace page. Advice only: nothing installs tmux.

Committed with --no-verify (user's call): the pre-commit hook runs the full
backend suite, which has ~378 pre-existing failures on Windows (S3/Linux-only
tests: sudo, /proc, systemd, members). Every test touching this change passes:
pty-attach, tmux, run, plugin-doctor-tmux, TerminalView, Workspace, and the
client tests related to the changed files; tsc and the header audit are clean.
This commit is contained in:
Nguyễn Ngọc Trí Vĩ
2026-10-15 11:46:09 +07:00
parent 7a88b74016
commit cd427d218b
19 changed files with 218 additions and 45 deletions
+19 -1
View File
@@ -116,7 +116,8 @@ describe("pty-attach", () => {
it("wires PTY data to binary WS frames and WS binary frames to PTY writes", () => {
const ws = makeFakeWs();
ptyAttach.attach(ws, "agenthub-lane-1", { cols: 80, rows: 24 });
// Linux: a Windows attach also says `wheel` first (its own test below).
ptyAttach.attach(ws, "agenthub-lane-1", { cols: 80, rows: 24, platform: "linux" });
fakePty.__emitter.emit("data", "hello from claude");
assert.equal(ws.__sent.length, 1);
@@ -269,4 +270,21 @@ describe("attachBinary on Windows", () => {
}
tmux.__reset();
});
it("tells a Windows attach to send the wheel itself (psmux never tracks the mouse)", () => {
for (const [platform, told] of [
["win32", true],
["linux", false],
]) {
const ws = makeFakeWs();
ptyAttach.attach(ws, "agenthub-lane-1", { cols: 80, rows: 24, platform });
const said = ws.__sent.filter((m) => !m.binary).map((m) => JSON.parse(m.data));
assert.equal(
said.some((m) => m.type === "wheel"),
told,
platform
);
ptyAttach.__resetGroups();
}
});
});