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 26–30 of 61

Page 6 of 13

v3.6.0

A reply finds the exact agent that asked

Headline: same-workspace agents now reply to the exact pane that asked. A task's reply returns to its originating pane instead of the workspace's active one, same-workspace history finally tells the two agents apart (sender vs receiver, per pane), and a status update is restricted to the addressed pane — completing the pane-level multi-agent mesh that #239 and #242 began. Plus a fix for the terminal+browser split that blanked its unfocused side.

Added

  • A2A symmetric reply — a reply returns to the exact pane that sent the task, and same-workspace history is told apart per pane (S-C2, #248). Follow-up to same-workspace agent messaging (#239). The task address model was asymmetric: to carried a pane anchor (#235) but from did not — so a reply had no pane to return to (it fell back to the workspace's active pane, or was suppressed same-workspace), and same-workspace history role collapsed to user for both parties, making the two panes' messages indistinguishable. The hardening in #242 already captured and validated the sender's pane id on the send path, then discarded it; persisting it into metadata.from opens three things with no new trust surface. (1) Symmetric reply pinning — a reply destined for the original sender now returns to that exact pane instead of the active-pane fallback, so a sender workspace running more than one agent gets the reply on the right pane (fail-closed if that pane has since closed — never a wrong-agent paste). (2) Per-pane history role — the role is computed from the caller's verified pane (user for the sender pane, agent for the receiver pane) instead of collapsing same-workspace. (3) Pane-granular status authz — a status update (a2a_task_update) on a pane-addressed task is restricted to the addressed receiver pane, not any pane in its workspace. A same-workspace reply is delivered as a one-line nudge to the addressed sibling — never a full-body paste into a live agent's prompt — and is suppressed entirely when it can't be proven a non-self target, so the #239 self-loop guard is preserved. The headless execute:true worker (which carries no sender pane id) is never locked out: an absent caller pane always falls back to workspace-level authz. Cross-workspace delivery and role are unchanged.

Fixed

  • Terminal + browser split no longer blanks the unfocused side (#247). A pane holding both a terminal and a browser surface renders them in a side-by-side split, but each surface gated its visibility on the pane's single active-surface id — so focusing one side hid the other (display:none) and the unfocused pane went blank, toggling as you switched. Visibility is now decoupled from focus: both sides stay rendered, and the active-surface id only drives keyboard focus.

v3.5.1

Fixed

  • surface_list/pane_list caller-scoping hardening (#245). An omitted-workspace surface_list/pane_list now revalidates a stale cached workspace id after a re-mint (daemon respawn / session restore) and prefers a confirmed-external caller's pinned workspace over the UI-active fallback — so a fail-soft read reports the caller's own workspace instead of an empty list or whatever the user has focused. Follow-up to the codex review on #242 (#243).

v3.5.0

Multi-agent workspaces that talk to each other

Headline: a workspace full of agents that can finally coordinate. Same-workspace agents now message each other directly (#239), every pane is individually addressable (#235), and the identity layer is hardened (#242) so a message never loops into the wrong pane or silently routes to a duplicate-named workspace. The A2A task inbox moved onto the EventBus so cross-agent delivery no longer corrupts a live terminal (#232), and Fleet View gained a unified approval inbox to clear every blocked agent from one list (#234).

Added

  • Same-workspace agent-to-agent messaging (#239). Two agent panes in the same workspace can now message each other with a2a_task_send — previously hard-rejected as "cannot send to yourself". Addressed by pane/surface id; a true self-send (your own pane) and an ambiguous no-address send are still refused, and the cross-workspace fail-closed boundary is unchanged. The data-suffix that isolates a sandbox instance now propagates to child PTYs so an isolated instance never leaks onto the production pipe.
  • Multi-agent identity & addressing hardening (#242). Closes the adjacent bug cluster that made a multi-agent workspace fragile. terminal_send/terminal_send_key now refuse an agent's own omitted-ptyId call instead of looping the paste into its own (or a non-deterministic sibling) pane. a2a_whoami answers per-pane — which agent am I, not the workspace's single aggregate label — so siblings are told apart. A duplicate workspace name is refused with both ids instead of silently routing to whichever came first. surface_list/pane_list report the caller's own workspace. A rejected A2A task transition explains the allowed next states. And a mis-propagated WMUX_DATA_SUFFIX fails loud instead of silently booting an isolated instance onto production data.
  • Pane-level A2A identity & addressing, plus multi-target MCP registration (#235). A workspace running several agents exposes each pane as an individually addressable A2A target.
  • A2A task inbox on the EventBus — pollable cross-agent delivery that no longer corrupts a live terminal (S-C2 ②). When one workspace's agent hands a task to another, the task is now teed onto the shared event ring, so the receiving agent can discover it by polling wmux_events_poll instead of having the message force-pasted into its terminal (which used to corrupt a running TUI's input box). The sender gets the status receipt — created → updated → cancelled — the same way. Delivery is strictly dual-party: only the two workspaces involved in a task ever see its events; a third workspace, and any workspace-less poll, see nothing. A receiver that is already running a live agent now gets a one-line nudge (a pointer to run a2a_task_query) instead of the full message body, so its prompt is never flooded — a receiver with no live agent still gets the full paste, so nothing regresses for peers that don't poll.
  • a2a_discover liveness hint (③). Peers returned by a2a_discover now carry an advisory live/idle signal so an orchestrator can prefer an agent that is actually running. Advisory only — it never gates delivery.
  • Unified approval inbox in Fleet View — clear every blocked agent's out-of-band prompt from one keyboard-driven list (S-C2). Fleet View's stubbed "Approvals" tab (Ctrl+Shift+A, then the Approvals tab) is now a single inbox of every approval currently holding the fleet hostage, each resolved through its own real path: MCP plugin permission prompts (an unconfirmed plugin requesting capabilities under enforce mode — several can stack at once, keyed distinctly, each row showing the declared capabilities with a per-row risk badge), and the A2A execute gate (a remote agent asking to spawn a bypassPermissions Claude CLI in your workspace, shown with its live 30-second auto-deny countdown and the sender→receiver context). Arrow to a row and press Enter to approve, Backspace/Delete to deny — except a row carrying a critical capability (e.g. terminal-content), which Enter will never grant: those require clicking the explicit Approve button, so scrolling a dense list can't blind-grant a dangerous permission. It's the same surface the old approval modal drove, kept in lock-step: resolving a prompt in the inbox or the modal clears it in the other, and a removal signal retires the row no matter how it was answered — through the old modal, by a coalesced sibling, or by a plugin disconnecting — so there are no phantom rows. While the Approvals tab is open it is the single surface (the modal is suppressed underneath it). Only the two out-of-band-resolvable sources appear here; an approval that can only be answered by typing into the pane stays jump-only rather than growing a dead Approve button.
  • Live output tail on Fleet View terminal cards. Each terminal card now shows its pane's last ~3 output lines, so you can read what an agent is actually doing — and triage which blocked one to jump to first — without leaving the cockpit. It's a pure renderer derivation off state the store already holds (no new daemon traffic), and it works for background panes too — the ones rendered off-screen, which was the subtle part: a card for a pane you've never had on screen still shows its live tail.
  • pane.split honors an explicit workspaceId (#238). A multi-agent orchestrator can split a specific (non-active) workspace's pane, with the new pane eagerly spawning its PTY rather than waiting to be focused.

Fixed

  • Right-click paste yields to the app when it owns the mouse (#241). A terminal app that has taken over the mouse (e.g. a TUI) now receives the right-click instead of having wmux intercept it for paste.

v3.4.0

Fleet View, and Claude conversations that survive a reboot

Headline: two ways to lose less time on a multi-agent day. Fleet View is the cockpit — every agent across every workspace on one screen, the blocked ones floated to the top, one click to jump to where you are needed. And X6 resume closes the loop on reboot survival: a Claude pane no longer just comes back as a shell, it comes back offering to resume the exact conversation it was running — on every pane, not just the one you were watching, with the permission mode you had set. Plus an agent toolbar, and fixes for the Windows taskbar icon and the PowerShell 5.1 prompt hook. Thanks to @matdac6 (#228, #229) and @snowyukitty (#227).

Added

  • Fleet View — every agent across every workspace on one screen (S-C1). Press Ctrl+Shift+A (or run "Open Fleet View" from the command palette) and the whole fleet snaps into one full-screen cockpit: a card for every pane across every workspace, sorted so the agents that want you float to the top. An agent paused mid-turn on a confirmation prompt — awaiting_input, the unattended-loop money state — sorts first, gets a yellow outline and a "needs your input" affordance, and a header chip tells you how many are waiting on you ("2 need you"). Idle terminals sink to the bottom and dim. Click a card — or arrow to it and press Enter — and you are there: it switches workspace, pane, and surface in one step and lands focus exactly where the agent is, reusing the same hardened jump the OS-toast notifications use (zoom coherence included). This is the screen you keep open while loops run unattended: walk away from six agents, glance once, jump straight to the blocked one.
  • Built as a pure derivation, not a new subsystem. The grid reads state the renderer already holds — every workspace's full pane tree lives in the store — so there is no new daemon round-trip and no second copy of the truth to go stale; it reflects live agent status the moment the daemon detects it. Status resolves per-PTY first and scans all of a pane's tabs, so an agent waiting for you in a background tab is never silently shown as idle. The overlay traps keyboard focus (no stray keystrokes leak into the terminal underneath it) and is fully keyboard- and screen-reader-navigable (role="dialog", roving role="option" cards). Output preview is status + workspace + path for now; a live output tail and the unified A2A + MCP approval inbox (the stubbed "Approvals" tab) are next.
  • claude --resume <id> after a reboot — the exact conversation, on every pane (X6 ③). The resume pill now restarts the exact Claude session it was bound to (claude --resume <session-id>), with the permission mode you had set (so a --dangerously-skip-permissions workflow survives the reboot), instead of the cwd-relative --continue that could resume the wrong conversation when several panes share a directory. The binding — the pane's Claude conversation id, captured live from the hook — is persisted on the daemon session record and survives a hard SIGKILL. Crucially it works for every pane that ran Claude, not just the one whose startup banner the daemon happened to catch live: a captured hook now also lights the pill (so a pane whose banner was missed still offers a resume), each pane is attributed by its own daemon session id (so two panes in the same directory each resume their own conversation, never each other's), and a capture that couldn't reach the daemon at the moment it fired is spooled to disk and reconciled on the next boot. A purged transcript or a moved working directory degrades safely to --continue rather than a dead --resume.
  • A supervised agent pane resumes its conversation on restart and reboot (X6 ①). When the daemon's pane supervisor (X8) re-creates a declared agent pane after a crash, a daemon restart, or a full reboot, it now relaunches the agent in resume form so the conversation continues where it left off, rather than starting a fresh agent in the same pane. The original launch command stays on the record; only the replay is rewritten, and the conversation binding is carried onto the recreated pane so a second crash before the next hook still resumes the exact session.
  • Agent toolbar — Attach, File explorer, Snippets, Rich Input, New (#228, thanks @matdac6). A toolbar above the terminal with one-click access to attaching context, a file explorer, snippets, a rich-input composer, and opening a new pane.

Fixed

  • The "Resume Claude" pill now survives a real reboot — even right after you start an agent. A pane where you'd just typed claude could come back from an OS reboot with no resume pill. The daemon persisted the detected-agent marker (lastDetectedAgent) on a 30s debounce, and a real reboot is a hard SIGKILL — no graceful flush runs — so a reboot inside that window dropped the marker and recovery had nothing to offer. The single idle agent pane, exactly the reboot-survival headline case, was the one most likely to hit it. Agent detection now persists immediately (saveImmediate), bounded to one write per agent transition by the existing slug guard. The same gap affected live working-directory changes, and was strictly worse: the session:cwd handler persisted nothing, so a reboot could restore a pane to a stale directory and make the cwd-scoped claude --continue resume the wrong conversation. Working directory now persists immediately on an actual cd (guarded so a per-prompt OSC 7 re-report doesn't amplify writes). The previous offer dogfood seeded the marker straight into the snapshot, bypassing the detect→persist path entirely, which is why the race went unseen; a new kill-real dogfood drives real agent detection and then SIGKILLs the daemon inside the window to prove the fix end to end. A follow-on GUI dogfood then surfaced a second, independent cause on the renderer: even with the marker persisted and delivered to the renderer, a recovered pane's xterm focus-tracking report (CSI I / CSI O) arrives through terminal.onData on mount and was mistaken for the user typing, so clearResumeHint retracted the pill the instant it hydrated — meaning the pill had effectively never rendered after a reboot at all. Focus reports are now excluded from the retract path (real keys, pastes, and IME commits still retract as intended). Both fixes are required for the pill to actually appear.
  • The prompt hook now works on Windows PowerShell 5.1 (#227, thanks @snowyukitty). The OSC 7 / 7727 sequences that drive working-directory tracking and the prompt markers are now emitted with [char]27, which PowerShell 5.1 passes through correctly — previously the escape was mangled on 5.1, so the hook silently did nothing and cwd/branch tracking never updated on that shell.
  • The Windows taskbar icon is back (#229, thanks @matdac6). The app icon had stopped rendering in the taskbar; icon.ico is now re-encoded with BMP frames so Windows draws it again.

v3.3.0

supervised agent panes, 74% faster cold start, and a lighter idle footprint

Headline: a wmux.json pane can now declare a restart policy and the daemon supervises it like an init system — auto-restarted with backoff across process exits, daemon restarts, and full reboots, with a runaway guard so a crash-loop burns backoff instead of tokens (X8). Cold start is 74% faster on the dev machine (5570 → 1176 ms; first contentful paint 5.2 → 0.65 s) after moving the auth-token ACL hardening off the boot critical path, loading the renderer in parallel with the daemon bootstrap, and adaptive readiness polling — with a new wmux doctor to diagnose a slow boot in one command. Plus a lighter idle footprint (lazy buffer allocation, visibility-gated metadata polling, pruned native prebuilds), a refined-terminal sidebar pass, and an awaiting-input signal for Claude Code's AskUserQuestion prompt. Thanks to @matdac6 for three contributions this cycle (#212, #218, #219).

Added

  • wmux now signals when Claude Code is waiting on an AskUserQuestion prompt (#212, thanks @matdac6). When Claude Code shows its multi-line boxed question UI inside a wmux pane, the pane's sidebar dot turns yellow and the awaiting-input sound fires — the same signal you already get for single-line approval prompts. Previously "awaiting input" was detected only by the regex AgentDetector, which is anchored to single-line prompts (Do you want to proceed?) and never matched the boxed AskUserQuestion layout, so a user who looked away got no cue that the agent was blocked on them. The fix is signal-based, not another regex: a PreToolUse hook scoped to the AskUserQuestion tool maps to the existing awaiting_input status (guarded on tool_name so a future broad matcher can't tunnel spurious signals). The dot clears automatically when you answer and the agent resumes. No new UI — it reuses the existing status, sound, and dot.
  • Cold-start boot-phase instrumentation (S-A). The main process now emits one cheap [boot-trace] line per boot milestone (process spawn → module eval → app-ready → plugin load → daemon bootstrap with spawn/pipe/ping sub-phases → ready end), plus a JSON summary that lands in the daily log file; the daemon exposes its own boot marks through daemon.ping. The perf bench collects both and prints a derived phase-attribution table, so a cold-start regression now points at the guilty phase instead of a single opaque number. First run of the new table immediately attributed ~70% of the measured cold start to the auth-token ACL hardening's synchronous PowerShell shell-outs (one in the main process, one in the daemon) — the optimization target for the follow-up PR. Zero telemetry: stderr and local log files only.
  • wmux doctor — one-command diagnostics (#216). A new CLI command that turns the boot-trace instrumentation into a user-facing health check: environment (version, pipes, auth token, data suffix, app-pipe reachability), daemon status over its own control pipe (pid, uptime, sessions, event-loop lag — diagnosable even when the main process is dead), the same boot-phase attribution table the perf bench prints (main + daemon-internal phases, parsed from the daily log's boot summary with a bounded tail read), an antivirus-tax hint when a cold-rescan phase exceeds 1.5 s, and today's error/warn counts for both log files. --json for scripts; exit 1 only when something actually failed. "wmux feels slow / won't start" reports can now begin with one command instead of log archaeology.
  • RAM attribution in the perf bench (#217). The bench's flat RAM number now ships with a per-category breakdown (main / renderer / gpu / utility / daemon / conhost / user shells), a --scrollback-lines A/B seed, and a WebGL-context occupancy probe — all additive, nothing gated. First verdict from the data, recorded in bench/README.md: about half the 8-pane footprint is the user's own shells, the scrollback A/B delta on near-empty terminals is ~0 (xterm's buffer is lazily populated), and the GPU process is a single fixed cost — so the planned RAM-diet code work was cancelled by measurement before any code was written.
  • Pane supervision — the daemon keeps declared panes alive as exec-style units (X8). A wmux.json pane can now declare restart: on-failure | always (with an optional restartLimit), and the daemon supervises that pane the way an init system supervises a service: when the process exits it is auto-restarted with exponential backoff, and a runaway guard halts supervision after N consecutive short-lived runs (it must be manually rearmed) so a tight crash-loop burns backoff instead of tokens. Because the supervisor is the daemon — which already survives app crashes and machine reboots — supervision is sticky: a supervised loop is restarted across daemon restarts and across a full reboot, so an unattended overnight loop comes back on its own after the machine cycles. Nothing supervises until you trust the file (same wmux.json trust gate); plain panes are unaffected.

Changed

  • Cold start: the renderer now loads in parallel with the daemon bootstrap (S-A Step 1) (#215) — measured 1436 → 1176 ms (-18%) locally, 1441 → 989 ms (-31%) on CI; first contentful paint 1.08 s → 0.65 s. Since the v2.13 first-keystroke race fix, the boot tail ran strictly serialized: wait for the daemon to spawn and connect, then start loading the renderer — stacking the two longest boot legs (~625 ms renderer, ~464 ms daemon bootstrap) back to back, with the window sitting on a blank background frame the whole time. The bootstrap is now kicked without awaiting and the renderer loads immediately, so the daemon spawn hides behind the renderer load. The race that forced the serialization (a renderer mounting mid handler-swap could mint a local-mode pty id and have its writes silently dropped — "first keystroke doesn't register" on fresh installs) is closed structurally rather than by ordering: the renderer's first ready-state query parks until the daemon-vs-local decision is final, and the pane gate keeps every terminal-create path shut until the startup reconcile completes. The one listener those defenses didn't cover (the late-reconcile trigger on daemon:connected, which previously could not fire before the renderer existed) is now gated on the pane gate, extracted, and unit-tested. Verified against the original regression scenario: 10/10 isolated cold boots with a keystroke fired the instant the terminal mounts, zero drops.
  • Cold start: adaptive daemon readiness polling (S-A C1) (#214). After spawning the daemon, the launcher polled for readiness on a fixed 200 ms interval — boot traces showed ~93–199 ms of pure poll quantization between "daemon wrote its pipe file" and "launcher noticed" on every cold start. The poll is now an immediate first check followed by a 40 ms cadence for the first 2 s, backing off to the original 200 ms for slow-machine tails; the same span now measures 6–44 ms per cold run. The zombie-pipe guard, auth-token gate, 15 s budget, and the already-running-daemon yield path are preserved, and the loop is extracted behind a dependency-injected helper with fake-timer tests (it previously had none).
  • Cold start: auth-token ACL hardening moved off the boot critical path (S-A) — measured 5570ms → 1436ms (-74%) on the dev machine, first contentful paint 5.2s → 1.1s. The boot traces attributed ~70% of cold start to the token-file ACL hardening's synchronous whoami + PowerShell shell-outs — once in the main process (PipeServer constructor, 2015ms median) and once in the daemon (3465ms, directly on the path the launcher polls). Three changes, none of which weaken the hardening guarantees:
    • Re-hardening an existing token is now deferred and fully asynchronous. The token VALUE doesn't change on re-harden, so an attacker who could exploit the brief deferred window could equally have read the file at any point of its prior on-disk lifetime under the same ACL — and the RPC surface is protected by the token value (timing-safe compare), not by the file ACL. The deferred path uses async execFile/spawn exclusively, so the multi-second shell-out can no longer stall the daemon's event loop either. Verified to converge to the same owner-only DACL (including removal of explicit Everyone ACEs) by the extended scripts/issue-124-acl-dynamic.mjs harness.
    • Freshly created token files are hardened via icacls (~120ms) instead of PowerShell (~1-2s). The #124 objection to icacls — it cannot remove a pre-existing explicit broad ACE — is unreachable on a file that did not exist before the write (it carries only inherited ACEs, which /inheritance:r strips). Overwrites of an existing file (token rotation, empty-file repair) keep the PowerShell-first DACL rebuild. Fail-closed semantics unchanged: if both primitives fail, the un-hardenable token is deleted and the write throws.
    • McpRegistrar no longer rewrites an identical token file at the end of the ready handler (the PipeServer constructor had just written the same value through the same secure path).
  • Lighter idle/background footprint — RAM, CPU, and package size (#219, thanks @matdac6). Three independent reductions, all transparent to consumers: the daemon's per-session RingBuffer now allocates 64 KB up front and doubles toward the configured ceiling (default 8 MB) on demand instead of committing the full ceiling per session — idle/quiet sessions hold ~64 KB, chatty ones still grow to the ceiling with no scrollback lost; the 5 s per-PTY metadata poll (git / gh / /proc work that only feeds cosmetic UI) is now gated on shouldPollMetadata() and skipped while the window is destroyed, loading, hidden, or minimized, with the next visible tick refreshing within ≤5 s so staleness stays bounded; and postPackage prunes the non-target node-pty prebuilds (the win32-x64/arm64 ConPTY binaries are ~30 MB each), reclaiming ~28 MB+ per build across both node-pty copies, while defensively keeping everything if the target dir is missing.
  • Refined-terminal sidebar aesthetics (#218, thanks @matdac6). A visual pass over the sidebar: the glyph icons (⚙ ⧉ ✕ ▸) across workspace rows, the mini-sidebar, and settings are replaced by a shared stroke-icon SVG module (icons.tsx) so every control scales crisply; the agent-status indicator now routes through AGENT_STATUS_ICON's dotVar/glowClass/mark fields as a colored status dot with an animated glow plus a right-aligned play/pause mark; and token-derived depth, softened popover borders (rounded-lg + color-mix), row/popover enter animations, and a shared focus-ring helper round it out — every motion effect gated behind prefers-reduced-motion. Spec and plan under docs/superpowers/.

Fixed

  • Token ACL hardening silently degraded to icacls when wmux was launched from PowerShell 7 (Store install). The inherited PSModulePath leads with pwsh 7's Core-edition Modules directory, so the Windows PowerShell 5.1 child failed to auto-load its own Microsoft.PowerShell.Management/Security modules (CommandNotFoundException on Get-Item), and the #124 DACL rebuild — the only primitive that removes pre-existing explicit broad ACEs — never ran, falling back to icacls on every boot. The 5.1 child now gets PSModulePath stripped from its environment so it reconstructs its own default module path regardless of which shell spawned wmux. Found via the new boot traces: the measured "hardening cost" on a pwsh7-launched dev box was actually a failing PowerShell plus the fallback.

Contributors

  • @matdac6 — three contributions this cycle, on top of the workspace Rename context-menu item (#184): the AskUserQuestion awaiting-input notification (#212) — a clean signal-based fix with a root-cause writeup, a tool_name guard against spurious signals, and tests on the wmux side; the refined-terminal sidebar aesthetics pass (#218); and the lighter idle footprint across RAM, CPU, and package size (#219). Thank you!