Use when you need to orchestrate multiple parallel terminal sessions via wsh server mode. Examples: "run builds in parallel across several projects", "tail logs in one session while working in another", "fan out tests across multiple sessions and gather results".
60
68%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Optimize this skill with Tessl
npx tessl skill review --optimize ./skills/multi-session/SKILL.mdIMPORTANT: EXECUTION CONTEXT This skill describes what to do — domain patterns and decision-making. It does NOT describe how to call the API.
- If you have
wsh_*tools (check your toolkit forwsh_send_input,wsh_get_screen, etc.): use them directly. Operation names in this skill generally map to tool names (e.g., "send input" →wsh_send_input). When in doubt, list your availablewsh_*tools.- If you do NOT have
wsh_*tools: you are in HTTP/curl fallback mode. DO NOT GUESS endpoints or CLI subcommands. Load the full API reference first: search your workspace forskills/core/and readSKILL.md. It contains every endpoint with working curl examples and a bootstrap sequence.- Quick bootstrap:
curl -sf --unix-socket ${XDG_RUNTIME_DIR:-/tmp}/wsh/default.http.sock http://localhost/health— if that fails:wsh server -L agent-$$ --ephemeral &and retry.
Sometimes one terminal isn't enough. You need to run a build while tailing logs. Run tests across three environments simultaneously. Drive multiple processes that each need independent input and output. Multi-session gives you this.
Use multi-session when:
Don't use multi-session when:
&& or & would sufficewsh always runs as a server daemon, so multi-session is
always available. The sessions API endpoint is at
/sessions/. You create sessions explicitly and interact
with each via /sessions/:name/ prefix.
If you're in an attached wsh session (started with wsh),
there's already a default session. You can create
additional sessions via the API alongside it.
Give each session a descriptive name that reflects its purpose:
create session "build"
create session "test" with command: npm test --watch
create session "logs" with command: tail -f /var/log/app.logYou can specify:
name — identifier (auto-generated if omitted)command — run a specific command instead of a shellrows, cols — terminal dimensionscwd — working directoryenv — environment variables (object of key-value pairs)tags — string labels for grouping and filteringA session with a command will exit when that command
finishes. A session without one starts an interactive shell
that persists until you kill it.
list sessions
get session "build"Prefer a graceful exit when the session is running an interactive program:
# Exit a shell
send input to "build": exit\n
# Quit a TUI
send input to "monitor": qThe session will close automatically when its process exits.
If the process is stuck or you don't care about graceful shutdown, force-kill it:
kill session "build"This terminates the process immediately. Clean up after yourself — don't leave orphaned sessions running.
If a session's purpose changes:
rename session "build" to "build-v2"The power of multi-session is parallelism. Here are the common coordination patterns.
Spawn several sessions, kick off work in each, then poll them for completion. Tag them for easy group operations:
# Create sessions and start work (all tagged "ci")
create session "test-unit" tagged: ci, send: npm run test:unit
create session "test-e2e" tagged: ci, send: npm run test:e2e
create session "lint" tagged: ci, send: npm run lint
# Poll each for completion
for each session:
wait for idle
read screen
check for shell prompt (done) or still running
# Gather results
read scrollback from each session
report combined resultsThis is the most common pattern. The key insight: you don't have to wait for one to finish before checking another.
Best approach — wait for any session (with tag filter):
wait for idle on sessions tagged "ci" (timeout 1000ms)
# returns the name of whichever session settled first
# read its screen, check if done
# repeat with last_session + last_generation to avoid
# re-returning the same session immediatelyThis races all tagged sessions and returns the first to settle. Much more efficient than polling each one individually. The tag filter ensures unrelated sessions don't interfere.
Alternative — poll round-robin:
await idle test-unit (short timeout, 1000ms, fresh=true)
await idle test-e2e (short timeout, 1000ms, fresh=true)
await idle lint (short timeout, 1000ms, fresh=true)
# repeat until all show shell prompts
# fresh=true prevents busy-loop storms when a session is idleOne session runs something persistent (a dev server, log tail, file watcher). Other sessions do active work. Periodically check the watcher for relevant output:
create session "server", send: npm run dev
create session "work"
# Do work in the work session
send to "work": curl localhost:3000/api/health
# Check server session for errors if something fails
read screen from "server"One session's output informs the next session's input. This isn't true parallelism — it's staged work:
create session "build"
send to "build": cargo build 2>&1 | tee /tmp/build.log
wait for build to finish
create session "deploy"
send to "deploy": ./deploy.sh
# only if build succeededWhen wsh is configured as a federated cluster, sessions can span multiple servers. A hub server orchestrates one or more backend servers, and the session list aggregates across all healthy backends transparently.
What changes with federation:
server
field indicating which machine they live onWhat stays the same:
For detailed guidance on distributed session management, server health monitoring, and failure handling, see the wsh:cluster-orchestration skill.
It's easy to create sessions and forget about them. Every session is a running process consuming resources. Adopt a discipline:
Names are how you identify individual sessions. Tags are how you group them. Use both for organization:
Good: "test-unit", "test-e2e", "build-frontend"
Bad: "session1", "s2", "tmp"If you're creating sessions in a loop, use a predictable naming scheme so you can iterate over them later:
test-0, test-1, test-2
build-api, build-web, build-docsTags let you group related sessions for bulk operations. Tag all test sessions with "test", all build sessions with "build", then list or wait-for-idle on just that group:
create "test-unit" tagged: test
create "test-e2e" tagged: test
create "lint" tagged: test
list sessions tagged "test"
wait for idle on sessions tagged "test"Tags can be added and removed after creation, so you can re-categorize sessions as their role changes.
If you just need to run three commands in sequence, one
session with && is simpler than three sessions. Multi-session
adds overhead — session creation, polling, cleanup. Only use
it when you genuinely need parallelism or isolation.
A session running a specific command (not a shell) will exit when that command finishes. The session disappears from the sessions list. If you're polling and a session vanishes, the process finished — read its output before it's gone, or redirect output to a file you can read from another session.
Each session is independent — different working directory,
different environment, different shell history. If you cd
in one session, the others are unaffected. This is useful
for isolation but means you can't share state between
sessions through shell variables. Use files, environment
variables at creation time, or the filesystem as shared state.
4863aaf
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.