Skip to main content

Overview

HAPI is a multi-agent platform that wraps multiple AI coding agents and provides unified remote control. Each agent has its own capabilities, permission modes, and integration approach.

Claude Code

Official Claude CLI with native desktop integration

Codex

OpenAI-powered agent via custom implementation

Cursor Agent

Cursor IDE’s CLI agent with plan/ask modes

Gemini

Google Gemini via Anthropic Code Plugins (ACP)

OpenCode

OpenCode agent with plugin hook system

Agent Flavors

// From shared/src/modes.ts
type AgentFlavor = 'claude' | 'codex' | 'gemini' | 'opencode' | 'cursor'
Each session has an optional flavor field in metadata that identifies the agent type:
type Metadata = {
  flavor?: string | null  // Agent flavor
  // ...
}
Flavor determines which permission modes and model modes are available for the session.

Claude Code

Overview

Official Claude CLI

  • Native desktop app integration
  • Built-in permission system
  • Plan mode support
  • Model selection (Sonnet/Opus)

Integration Approach

HAPI wraps the official claude CLI binary:
# User runs
hapi

# HAPI internally runs
claude --api-url <hub-url> [user flags]
Key Files:
  • cli/src/claude/ - Claude Code integration
  • cli/src/claude/runClaude.ts - Main wrapper

Permission Modes

const CLAUDE_PERMISSION_MODES = [
  'default',           // Ask for permission on every tool use
  'acceptEdits',       // Auto-approve file edits, ask for commands
  'bypassPermissions', // Approve everything (YOLO)
  'plan'               // Plan mode, describe actions first
] as const

Standard Mode

  • Ask for permission on every tool use
  • User approves/denies each action
  • Safest mode, most control
hapi --mode default

Model Modes

const MODEL_MODES = ['default', 'sonnet', 'opus'] as const

default

Use default model (Sonnet 3.5)

sonnet

Force Sonnet 3.5 (faster)

opus

Force Opus (more capable)
# Force Opus model
hapi --model opus

Session Management

# Start Claude session
hapi

# Resume existing session
hapi --resume <sessionId>

# Pass through Claude CLI flags
hapi --context "Refactor auth module"

Codex

Overview

OpenAI-Powered Agent

  • Custom implementation (not official OpenAI CLI)
  • Read-only and safe-yolo modes
  • MCP tool integration
  • Resume support

Integration Approach

Codex is a custom agent implementation built into the HAPI CLI: Key Files:
  • cli/src/codex/ - Codex mode integration
  • cli/src/codex/runCodex.ts - Main entry point
  • cli/src/codex/happyMcpStdioBridge.ts - MCP bridge

Permission Modes

const CODEX_PERMISSION_MODES = [
  'default',    // Ask for permission
  'read-only',  // No file modifications allowed
  'safe-yolo',  // Auto-approve safe operations
  'yolo'        // Approve everything
] as const

Standard Mode

  • Ask for permission on file writes and commands
  • Allow reads without asking
hapi codex

Commands

# Start Codex session
hapi codex

# Resume existing session
hapi codex resume <sessionId>

# Start MCP bridge (exposes local tools to Codex)
hapi mcp

Model Configuration

Codex does not support runtime model switching. Model is configured via environment variables or defaults.

Cursor Agent

Overview

Cursor IDE CLI

  • Official Cursor Agent CLI
  • Plan and ask modes
  • Continue from last chat
  • Local and remote modes

Integration Approach

HAPI wraps the agent CLI from Cursor:
# Install Cursor Agent CLI
# macOS/Linux:
curl https://cursor.com/install -fsS | bash

# Windows:
irm 'https://cursor.com/install?win32=true' | iex
Key Files:
  • cli/src/cursor/ - Cursor Agent integration
  • cli/src/cursor/runCursor.ts - Main wrapper

Permission Modes

const CURSOR_PERMISSION_MODES = [
  'default',  // Ask for permission
  'plan',     // Plan mode
  'ask',      // Ask mode
  'yolo'      // Approve everything
] as const

Standard Mode

  • Ask for permission on tool use
hapi cursor

Commands

# Start Cursor session
hapi cursor

# Resume existing session
hapi cursor resume <chatId>

# Continue from last chat
hapi cursor --continue

# Set model
hapi cursor --model gpt-4-turbo
Cursor supports both local and remote modes. In remote mode, it uses agent -p with stream-json output.

Gemini

Overview

Google Gemini via ACP

  • Integrated via Anthropic Code Plugins (ACP)
  • Remote mode only (waits for web/Telegram input)
  • Tool definitions via ACP protocol
  • Same permission modes as Codex

Integration Approach

Gemini runs via the ACP protocol: Key Files:
  • cli/src/agent/ - Multi-agent support
  • cli/src/agent/runners/runAgentSession.ts - ACP session runner

Permission Modes

const GEMINI_PERMISSION_MODES = [
  'default',
  'read-only',
  'safe-yolo',
  'yolo'
] as const
Gemini uses the same permission modes as Codex.

Commands

# Start Gemini session (remote mode only)
hapi gemini

# Gemini waits for messages from hub/web/Telegram
# No local mode available
Gemini runs in remote mode only. It does not accept local terminal input. Use the web UI or Telegram to send messages.

Model Configuration

Gemini model is configured via environment variables:
GEMINI_API_KEY=<key>
GEMINI_MODEL=gemini-1.5-pro

OpenCode

Overview

OpenCode via ACP

  • Integrated via ACP and plugin hooks
  • Local and remote modes supported
  • Plugin system for extensibility
  • Simplified permission modes

Integration Approach

OpenCode uses ACP + a plugin hook system: Key Files:
  • cli/src/opencode/ - OpenCode ACP + hook integration
  • cli/src/opencode/runOpencode.ts - Main runner

Permission Modes

const OPENCODE_PERMISSION_MODES = [
  'default',  // Ask for permission
  'yolo'      // Approve everything
] as const
OpenCode has simplified permission modes compared to other agents.

Commands

# Start OpenCode session
hapi opencode

# OpenCode supports both local and remote modes
# Local mode streams via OpenCode plugins

Agent Registry

The CLI maintains an agent registry for session creation:
// Pseudo-code pattern
const AGENT_REGISTRY = {
  claude: {
    run: runClaude,
    permissionModes: CLAUDE_PERMISSION_MODES,
    modelModes: MODEL_MODES
  },
  codex: {
    run: runCodex,
    permissionModes: CODEX_PERMISSION_MODES,
    modelModes: []
  },
  cursor: {
    run: runCursor,
    permissionModes: CURSOR_PERMISSION_MODES,
    modelModes: []
  },
  gemini: {
    run: runAgentSession,
    permissionModes: GEMINI_PERMISSION_MODES,
    modelModes: []
  },
  opencode: {
    run: runOpencode,
    permissionModes: OPENCODE_PERMISSION_MODES,
    modelModes: []
  }
}

Session Factory Pattern

Each agent follows a common session factory pattern:
1

Create API Client

const api = await ApiClient.create()
2

Get or Create Session

const session = await api.getOrCreateSession({
  tag: workingDir,
  metadata: { flavor: 'claude', ... },
  state: null
})
3

Connect to Hub

const syncClient = api.sessionSyncClient(session)
await syncClient.connect()
4

Start Agent

await runAgent({ session, syncClient, ... })
5

Register RPC Handlers

await syncClient.registerRpcHandler('abort-session', async () => {
  process.exit(0)
})

Permission Mode Validation

The shared package provides functions to validate permission modes per agent:
// From shared/src/modes.ts
function getPermissionModesForFlavor(flavor?: string | null): readonly PermissionMode[] {
  if (flavor === 'codex') {
    return CODEX_PERMISSION_MODES
  }
  if (flavor === 'gemini') {
    return GEMINI_PERMISSION_MODES
  }
  if (flavor === 'opencode') {
    return OPENCODE_PERMISSION_MODES
  }
  if (flavor === 'cursor') {
    return CURSOR_PERMISSION_MODES
  }
  return CLAUDE_PERMISSION_MODES
}

function isPermissionModeAllowedForFlavor(
  mode: PermissionMode,
  flavor?: string | null
): boolean {
  return getPermissionModesForFlavor(flavor).includes(mode)
}
Example:
// Check if 'acceptEdits' is valid for Codex
const valid = isPermissionModeAllowedForFlavor('acceptEdits', 'codex')
// Returns: false (Codex doesn't support acceptEdits)
Always validate permission mode against the session’s flavor before setting. Invalid modes are rejected by the Hub.

Model Mode Validation

Model modes are only supported for Claude:
function getModelModesForFlavor(flavor?: string | null): readonly ModelMode[] {
  if (flavor === 'codex' || flavor === 'gemini' || flavor === 'opencode' || flavor === 'cursor') {
    return []  // No model modes
  }
  return MODEL_MODES  // ['default', 'sonnet', 'opus']
}

function isModelModeAllowedForFlavor(
  mode: ModelMode,
  flavor?: string | null
): boolean {
  return getModelModesForFlavor(flavor).includes(mode)
}

Agent-Specific Session IDs

Each agent stores its own session ID in metadata:
type Metadata = {
  claudeSessionId?: string
  codexSessionId?: string
  geminiSessionId?: string
  opencodeSessionId?: string
  cursorSessionId?: string
  // ...
}
These IDs are used internally by the agents for state tracking and resumption. They’re separate from HAPI’s session ID.

Tool Availability

Each agent exposes different tools:
{
  "tools": [
    "edit_file",
    "run_command",
    "read_file",
    "list_files",
    "search_files"
  ]
}
Tool lists are stored in metadata.tools for reference. They’re populated during session initialization.

Choosing an Agent

Use Claude

Best for:
  • General coding tasks
  • Native desktop integration
  • Plan mode workflows
  • Fine-grained permissions

Use Codex

Best for:
  • OpenAI enthusiasts
  • Custom tool integration
  • Read-only analysis
  • MCP workflows

Use Cursor

Best for:
  • Cursor IDE users
  • Plan/ask mode workflows
  • IDE-integrated sessions

Use Gemini

Best for:
  • Google ecosystem users
  • Remote-only workflows
  • Telegram/web control

Use OpenCode

Best for:
  • Plugin extensibility
  • Custom workflows
  • Simplified permissions

Sessions

Session metadata and state management

How It Works

Session lifecycle and data flow

Architecture

Technical architecture overview

Quick Start

Get started with HAPI

Build docs developers (and LLMs) love