Skip to content

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 (default claude_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:

agentwatch session wait worker-a --timeout 600   # 10 minute timeout