Skip to main content
CHANGELOG

What shipped

Released versions only, generated at build time from the product Keep a Changelog file. Unreleased work is not listed here: if it is not in a version heading, it has not shipped.

Latest released v3.23.4 · · 61 releases

Currently downloadable v3.25.2-preview.1 · preview channel

Those are two different questions. This page lists versions that have been released, from the product changelog. The download page serves whatever the live preview channel points at, which moves ahead of the last released version with every build. Download the current build from /download. Channel and exact version always come from the live release manifest.

Releases 31–35 of 61

Page 7 of 13

v3.2.0

wmux CLI on your PATH, wmux.json project config, click-to-jump notifications, perf gate

Headline: every shell — inside or outside wmux — gets a wmux command with verified self-pane identity; a repo-root wmux.json turns "open this repo" into a fully arranged workspace (custom commands + declarative pane layout) behind a byte-exact trust gate; clicking a desktop toast now jumps straight to the pane that fired it; and a benchmark harness with a CI regression gate puts real numbers behind the performance story (echo p95 29.2 ms, no degradation at 8 panes). Plus cross-workspace browser-close routing, a multi-image paste fix, and Smart App Control install guidance.

Added

  • Clicking an OS toast now jumps to the pane that fired it (X2). Desktop notifications — agent turn-ends, OSC 9/777/99 terminal notifications, process-exit errors, and external wmux notify calls — carry their originating pane context. Clicking the toast restores and focuses the window, switches to the owning workspace, activates the pane and the exact surface (tab) that produced the notification, marks its unread notifications read, and clears the attention ring. Toasts from wmux notify --workspace <id> jump to that workspace; if the source terminal closed between toast and click, the click degrades to the old focus-the-window behavior.
  • wmux setup-hooks — install Claude Code hooks without the marketplace plugin. The plugin-less path to the deterministic agent-signal bridge: wmux setup-hooks installs the same 4 hook entries (Stop, SubagentStop, SessionStart, PostToolUse) directly into Claude Code's user settings (~/.claude/settings.json) and copies the bridge to a stable, update-proof location (~/.wmux/hooks/wmux-bridge.mjs) that the settings reference — never the versioned install dir, so it survives app updates. The merge is idempotent and surgical: it preserves all your existing hooks and every other settings key, and a corrupted settings.json aborts rather than clobbering your config. --status reports which events are wired, whether the copied bridge is stale (byte-compared against the bundled source), and warns if the marketplace plugin is also installed (which would double-fire signals); --remove deletes only the wmux-owned entries.
  • A1 performance benchmark harness + CI perf gate. scripts/perf-bench.mjs measures what users feel against the packaged app: input latency (key→echo and key→frame, instrumented inside the renderer so CDP transport never pollutes the numbers; at 1 pane and 8 panes), cold-start milestones (spawn → pipe ready → renderer → first PTY data), and full-process-tree RAM including the detached daemon. Each run spawns the app in an isolated data namespace (WMUX_DATA_SUFFIX), so it can run alongside a live wmux, and shuts the daemon down cleanly afterwards. scripts/perf-compare.mjs gates regressions against blessed baselines (double-condition: ratio AND absolute margin) and .github/workflows/perf.yml runs the gate on PRs and appends a trend line to bench/history.ndjson on main. First measured numbers on the dev machine (i5-13420H): echo p95 29.2 ms, key→frame p95 44.1 ms, with no measurable degradation at 8 panes — baselines are descriptive measurements, never aspirational targets. See bench/README.md.
  • Project configuration via wmux.json (X5). Drop a wmux.json at your repo root and wmux turns it into a per-project workspace: custom commands ({"id": "dev", "title": "Dev server", "command": "npm run dev"}) appear in the command palette and the sidebar's project dialog, and a declarative pane layout (nested panes with per-pane startup command, project-relative cwd, or a url for an embedded browser pane) is applied automatically when you open a fresh workspace in that repo — "open this repo → Claude Code + dev server + browser, arranged" with zero clicks. Discovery walks up from the workspace's live cwd and stops at the repo boundary. Nothing executes until you trust the file: wmux.json is checked into the repo, so the first discovery only displays — a review dialog shows every shell command verbatim, and the trust grant is bound to a hash of the exact bytes reviewed. Any later edit (e.g. a malicious PR changing a command) demotes the project to display-only until re-approved; "deny" is sticky until explicitly cleared.
  • wmux CLI on your PATH, with verified self-pane identity (X4). The installer now drops a wmux command onto the user PATH (regenerated on every update, removed on uninstall), so any shell — inside or outside wmux — can script the app: wmux send "npm test" --submit, wmux read-screen, wmux notify "Done" "Build finished", wmux open http://localhost:3000, wmux split, wmux list-workspaces --json. Run inside a wmux pane, terminal commands target the pane you typed them in — identity is resolved by walking the CLI's own process tree against the PID map (the same verified identity the MCP terminal tools use, never the spoofable/stale env hint), and the zero-spawn fast path covers the common shell-direct case. --pane <ptyId> targets another pane explicitly, --active keeps the old UI-focused-pane behavior, and notifications/browser opens route to the calling workspace automatically. The CLI client also gained the TCP-localhost fallback for Windows named-pipe ACL edge cases.

Documentation

  • Smart App Control (SAC) install guidance (#200, reported with a full diagnostic timeline by @alphabeen). On Windows 11 devices with SAC enforcing, the unsigned installer can be blocked outright — no SmartScreen dialog, no "Run anyway" — and the block can be transient (cloud reputation): the same binary may install successfully hours later with zero local changes. README (install section + FAQ) and install.ps1 now explain how to confirm SAC is the cause (Get-MpComputerStatus | Select-Object SmartAppControlState, Code Integrity Event ID 3077) and the workarounds: winget/Chocolatey, retry later, or build from source.

Changed

  • a2a.resolve.identity now returns pane-level entries (pid + ptyId + workspaceId) alongside the existing mappings — additive; existing MCP clients are unaffected.

Fixed

  • Pasting multiple images in a row no longer invalidates the earlier ones (#201). Each image paste deleted the previous wmux-paste temp file, so pasting several screenshots into Claude Code left only the most recent path readable. Temp files now survive the session (a startup sweep removes stale ones older than 24h) and get a random suffix so rapid pastes can't collide.
  • browser_close / wmux browser close can no longer tear down a browser pane the user is viewing in another workspace. browser.open was pinned to the caller's workspace in #193, but browser.close kept resolving "the browser pane" inside the UI-active workspace — an agent in workspace A issuing a close took down whatever browser the user happened to be looking at in workspace B, or got a spurious "not found" when B had none. Close now routes the same way open does: MCP resolves the calling workspace (fail-closed), the CLI uses its verified self-pane identity (with a --workspace override), and an explicit surfaceId — which is globally unique — is found across all workspaces. surface.close with an explicit id likewise no longer fails with "surface not found" when the surface lives outside the active workspace. Callers that pass no workspace at all keep the active-workspace behavior.

Contributors

  • @alphabeen — Smart App Control installation investigation (#200): a complete diagnostic timeline (registry state, Code Integrity 3077 events, the transient cloud-reputation behavior, and why v2.x was unaffected) that became the new SAC install guidance verbatim. Exemplary report — thank you!

v3.1.1

browser pane wired into the workflow, IME input self-healing

Headline: the embedded browser pane is now reachable from where you actually work — terminal URLs route smartly, sidebar port badges open localhost in one click, and browser panes restore on the page you last visited. And the field-reported "keyboard input dies until you toggle multiview" IME failure on Korean Windows now self-heals: the suspect textarea-clearing is off by default and a storm guard detects the dead-input signature and resyncs the IME automatically.

Added

  • The embedded browser pane is now wired into the terminal workflow (X3). Clicking a URL printed in a terminal routes smartly: localhost / 127.0.0.1 URLs open in the workspace's embedded browser pane (reusing the existing pane and navigating it), external URLs open in the system browser, and Ctrl/Cmd+click inverts the choice. The sidebar's listening-port badges (X1) are now clickable — one click shows http://localhost:<port> in that workspace's browser pane, un-zooming any pane that would hide it. target="_blank" links inside the browser pane now work and open in the same pane (popup windows stay blocked). Ctrl+Shift+L and the palette's "Open Browser" keep their always-create-a-new-pane behavior.
  • Browser panes restore on the page you last visited. Every navigation — toolbar, in-page links, agent-driven CDP navigations alike — is persisted per surface, so a session restore reopens each browser pane on its last URL instead of the one it was created with.

Fixed

  • Keyboard input no longer dies until the terminal is remounted (Korean/CJK IME "claim storm"). Field report on v3.0.0: typing and arrow keys stopped reaching the terminal — clicking didn't help, only forcing multiview on and off (which remounts the terminal) recovered it. Mechanism: when the Windows IME's state desyncs from xterm's hidden textarea, it claims every keydown (keyCode 229) and xterm drops all of them. Two-part fix: (1) the v3.0.0 idle IME-textarea clearing (#167 protection for field-replacing voice injectors) is now off by default — its programmatic wipe of the IME-owned textarea is the prime suspect for the desync; it remains available under Settings → Terminal for AutoGLM-style tool users. (2) A new always-on storm guard detects the claim-storm signature (consecutive 229 keydowns across distinct keys with zero composition activity) and resyncs the IME with a blur/refocus — the remount cure, automated — surfacing a toast and a console diagnostic so the trigger can be confirmed in the field.
  • Session restore no longer leaves keyboard focus on nothing. Restored terminals register for focus only after their async scrollback load, but the focus driver gave up after ~10 animation frames and the boot-time focus target never changes again — so on slower restores the app came up with DOM focus on <body> (typing went nowhere until a pane/workspace switch). Terminal registration now pushes a notification the focus driver subscribes to, one-shot, so late registrations still receive focus and later re-registrations can't steal it.
  • browser.open on an existing browser surface now actually navigates the webview. The reuse path only rewrote store state, which the mounted webview never re-reads — the MCP call reported success while the page stayed put.
  • browser.open no longer resets an unspecified partition to the default. The forced reset remounted the webview (the partition is part of its render key) and dropped the login session.

v3.1.0

UI plugin host, workspace context sidebar, terminal notifications

Headline: wmux panes and sidebars are now extensible by third-party UI plugins running in sandboxed iframes under the same permission stack as MCP plugins; the workspace sidebar shows live zero-config context (git branch, PR status, process-scoped listening ports, latest notification); and standard terminal notification escape sequences (OSC 9/777/99) are parsed into first-class events. Plus a batch of rendering and MCP-routing fixes. All features dogfood-verified on a live build.

Added

  • Workspace context sidebar (X1): live git branch, PR status, scoped listening ports, and latest notification per workspace — zero config. The sidebar now shows each workspace's git branch via an fs.watch on .git/HEAD (no polling; linked worktrees detected and marked), the current branch's PR number/state/CI checks from a 5-minute gh cache (silently absent when gh isn't installed; click opens the PR), listening TCP ports matched against each pane's own process tree (previously the port list was machine-global — every workspace showed the same first-20 ports), and a one-line summary of the latest terminal notification. All context flows through the existing workspace.metadata.changed event, so MCP clients and plugins see the same data the sidebar does.
  • UI plugin host: sandboxed sidebar panels, status-bar widgets, pane badges, and palette commands. Drop a bundle in ~/.wmux/plugins/<name>/ with a manifest.json and wmux hosts its UI in a sandboxed iframe (opaque origin, no network — the postMessage bridge is the only channel out). Plugin RPCs dispatch through the same permission stack as MCP plugins (trust DB, capability enforcement, approval prompts), with new capabilities ui.sidebar / ui.statusbar / ui.pane-decoration / ui.commands / notifications.read. Includes a reference plugin under examples/plugins/hello-panel. Verified end-to-end on a live build (approval flow → mount → bridge RPC → pane badge).
  • Terminal desktop notifications (OSC 9 / OSC 777 / OSC 99) are now parsed and surfaced as events. Programs that emit the standard notification escape sequences — iTerm2-style OSC 9, urxvt OSC 777;notify, and the kitty OSC 99 desktop-notification protocol (including chunked and base64 payloads) — produce a new notification.received event on the event bus (pollable via wmux_events_poll) in both daemon and local PTY modes. ConEmu's OSC 9 progress subcommands no longer trigger spurious toasts, and notification text is sanitized and length-capped. Groundwork for the attention-ring / toast-routing notification system.

Fixed

  • surface_list / pane.list no longer report a stale, workspace-wide cwd for every surface. Each surface's own live working directory (OSC 7 / prompt scrape) is now authoritative; the workspace-level metadata cwd — which is just whichever active surface last changed directory — is only a fallback. Previously that single path was stamped onto every surface in the workspace.
  • Panes no longer turn into X-boxes or blank out after splitting / tab-switching through many content-heavy panes in a long session. xterm's WebglAddon.dispose() detaches the renderer but never frees the underlying WebGL2 context, so split/tab churn accumulated zombie contexts past Chromium's ~16-context cap, force-evicting a live pane's context. Every addon teardown now force-releases its GL context immediately via WEBGL_lose_context.loseContext(). (#199, resolves #197)
  • Non-selected panes no longer render garbled or blank glyphs when switching pane selection. After long use with content-heavy panes, switching the selected pane could corrupt the other panes. xterm's WebGL addon shares one glyph texture atlas across every same-config terminal (CharAtlasCache); the focus/visible defensive repaint called clearTextureAtlas(), which empties that shared atlas and rebuilds only the newly-focused pane — the siblings kept stale per-cell texture coordinates and sampled an emptied/repositioned atlas. The repaint now does a full-range refresh() only and never touches the shared atlas; the earlier "garbled glyphs after a burst" case (#166) was already covered by the burst-path refresh, which never cleared the atlas. (#196, resolves #191)
  • MCP workspace-identity resolution no longer blocks the event loop. The identity PID-tree walk used a synchronous execFileSync per ancestor; it now walks the tree with async execFile, preserving the resolution result and the source invariant. (#195, resolves #194)
  • Playwright auto-open is pinned to the calling session's workspace. browser.open without an explicit workspaceId opened the browser in whichever workspace happened to be active; the engine now resolves the calling session's workspace and fails closed instead of falling back to the active one. (#193, resolves #190)
  • Esc now reaches the terminal under a CJK IME. While a CJK IME composition was active (keyCode 229), xterm dropped the Esc keystroke; wmux now matches the physical key code and injects Esc directly, the same class of fix as the Ctrl+J newline issue. (#189)

Contributors

  • @zer0ken — WebGL context-leak fix (#199), shared-atlas pane-corruption fix (#196), non-blocking MCP identity walk (#195), and Playwright workspace pinning (#193).
  • @snowyukitty — CJK IME Esc fix (#189).

v3.0.0

external-tooling foundation, PowerShell 7 by default, terminal UX, cross-workspace hardening

Milestone release. Headline: a reference plugin and workflow-friendly APIs that make wmux a foundation external tools build on, PowerShell 7 chosen as the default shell wherever it's installed (including Store builds), a batch of terminal UX (font zoom, configurable start directory, split CWD inheritance), and the close of the cross-workspace terminal read/write isolation gap. No breaking changes — this is a milestone version bump, not a wire-format or config break; existing sessions, profiles, and configs carry over untouched. All dogfood-verified on a live build before tagging.

Added

  • Terminal starting directory + split CWD inheritance. New panes can inherit the active pane's working directory on split, with a global/per-profile setting for the default startup directory and a toggle for inheritance — a priority chain that leaves the main process and daemon untouched. (#177, resolves #173 / #174 / #175)
  • Keyboard zoom for terminal font size. Ctrl+= / Ctrl+- / Ctrl+0 grow, shrink, and reset the terminal font, resolved from the physical key code so it's IME-safe, clamped to 12–24px. (#172, resolves #171)
  • Rename a workspace from the right-click menu. A Rename entry on the workspace context menu, reusing the existing inline-rename flow (same as double-click). (#184)
  • Substrate reference plugin and restructured docs. A reference MCP plugin, Diátaxis-organized documentation, a drift fix, API codegen, and a performance characterization pass — closing the external-tooling API request and giving integrators a worked example to build against. (#165, closes #15)

Changed

  • PowerShell 7 is preferred over Windows PowerShell 5.1 as the default shell wherever it's installed — including Microsoft Store builds exposed only through the WindowsApps App Execution Alias. The alias is both detected (via reparse-point resolution; existsSync alone misses the 85-byte symlink stub) and actually launchable (the stub can't be spawned directly by node-pty, so wmux resolves it to the real package target). Shell resolution is now single-sourced between the main process and the daemon, so the two can't drift. (#178, #180, #181, #186; resolves #176, #179, #183, #185)

Security

  • Cross-workspace terminal read/write via spoofable workspace identity is closed. A token-holding external MCP client could spoof WMUX_WORKSPACE_ID to a victim workspace and, naming that workspace's ptyId, read or write its terminal — the main-side ownership assert only verified that the ptyId belonged to the (attacker-supplied) workspaceId, not that the caller was entitled to that workspace. Part 1 gave input.readScreen the assertWorkspaceOwnsPty check its sibling handlers already had (it was the one terminal-IO handler that skipped it). Part 2 removed the spoofable identity the assert trusts: terminal tools (terminal_read / terminal_read_events / terminal_send / terminal_send_key) now resolve their workspace from verified PID-mapped identity only, never the env hint — a genuine external caller gets a dedicated claimed workspace, an explicit foreign ptyId fails closed, and a boot-reconcile grace keeps a first-party caller from being misclassified during a daemon respawn. (#164 + #188, resolves #163)

Fixed

  • Prefix-mode Toggle Zoom now actually zooms. The tmux-style prefix Toggle Zoom toggled internal state but no rendering code read it, so the keystroke was consumed with no visible change. The zoomed pane is now rendered full-bleed (siblings hidden) and exactly restored on toggle-off, with split/close coherence and a ZOOM badge. (#187, resolves #182)
  • Garbled glyphs clear without a manual resize. Panes could render corrupted glyphs until a border drag forced a repaint; wmux now repaints defensively. (#168, resolves #166)
  • IME input no longer wipes the typed line. xterm's hidden IME textarea is cleared when idle, so a voice/IME input method (e.g. AutoGLM) no longer discards the already-typed line. (#170, resolves #167)
  • Sidebar hide/expand controls mirror correctly when docked on the right. (#160)
  • The @electron/asar header cache is dropped after the postPackage repack, so the packaged asar can't be stale. (#161)
  • Restored the bench B3 drop-tracking variables lost in an earlier refactor and refreshed the perf numbers. (#169)

Contributors

Thanks to the external contributors and reporters in this release:

v2.18.0

terminal fonts, color customization, settings polish

Headline: pick any installed terminal font (and ship the recommended ones so they work everywhere), a point-and-style color inspect mode for theming, and a settings UI polish pass. All dogfood-verified on a live build before tagging.

Added

  • Pick any installed terminal font. The font setting is now a combobox over every font installed on the machine — click to browse the full list (each option rendered in its own face), type to filter, with a live Latin+Hangul preview so you can confirm a mixed-mono font has fixed-width CJK glyphs before committing. A separate "custom" entry mode takes any family name by hand for not-yet-installed fonts. Fixes a silent powershell.exe ENOENT (a bare-name spawn that failed to resolve under Electron) which had made the installed-font enumeration return nothing — so the feature never actually worked before this. (#155, resolves #147)
  • Bundled terminal fonts. JetBrains Mono, Fira Code, and JetBrainsMonoHangul now ship with the app (alongside the existing Cascadia Code/Mono), so the recommended fonts — including fixed-width Hangul — work on every machine without a manual install. All under the SIL Open Font License 1.1; license texts are bundled and listed in THIRD_PARTY_NOTICES. (#158)
  • Point-and-style color inspect mode. Click a chrome region to recolor it by theme token, with contrast-safety checks so a custom palette stays readable. (#156)

Changed

  • Settings tabs use SVG line icons in place of the dated unicode glyphs. (#148)
  • Settings design-system pass — shared primitives, accessibility fixes, and tab-label i18n. (#150)
  • i18n: Claude integration and first-run setup tab bodies are now translated across 21 locales (#152); the color customization strings across 22 locales (#157).

Fixed

  • Ctrl+J inserts a newline even with a CJK IME active. Inside in-pane TUIs (codex, Claude Code) Ctrl+J intermittently failed to add a newline — it worked with the IME off and broke with a Chinese / Japanese / Korean IME on. The byte pipeline below xterm is transparent to \n; the keystroke was lost at xterm's keyboard layer, which derives Ctrl+<letter> from the deprecated KeyboardEvent.keyCode. With the IME enabled the keydown reports keyCode === 229 ("Process") with key !== 'j', so xterm's 65–90 branch never matched and emitted nothing. wmux now resolves the newline keys (Shift+Enter, Ctrl+J) from the physical event.code and writes the byte itself — the same IME-safe approach already used for the split shortcuts — so Ctrl+J sends \n regardless of IME state. It defers while an IME composition is active (so a preedit is never split) and when the user has bound Ctrl+J to a custom keybinding. xterm 6 has no kitty/modifyOtherKeys path, so the emitted byte matches its legacy output when no IME is active. (#153)
  • --squirrel-firstrun no longer becomes a never-quitting zombie. On Windows the Squirrel first-run hook was misclassified, so the process neither initialized nor quit, leaving an idle GPU/network process behind after install. (#154)

Contributors

Thanks to the external contributors in this release:

Bundled fonts under the SIL Open Font License 1.1: JetBrains Mono (The JetBrains Mono Project Authors), Fira Code (The Fira Code Project Authors), and JetBrainsMonoHangul (Janghyub Seo).