Skip to main content

Overview

The HAPI runner is a background service (daemon) that manages AI agent sessions. It allows you to:
  • Keep sessions running even after closing your terminal
  • Spawn new sessions remotely via web/mobile app
  • Manage multiple concurrent sessions
  • Automatically restart sessions on system reboot

How the Runner Works

The runner operates as a detached background process that:
  1. Listens for session spawn requests from the hub
  2. Manages the lifecycle of active sessions
  3. Sends periodic heartbeats to the hub
  4. Cleans up terminated sessions
  5. Maintains state in ~/.hapi/runner.state.json
The runner starts automatically when you run hapi for the first time. Manual management is optional.

Commands

hapi runner start

Start the runner as a detached background process.
hapi runner start

Output Example

$ hapi runner start
Runner started successfully
The runner will continue running in the background even after the terminal closes.

Behavior

  • Spawns runner as detached process with stdio: 'ignore'
  • Waits up to 5 seconds to confirm successful start
  • Exits with error if runner fails to start
  • Safe to run if runner is already running (no-op)

hapi runner stop

Stop the runner gracefully. Active sessions remain alive.
hapi runner stop
Stopping the runner does not terminate active sessions. Sessions continue running as independent processes.

What Happens

  1. Sends shutdown signal to runner process
  2. Runner saves its state
  3. Runner closes HTTP server
  4. Runner exits gracefully
  5. Active sessions keep running

To Kill All Processes

If you need to stop both the runner and all sessions:
hapi doctor clean

hapi runner status

Show detailed runner diagnostics and health information.
hapi runner status
This command runs the same diagnostics as hapi doctor but focuses on runner-specific information.

Output Example

$ hapi runner status

 Runner Status
  PID: 12345
  Version: 0.16.0
  HTTP Port: 37891
  Uptime: 2 hours 34 minutes
  Last Heartbeat: 3 seconds ago
  Active Sessions: 2
  Log File: /home/user/.hapi/logs/runner-2026-03-06.log

 Runner Health
  Process alive: yes
  Version matches: yes
  State file valid: yes

hapi runner list

List all active sessions currently managed by the runner.
hapi runner list

Output Example

$ hapi runner list
Active sessions:
[
  {
    "sessionId": "session-claude-abc123",
    "type": "claude",
    "pid": 12346,
    "startTime": "2026-03-06T10:30:00.000Z",
    "mode": "remote"
  },
  {
    "sessionId": "session-codex-xyz789",
    "type": "codex",
    "pid": 12347,
    "startTime": "2026-03-06T12:15:00.000Z",
    "mode": "local"
  }
]

No Active Sessions

$ hapi runner list
No active sessions this runner is aware of (they might have been started by a previous version of the runner)

Runner Not Running

$ hapi runner list
No runner running

hapi runner stop-session

Terminate a specific session by its ID.
hapi runner stop-session <sessionId>
sessionId
string
required
The unique session ID to terminate. Get session IDs from hapi runner list.

Examples

# Stop specific session
hapi runner stop-session session-claude-abc123

# Output
Session stopped

Errors

# Missing session ID
$ hapi runner stop-session
Session ID required

# Runner not running
$ hapi runner stop-session session-abc123
No runner running

# Session not found or already stopped
$ hapi runner stop-session session-abc123
Failed to stop session

hapi runner logs

Print the path to the latest runner log file.
hapi runner logs

Output Example

$ hapi runner logs
/home/user/.hapi/logs/runner-2026-03-06.log

View Logs

Combine with other commands to view or tail logs:
# View logs
cat $(hapi runner logs)

# Tail logs (follow)
tail -f $(hapi runner logs)

# Search logs
grep "ERROR" $(hapi runner logs)

No Logs Found

$ hapi runner logs
No runner logs found

Runner State File

The runner maintains state in ~/.hapi/runner.state.json (or $HAPI_HOME/runner.state.json).

State File Structure

{
  "pid": 12345,
  "httpPort": 37891,
  "startTime": "2026-03-06T08:00:00.000Z",
  "startedWithCliVersion": "0.16.0",
  "startedWithCliMtimeMs": 1709712000000,
  "lastHeartbeat": "2026-03-06T10:30:45.123Z",
  "runnerLogPath": "/home/user/.hapi/logs/runner-2026-03-06.log"
}
pid
number
Process ID of the runner daemon
httpPort
number
HTTP port used for runner control API
startTime
string
ISO timestamp when runner started
startedWithCliVersion
string
CLI version that started this runner instance
lastHeartbeat
string
ISO timestamp of last heartbeat (updated every 60 seconds by default)
runnerLogPath
string
Path to current log file

Automatic Runner Management

The runner starts automatically in these scenarios:

1. First Session Start

When you run hapi (or any session command) for the first time:
$ hapi
[DEBUG] Ensuring hapi background service is running & matches our version...
[DEBUG] Starting hapi background service...
 Runner started
 Session starting...

2. Version Mismatch

If the running runner version doesn’t match the installed CLI version, it’s automatically restarted:
$ hapi
[DEBUG] Runner version mismatch (0.15.0 vs 0.16.0)
[DEBUG] Starting updated runner...
 Runner updated to 0.16.0

3. Stale or Dead Runner

If the runner state is stale or the process is dead, it’s cleaned up and restarted automatically.

Runner Lifecycle

Startup Sequence

  1. Runner process spawned as detached
  2. Loads configuration from environment and settings
  3. Acquires exclusive lock file (runner.state.json.lock)
  4. Writes state to runner.state.json
  5. Starts HTTP control server on random port
  6. Connects to HAPI hub via Socket.IO
  7. Registers runner with hub
  8. Begins heartbeat loop (every 60 seconds)
  9. Listens for session spawn requests

Heartbeat

Every 60 seconds (configurable via HAPI_RUNNER_HEARTBEAT_INTERVAL):
  1. Updates lastHeartbeat in state file
  2. Sends heartbeat to hub
  3. Cleans up dead session processes

Shutdown Sequence

  1. Receives shutdown signal (from hapi runner stop)
  2. Stops accepting new session requests
  3. Closes HTTP control server
  4. Disconnects from hub
  5. Releases lock file
  6. Exits process
  7. Active sessions continue running independently

Troubleshooting

Runner Won’t Start

Check if already running:
hapi runner status
View logs:
cat $(hapi runner logs)
Kill and restart:
hapi doctor clean
hapi runner start

Stale Runner State

If runner state becomes corrupted:
# Stop runner
hapi runner stop

# Remove state file
rm ~/.hapi/runner.state.json
rm ~/.hapi/runner.state.json.lock

# Restart
hapi runner start

Version Mismatch

If runner and CLI versions don’t match:
# Runner auto-updates on next session start
hapi

# Or manually restart runner
hapi runner stop
hapi runner start

Port Already in Use

The runner uses a random available port, so port conflicts are rare. If you encounter issues:
# View current port
hapi runner status

# Stop runner and let it pick new port
hapi runner stop
hapi runner start

Sessions Not Appearing

Check runner is running:
hapi runner status
List active sessions:
hapi runner list
Check hub connection:
hapi auth status
cat $(hapi runner logs) | grep "Connected to hub"

Advanced Usage

Custom Heartbeat Interval

export HAPI_RUNNER_HEARTBEAT_INTERVAL=30000  # 30 seconds
hapi runner start

Custom HTTP Timeout

export HAPI_RUNNER_HTTP_TIMEOUT=15000  # 15 seconds
hapi runner start

Multiple Runners (Multi-Namespace)

Run separate runners for different HAPI namespaces:
# Runner 1
export HAPI_HOME=~/.hapi-namespace1
export CLI_API_TOKEN=token1
hapi runner start

# Runner 2
export HAPI_HOME=~/.hapi-namespace2
export CLI_API_TOKEN=token2
hapi runner start

Diagnostics

View full system diagnostics including runner status

Session Commands

Start sessions managed by the runner

Next Steps

Build docs developers (and LLMs) love