A shared workspace runs its agent inside a tmux new-session -A instead of a plain PTY child. The agent lives in the tmux server, not in wsx's process tree — quitting wsx (or losing your ssh connection) doesn't kill it. Next time wsx starts, it reattaches to the same tmux session automatically.
Shared workspaces require tmux ≥ 3.2 (for the -e flag on new-session, used to forward wsx's environment into a pre-existing tmux server).
Create shared:
wsx workspace create <repo> --shared
Or from the dashboard, press S (capital) instead of n/N — it opens the same "new workspace" modal, just pre-set to shared. Ctrl-s toggles the shared flag while the modal is open, so you can flip it either way before confirming.
Convert an existing workspace:
wsx workspace share <repo> <slug>
wsx workspace unshare <repo> <slug>
These CLI commands flip the shared flag. Running sessions keep their current backend (shared or non-shared) until restarted manually — the command prints a note saying so. New or restarted sessions will pick up the new backend.
Alternatively, press T (capital) on a selected workspace row to open a confirmation modal. This immediately restarts any currently-running agent sessions in that workspace — there's no way to move a live process in or out of tmux — but conversation history isn't lost: the restart resumes via --continue, so the agent picks the conversation back up. Non-running instances just flip the flag with nothing to restart. T is a no-op on a repo header; sharing is per-workspace.
Session naming:
Each agent instance in a shared workspace gets a deterministic tmux session name: wsx-<repo>-<workspace> for the primary agent, or wsx-<repo>-<workspace>-<agent><ordinal> for additional instances (e.g. wsx-myrepo-fix-bug-codex2). Characters outside [A-Za-z0-9_-] in the repo/workspace name are replaced with -, since tmux rejects . and : in session names. If two workspaces sanitize to the same name (e.g. repo a + workspace b-c vs repo a-b + workspace c), wsx appends the workspace id to disambiguate, so -A never attaches to the wrong agent. The name is derived once, stored in session_ref, and reused verbatim afterwards — it is never re-derived, so renaming a workspace does not orphan its running agent.
Dashboard indicator:
Shared workspaces are marked on the dashboard with a badge just left of the branch name — the tmux logo when nerd fonts are enabled, a hollow ◇ otherwise. The badge is green while the workspace's tmux session is alive (attached in this wsx, or detached-but-running on the server) and red when the workspace is shared but no live session backs it — the session has exited or was never started, so a remote peer can't attach. Direct workspaces show no badge.
A shared workspace's tmux session can outlive the wsx process that spawned it — right after a wsx restart, the agent is still running on the server even though this wsx holds no client for it. The green badge covers that case too: wsx periodically confirms the session with tmux has-session, so the badge stays green while the workspace's row status reads idle. Attaching (Enter on the row) reattaches wsx's client to the live session; the agent and its history are exactly where you left them.
Manual access:
Because the agent is a normal tmux session, you can attach to it directly, bypassing wsx entirely:
tmux attach -t wsx-<repo>-<workspace>
This works over a plain ssh connection today — no wsx-specific networking, remote-control setup, or port-forwarding required. See Remote access for the broader pattern of running wsx itself over ssh/tmux; shared workspaces are the finer-grained, per-workspace version of the same idea; you can tmux attach to one agent's session without pulling in the rest of wsx.
Listing shared workspaces:
wsx shared list
wsx shared list --json
Without --json, prints one tab-separated line per agent instance: repo, workspace, tmux session name, and alive/(dead)/-. With --json, prints the same data as structured records (repo, workspace, branch, worktree path, and each agent's label/kind/session name/liveness) — useful for scripting against.
Browsing another machine:
To browse and attach to shared workspaces running on a remote machine, configure a list of ssh destinations under wsx config edit shared_hosts. The setting stores one entry per line as name=ssh-destination, e.g.:
mini=eben@ebenmini.local
lab=user@lab.example.com
On the dashboard, press H (capital, mnemonic hosts) to open a picker over these configured hosts, sorted by name. If no hosts are configured, an error modal points you at wsx config edit shared_hosts.
Selecting a host spawns a background fetch via ssh <dest> "sh -lc 'wsx shared list --json'" (one pre-quoted remote command, so ssh's argv join preserves it; login shell so wsx is found on the host's PATH). Results render as a list titled "shared workspaces on <host>", showing one row per agent instance:
repo/workspace branch label ●|✗
The marker (● for alive, ✗ for dead/stale) indicates whether the remote tmux session still exists. Navigate with j/k (or ↑/↓), select a live row with Enter to attach, r to re-fetch the list, and Esc to close. The list is ephemeral — nothing is written to the local database, so there's no sync or cache-invalidation problem.
Attaching spawns ssh -t <dest> -- "sh -lc \"tmux -u attach -t '=<name>'\"" as a PTY session — the remote command is one pre-quoted argument routed through a login sh (the same PATH rules as the list fetch; sshd otherwise hands the command to a non-login zsh that reads only ~/.zshenv, where homebrew's tmux often isn't on PATH), the = target is single-quoted so zsh can't expand it as a command path, and -u forces UTF-8 (the ssh context has no locale, and without it tmux degrades box-drawing characters to rows of literal qs). You interact with the remote agent as if it were local; the exact-match = prefix ensures the correct agent is targeted, even if multiple agents sanitize to similar names.
Detaching and persistence:
Ctrl-x d detaches from the remote session, severing only the local ssh client. The remote agent keeps running in its tmux server — quitting wsx has the same effect. Reattaching resumes the exact session with its full history intact. Detaching lands back on the dashboard; the fetched list is ephemeral and never persisted, and pressing H again reopens the host picker with a fresh fetch.
Failure modes:
Fetching fails if the host is unreachable, ssh authentication fails, wsx is missing on the host's login-shell PATH, or a row's tmux session has since died (stale). All fetch errors surface in an error modal carrying ssh's stderr; dead rows show the ✗ marker and cannot be attached to (attempting to attach shows a notice "no live session to attach to").
Requirements:
- SSH key access to the remote host (password prompts are not supported for the background list fetch — use key-based auth via
ssh-agentor key files; the attach itself runs in a real terminal but key auth is strongly recommended for a smooth flow). - wsx and tmux installed on the host and reachable via a login
sh's PATH (e.g.,ssh <host> "sh -lc 'which wsx tmux'"should print both — the outer double quotes keepsh -lc '…'a single argument, so ssh's space-join back into the host shell preserves the inner quoting). macOS gotcha: PATH additions that live only in zsh config (~/.zshrc/~/.zprofile, including homebrew'sbrew shellenv) are invisible tosh -l— add them to~/.profiletoo. - Workspaces created as shared on the host (either via
wsx workspace create <repo> --sharedor by converting an existing one withT). - A local
sshbinary (no local tmux needed for remote attach).
v1 limitation — scrollback:
Reattaching (in wsx or via a bare tmux attach) only repaints the tmux session's current visible screen — wsx's own scrollback buffer (see Mouse, scrollback, and text selection) resets with each new client and doesn't carry history across a detach/reattach. tmux's own scrollback for the session is unaffected and still reachable in-session via its usual copy-mode (Ctrl-b [ with tmux's default prefix). A richer remote-scrollback view is planned for a later phase.