Multi-Agent Guide¶
Coordinate multiple AI coding sessions with agentwatch.
Overview¶
agentwatch runs one kind of session: a visual tmux TUI (Claude Code, Codex,
or a shell) shown in the web viewer. The agentwatch session CLI is a full
alternative control plane for those sessions — anything a human can do in the
browser, an agent can do from the shell, and nothing more. There is no separate
"headless" or "background" mode: an orchestrating agent opens a normal session
and drives it exactly as a person would in the web viewer.
This enables:
- Parallel task execution - Multiple agents working on different tasks
- Orchestration - One agent driving other agent sessions from its shell
- Supervision - Watching several sessions and stepping in when they need you
Orchestration is driven entirely from the command line with the agentwatch
session CLI. An orchestrating agent simply runs agentwatch session ...
commands (via its shell tool) against the local daemon; no per-agent
configuration is needed. The same commands work over SSH
(ssh HOST agentwatch session ...), so you can drive sessions on a remote
machine with zero extra setup.
The core loop is: spawn a session with an initial task, wait until it
needs attention, inspect it with capture/parse, then respond —
answer a prompt, send a follow-up message, or send raw keys — just as you would
by clicking and typing in the browser.
Viewing Multiple Sessions¶
Web Viewer¶
The session list shows all active sessions with their states. Click any session to view its terminal.
State badges update in real-time, so you can see at a glance which sessions need attention.
CLI¶
# List all sessions
agentwatch session list
# Detailed status (optionally for one session)
agentwatch session status
agentwatch session status my-session
# Capture a specific session's terminal
agentwatch session capture my-session
# Parsed view (program/state/detail)
agentwatch session parse my-session --verbose
Every verb accepts --json for machine-readable output.
API¶
# List all sessions via the dual-auth API (session cookie OR Bearer token)
curl -sk -H "Authorization: Bearer $(cat ~/.config/agentwatch/tokens/api)" \
https://localhost:8081/api/state/sessions
Hooks for Multiple Sessions¶
Configure hooks that fire for any session or specific ones:
hooks:
# Fire for any session
- name: any-blocked
type: shell
trigger: state_change
to_states: [Blocked]
command: 'notify-send "agentwatch" "$SESSION_NAME needs input"'
# Fire only for specific sessions (use session prefix)
- name: worker-complete
type: webhook
trigger: state_change
to_states: [Idle]
url: "https://api.example.com/worker-done"
The $SESSION_NAME variable identifies which session triggered the hook.
CLI Orchestration¶
Use the agentwatch session CLI to programmatically manage multiple sessions.
An orchestrating agent runs these commands through its shell tool; because they
go through the daemon's API, the exact same invocations work locally or over
SSH.
Starting Sessions¶
# Start three coding sessions, each with an initial task
agentwatch session new --dir ~/proj --name worker-a --task "Implement feature A"
agentwatch session new --dir ~/proj --name worker-b --task "Implement feature B"
agentwatch session new --dir ~/proj --name worker-c --task "Write integration tests"
Useful session new options:
--agent claude_code|codex|shell- which tool to launch (defaultclaude_code)--name NAME- stable name for later commands (otherwise auto-generated)--task "..."- initial prompt sent to the agent once it's ready--permission-mode MODE- starting permission mode, agent-aware:- claude_code:
default|acceptEdits|plan|bypassPermissions - codex:
auto|read-only|full-access - (ignored for
shell)
Each session is a normal visual TUI. You can watch it in the web viewer at the same time you drive it from the CLI — they act on the same session.
Waiting for a Session¶
wait and wait-any block until a session needs attention (it enters Blocked,
Idle, Error, or Stopped). They use a chunked long-poll, so they block
efficiently instead of busy-waiting. Add --timeout S to bound the wait.
# Block until one specific session needs attention
agentwatch session wait worker-a
# Block until any of the named sessions needs attention
agentwatch session wait-any worker-a worker-b worker-c
Responding to a Session¶
Once a session needs attention, inspect it and reply the same way you would in the browser:
# See what it's asking
agentwatch session parse worker-a --json
agentwatch session capture worker-a
# Answer a prompt with keystrokes (e.g. pick option 1 / approve a dialog)
agentwatch session respond worker-a --keys 1
# Send a follow-up instruction (types text and submits it)
agentwatch session tell worker-a "Now run the tests"
agentwatch session respond worker-a --message "Now run the tests"
# Send raw terminal input for anything else
agentwatch session send worker-a --key escape
agentwatch session send worker-a --text "git status" --enter
respond takes either --keys (answer a prompt with keystrokes) or --message
(type text and submit). tell NAME MSG is sugar for respond --message. For
lower-level control, send / key reproduce every keystroke the web viewer can
send.
Orchestration Patterns¶
Sequential (dependent tasks)¶
When one task depends on another, spawn, wait, then spawn the next:
# Task B depends on Task A
agentwatch session new --dir ~/proj --name task-a --task "Implement the base class"
agentwatch session wait task-a # block until it needs attention
agentwatch session parse task-a --json # confirm it finished cleanly
agentwatch session new --dir ~/proj --name task-b --task "Implement a subclass using the base class"
agentwatch session wait task-b
Parallel (independent tasks)¶
For independent work, spawn several sessions and service whichever needs attention first:
# Start independent sessions
agentwatch session new --dir ~/proj --name worker-a --task "Feature A"
agentwatch session new --dir ~/proj --name worker-b --task "Feature B"
agentwatch session new --dir ~/proj --name worker-c --task "Feature C"
# Drive them: wait for any to need attention, handle it, repeat
sessions=(worker-a worker-b worker-c)
pending=("${sessions[@]}")
while [ "${#pending[@]}" -gt 0 ]; do
result=$(agentwatch session wait-any "${pending[@]}" --json)
name=$(echo "$result" | jq -r .name)
state=$(echo "$result" | jq -r .state)
case "$state" in
idle)
# Task reached the prompt — inspect, then send a follow-up or drop it
echo "$name is idle"
pending=("${pending[@]/$name}") # remove from pending
;;
blocked)
# It's asking something — read the prompt and answer with keystrokes
agentwatch session capture "$name"
agentwatch session respond "$name" --keys 1
;;
error)
echo "$name errored: $(echo "$result" | jq -r .detail)"
pending=("${pending[@]/$name}")
;;
stopped)
pending=("${pending[@]/$name}")
;;
esac
done
echo "All sessions handled"
The orchestrator is just a shell loop that runs wait-any, reads the --json
output, and drives each session with the same verbs a human clicks in the web
viewer.
Naming Conventions¶
Use consistent --name values for easier management:
# By task type
agentwatch session new --dir ~/proj --name feature-auth --task "..."
agentwatch session new --dir ~/proj --name feature-api --task "..."
agentwatch session new --dir ~/proj --name test-unit --task "..."
# By worker number
agentwatch session new --dir ~/proj --name worker-1 --task "..."
agentwatch session new --dir ~/proj --name worker-2 --task "..."
If you omit --name, the daemon assigns a generated name (reported in the
command output). Consider system resources when deciding how many sessions to
run concurrently.
Resource Isolation¶
Each session runs in its own tmux session with:
- Isolated working directory (set per session with
--dir) - Separate environment variables
- Independent terminal state
Working Directory Control¶
Each session is pinned to the directory you pass to --dir:
agentwatch session new --dir /projects/api --name api-work --task "..."
agentwatch session new --dir /projects/web --name web-work --task "..."
Driving Sessions Over SSH¶
Because the CLI talks to the daemon over its /api endpoints, every
command works unchanged over SSH:
ssh build-host agentwatch session new --dir ~/proj --name remote-a --task "..."
ssh build-host agentwatch session wait-any remote-a remote-b
ssh build-host agentwatch session respond remote-a --keys 1
No tokens or per-agent configuration are required on either end - the remote daemon authenticates the request and the SSH session inherits it.
Monitoring Patterns¶
Dashboard View¶
For many sessions, the web viewer's session list provides a dashboard:
- Color-coded states
- Duration in each state
- Quick access to any session
Webhook Aggregation¶
Send all state changes to a central endpoint:
hooks:
- name: central-monitor
type: webhook
trigger: any_change
url: "https://your-dashboard/agentwatch/events"
include: json
Build a custom dashboard from the webhook events.
Log Aggregation¶
Log all changes to a file:
hooks:
- name: log-all
type: shell
trigger: any_change
command: |
echo "$TIMESTAMP $SESSION_NAME $PROGRAM $STATE $DETAIL" \
>> /var/log/agentwatch/all-sessions.log
Best Practices¶
Clean Session Exit¶
Always exit sessions cleanly:
# For each session:
agentwatch session exit worker-a # ask the agent to exit gracefully
agentwatch session wait worker-a # wait for it to settle (Idle/Stopped)
agentwatch session terminate worker-a # tear down the tmux session
Use agentwatch session terminate NAME --force to kill a stuck session.
Handle All Outcomes¶
Every session can settle into:
- Idle - Reached the prompt (task done, or waiting for your next message)
- Blocked - Needs a decision (permission dialog, question)
- Error - Something went wrong
- Stopped - Terminated externally
Handle all cases in your orchestration logic.
Limit Concurrency¶
Don't spawn too many agents:
- System resources (CPU, memory)
- API rate limits
- Context switching overhead
Start with 2-3 concurrent agents and scale based on results.
Use Timeouts¶
Set timeouts for long-running tasks: