Added
-
swemux agent- the mux fleet tools over a second transport. Inside any swe-mux
pane,swemux agent toolslists exactly the tools that session may call and
swemux agent call <tool> key=value ...calls one (key:=valuefor JSON-typed values,
--inputfor a whole JSON object). It authenticates as the calling session with the
credentials every pane already carries and speaks to the same endpoint the MCP client
uses, so it inherits every authority check, budget, and provenance record - and it is a
pure passthrough, so new tools appear in it with no CLI change. A new per-harness
"Agent CLI" toggle sits beside the MCP one under Settings → Harnesses → Fleet access;
with both capabilities off, that harness's sessions hold no fleet surface at all and the
daemon refuses their tokens. Every agent pane now carriesMUX_SURFACESnaming what it
holds, and the shipped skill teaches whichever surface is present. In exchange, the
session-acting operator commands (swemux send,kill,spawn) now refuse requests
from an agent session's pane and name the agent surface instead. -
await_session- wait for another session inside one tool call. The synchronous
sibling ofwatch_session: it blocks until the target leaves working and holds a settled
state, ends, or a bounded timeout elapses - and the timeout is a normal result carrying
the current state, so a caller re-calls to keep waiting without ever outliving its own
tool-call timeout. Nothing is staged or delivered; the answer is the call's return value. -
request_spawncan now watch, place, and model its spawn in one call.watch:true
arms the settle watch you would otherwise arm separately (deferred through human approval
on the draft path),pane:"split_horizontal"|"split_vertical"asks the first browser
viewing the Project to open the new session in a split beside it, and an omittedmodel
now genuinely takes the target Project's newdefault_agent_modelstable (Projects →
Repository options, or.swe-mux/config.toml) before falling back to the CLI's own
sticky default. -
Monitoring agents now hear their answers without a human pumping the queue. An open
settle watch holds the watcher's auto-delivery grant open for the watch's own lifetime
(previously any watch longer than the idle window lapsed the grant, and the notice
arrived with nothing to deliver it), and any authenticated mux tool call now counts as
the "somebody is reading this" evidence that resets the consecutive-send cap - so an
orchestrator that reacts to notices by acting, rather than by writing replies, no longer
goes silently deaf after three deliveries. -
First-run setup asks the questions that used to wait for Settings. The tier page now
carries a live-previewed theme picker and a Customize fold-out with two new axes: an
agent autonomy level (Supervised / Assisted / Autonomous - whether queued messages
deliver themselves, and under how wide a set of caps) and one checkbox per switch the
chosen tier sets, applied atomically with the tier through the same daemon-owned policy
route (POST /api/experience-tiernow takesautonomyandoverrides;
GET /api/experience-tiersserves the tables the panel draws). The agents page gains an
install-wide fleet access choice - MCP + CLI, MCP only, CLI + skill, or none -
answered once instead of per harness. And setup now ends on a first steps page: the
same three-quest card the empty workspace shows, so finishing setup hands off into the
guided voice, worktree, and phone setups rather than closing on a blank stage. -
The command rail can be switched off per device class. Settings → Appearance →
Action rail:rail_enabled_desktop/rail_enabled_mobile, both on by default,
hot-applied, and offered during first-run Customize. Turning one off hides the whole
rail there (on mobile that includes the pinned Send button) while the configured layout
is kept and comes back untouched. -
The first launch of the Windows desktop shell offers to add itself to the Start Menu and
to start when you sign in. A wheel cannot create a shortcut and nothing runs after
pip/uv, soswemux install-shortcutwas a command nobody knew to run. Asked once per
install whichever way you answer, never for the installer's own build, and never when a
Start Menu entry already exists. It writes no desktop icon; that stays a choice in
Settings. -
swemux startruns the daemon in the background and returns once it is serving. For
the browser-only case, for Linux and macOS where there is no desktop app, and for
iterating from a checkout: closing the terminal does not stop it,swemuxd --shutdown
does, and a daemon that is already serving is reported and left alone. It is the only
command that starts a daemon, and only when typed -swemux lsagainst a stopped daemon
still says so. -
A daemon started where you can see it opens the UI in your browser. Gated on a
terminal actually watching the process, which is what keeps it out of every start that
should not do this: the tray's own daemon child,swemux start's detached child, a
restart successor, and a login task are all non-TTY and unaffected.--no-browseron
either command, orSWE_MUX_NO_BROWSERfor a caller that cannot pass a flag.
Changed
-
The side panel leads with Notes and Files, and the default rails differ per device.
On a fresh install (or a device that never rearranged its tabs) the utility drawer's
default order is now Notes, Files, then the session block - the two surfaces that are
useful before a single session exists come first. The deterministic tier's default also
puts the Activity tab away, since everything that feeds it is model-backed and off in
that tier; choosing Automations brings it back. The Action rail's shipped layout is now
a desktop row of mouse-verbs (copy surfaces, paste, approve, Markdown helpers, the
pickers) and two mobile rows that keep the terminal keys, modifiers, and pads - lifted
from a long-lived daily-driver configuration instead of one identical row on both.
Existing arrangements and rail layouts are untouched; defaults only ever apply where
nothing was stored. -
notifynow requires thedeliveryargument ("when_idle"or"now", and"now"
requires a reason). With a silent default, senders never weighed whether the target
should hear them before or after finishing its current turn; now the choice is made at
every call. The message size cap also rose from 4,000 to 8,000 characters - a real task
brief with acceptance criteria did not fit, and what got trimmed was the constraints. -
On Windows,
swe-muxnow works on a plainuv tool install swe-muxor
pip install swe-mux, with no terminal, no extra and no download.pystrayand
pywebviewmoved out of thedesktopextra and into ordinary dependencies, marked
Windows-only. Previously every install got theswe-muxlauncher - it is a GUI entry
point, so it opens no console - while an install without the extra got a launcher whose
only behaviour was to fail on a missing import, into a message box, with both suggested
remedies leading back through a terminal. Over 2.4 MB of pure-Python packages. The
swe-mux[desktop]spelling still resolves and now adds nothing, so existing scripts keep
working. -
The
muxandmuxdaliases are gone. The commands areswemux,swemuxdand
swe-mux- one per program, which is the floor.muxis shared with at least one
unrelated tool in the same category, and on a machine with both, PATH order silently
decides which one a typedmuxruns. Shipping a launcher under that name is what creates
the collision rather than what survives it; not occupying it leaves nothing to shadow.
If aswemuxever is unreachable,swemux doctorand the daemon's own startup hint say
so by name rather than leaving you to guess. -
The Windows installer now ticks "Start swe-mux when I sign in" by default. A
multiplexer that has to be launched before it can watch anything is answering the wrong
question at sign-in. It starts hidden in the tray and the tray menu turns it off in one
click. The desktop-icon box stays unticked. -
swemux install-shortcut's run-at-login entry can finally be created from the UI.
Settings → General → Desktop integration lets you choose which of the three shortcuts to
write - Start Menu, Desktop, Start with Windows - where before it always reported all
three, could remove all three, and could only ever create two. The only way to turn on
run-at-login was the tray menu, which a phone or any remote client cannot reach. -
"Start with Windows" in the tray menu now reads both mechanisms it can be turned on
with. A run-at-login entry created byswemux install-shortcut --startupor by Settings
is ashell:startupshortcut, and the menu item only consulted the registry - so it
showed "off" beside a swe-mux that demonstrably did start at sign-in, and turning it "on"
left two entries racing to launch the same app. Either one now counts as on, and turning
it off clears both. -
swemuxdnow prints where the UI is and how to stop it, instead of a bare
======== Running on ... ========line that happened to carry the URL. Nothing runs after
uv tool installprints its executable list, so a daemon's first line is the only place
those facts can be given. It also names the thing Ctrl-C does not do: Ctrl-C detaches and
leaves supervised sessions running, andswemuxd --shutdownis what stops everything. -
The Settings → Desktop integration group no longer offers to download the desktop shell,
because there is nothing left to download. It reports whether this environment can run the
tray and, when it cannot, the reinstall command for how this copy was installed.
Fixed
- Turning the per-harness Agent CLI toggle off is now reported as restart-scoped, like
its MCP and instrumentation neighbours. It was documented and rendered as needing a
restart while the daemon classified it as hot-applied, so the response claimed an apply
that never reached already-built adapters.
Everything in the following group came from one testing session on a clean Windows 11 machine
that is not the development host, which is the first time swe-mux had been run on one. They
share a cause: a property of the development host was written down as a fact about Windows.
- Claude Code sessions report their lifecycle again on machines without Git Bash. The
hook command named the interpreter in the MSYS form (/c/Users/...), which only Git Bash
can run. Where Claude Code dispatches hooks through PowerShell instead, all eleven events
failed withCommandNotFoundExceptionand took status detection, history, the prompt queue
and approvals with them - while the session kept running and looking healthy. The path is
now writtenC:/Users/..., the one spelling PowerShell, Bash and cmd all execute, and it
reaches the shell unquoted (a space is removed via the 8.3 short name, because at command
position PowerShell reads a quoted string as a string rather than a program). swemux doctornow fails when an agent session has never reported a hook. The failure
above was silent and total, and was found by a human reading the CLI's stderr. It reports
only sessions this daemon spawned, so a daemon restart does not flag every healthy session.- A prerequisite that is installed but not on PATH is no longer reported as missing.
Detection equated "on PATH" with "installed", so a machine with Git installed and Tailscale
installed and connected to a tailnet was told towinget installboth. Tailscale's
Windows installer never adds its directory to PATH, so that was every GUI install of it -
and it silently disabled Tailscale Serve,tailscale certand the direct TLS listener with
it. There are now three states rather than two, the off-PATH remedy names PATH instead of
an install, and detection looks in the default install locations. - Settings → Diagnostics has a Re-scan button and a per-tool path override. Re-scan
re-reads PATH from Windows before looking again, because a daemon inherits its environment
once at startup and would otherwise keep reporting a tool installed five minutes ago as
absent, with nothing saying why. - Install instructions are per-platform. A Linux user missing Git was told to run
wingetand sent to a/download/winpage. uvis on the prerequisite checklist, since managed integrations are installed with it.- The Edge TTS integration installs without uv when there is a real Python to use. uv is
still preferred and still tried first, becauseuv venv --python 3.12can provide the
interpreter as well as the environment - which is the only thing that works on a machine
with no Python, or in the frozen desktop app wheresys.executableis the bundle rather
than a Python. A source install with neither uv nor that problem now falls back to
python -m venvand pip, installing the same pinned version from the same index. Where
neither is possible the refusal names both remedies instead of only naming uv, and a
Debian machine missingpython3-venvis told that specifically. - A Windows 11 machine without WSL stops spawning
wsl.exeevery 30 seconds. Windows 11
ships that binary whether or not the subsystem is installed, so availability was a
guaranteed false positive; the stub then blocked, because the daemon runs windowless, and
was tree-killed at an 8s timeout on every status poll, forever. Availability is now a
registry check and no WSL command inherits the daemon's stdin. - Voice setup works on Python 3.13 and 3.14.
uv tool installpicks the newest CPython
on the machine, and the pinned voice closure had no wheel spaCy could load on 3.14, so
local voice dead-ended at "no wheel this interpreter can load for: spacy". The pins now
cover 3.12 through 3.14, CI checks the closure against each of them rather than only the
3.12 it pins, and the refusal names the interpreter and the supported range instead of
reading as a broken package. - Downloads verify TLS against the OS certificate store. The Kokoro pronunciation model
failed withunable to get local issuer certificateagainst github.com on a machine whose
browser reached the same URL: Windows fetches most roots on demand and Python's reading of
the store only sees the ones already fetched. - The Edge TTS install no longer throws away 80 seconds of work to report a timeout. It
checks it can reach PyPI before building anything, and separates "cannot connect" from
"cannot verify". Its verification step - which imports the package and makes no network
request at all - had a 20s budget that a first-ever import on a cold, scanned filesystem
could not meet; it is now 120s and says what it was actually doing.
Full changelog: https://github.com/jatoran/swe-mux/blob/v0.2.0/CHANGELOG.md