> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/ComposioHQ/agent-orchestrator/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent

> Plugin interface for AI coding agent adapters (Claude Code, Codex, Aider, OpenCode) with launch, activity detection, and session info

## Overview

The `Agent` interface defines how the orchestrator interacts with specific AI coding tools. Agent plugins know how to launch agents, detect their activity state, extract session information, and handle restoration.

**Plugin Slot:** `agent`\
**Default Plugin:** `claude-code`

## Interface Definition

```typescript theme={null}
export interface Agent {
  readonly name: string;
  readonly processName: string;
  readonly promptDelivery?: "inline" | "post-launch";
  
  getLaunchCommand(config: AgentLaunchConfig): string;
  getEnvironment(config: AgentLaunchConfig): Record<string, string>;
  detectActivity(terminalOutput: string): ActivityState;
  getActivityState(session: Session, readyThresholdMs?: number): Promise<ActivityDetection | null>;
  isProcessRunning(handle: RuntimeHandle): Promise<boolean>;
  getSessionInfo(session: Session): Promise<AgentSessionInfo | null>;
  getRestoreCommand?(session: Session, project: ProjectConfig): Promise<string | null>;
  postLaunchSetup?(session: Session): Promise<void>;
  setupWorkspaceHooks?(workspacePath: string, config: WorkspaceHooksConfig): Promise<void>;
}
```

## Properties

<ResponseField name="name" type="string" required>
  Plugin name identifier (e.g. `"claude-code"`, `"codex"`, `"aider"`).
</ResponseField>

<ResponseField name="processName" type="string" required>
  Process name to look for when checking if agent is running (e.g. `"claude"`, `"codex"`, `"aider"`).
</ResponseField>

<ResponseField name="promptDelivery" type="'inline' | 'post-launch'">
  How the initial prompt should be delivered:

  * `"inline"` (default): prompt included in launch command (e.g. `-p` flag)
  * `"post-launch"`: prompt sent via `runtime.sendMessage()` after agent starts

  Use `"post-launch"` for agents where inlining the prompt causes one-shot/exit behavior.
</ResponseField>

## Methods

<ResponseField name="getLaunchCommand" type="(config: AgentLaunchConfig) => string" required>
  Get the shell command to launch this agent.

  **Parameters:**

  * `config` - Launch configuration with session ID, project config, issue, prompt, model, permissions, system prompt

  **Returns:** Shell command string
</ResponseField>

<ResponseField name="getEnvironment" type="(config: AgentLaunchConfig) => Record<string, string>" required>
  Get environment variables for the agent process.

  **Parameters:**

  * `config` - Launch configuration

  **Returns:** Key-value map of environment variables
</ResponseField>

<ResponseField name="detectActivity" type="(terminalOutput: string) => ActivityState" required>
  **Deprecated:** Detect agent activity from terminal output (hacky, use `getActivityState()` instead).

  **Parameters:**

  * `terminalOutput` - Recent terminal output from runtime

  **Returns:** ActivityState
</ResponseField>

<ResponseField name="getActivityState" type="(session: Session, readyThresholdMs?: number) => Promise<ActivityDetection | null>" required>
  Get current activity state using agent-native mechanism (JSONL, SQLite, etc.). This is the preferred method.

  **Parameters:**

  * `session` - Session to check
  * `readyThresholdMs` - Milliseconds before "ready" becomes "idle" (default: 300000 = 5 min)

  **Returns:** ActivityDetection with state and timestamp, or null if cannot determine
</ResponseField>

<ResponseField name="isProcessRunning" type="(handle: RuntimeHandle) => Promise<boolean>" required>
  Check if agent process is running given a runtime handle.

  **Parameters:**

  * `handle` - Runtime handle from session

  **Returns:** true if process is running, false otherwise
</ResponseField>

<ResponseField name="getSessionInfo" type="(session: Session) => Promise<AgentSessionInfo | null>" required>
  Extract information from agent's internal data (summary, cost, session ID).

  **Parameters:**

  * `session` - Session to extract info from

  **Returns:** AgentSessionInfo or null if not available
</ResponseField>

<ResponseField name="getRestoreCommand" type="(session: Session, project: ProjectConfig) => Promise<string | null>">
  Optional: Get a launch command that resumes a previous session.

  **Parameters:**

  * `session` - Session to restore
  * `project` - Project configuration

  **Returns:** Command string or null if no previous session found (falls back to `getLaunchCommand()`)
</ResponseField>

<ResponseField name="postLaunchSetup" type="(session: Session) => Promise<void>">
  Optional: Run setup after agent is launched (e.g. configure MCP servers).

  **Parameters:**

  * `session` - Newly launched session
</ResponseField>

<ResponseField name="setupWorkspaceHooks" type="(workspacePath: string, config: WorkspaceHooksConfig) => Promise<void>">
  Optional: Set up agent-specific hooks/config in the workspace for automatic metadata updates.

  Called once per workspace during `ao init`/`start` and when creating new worktrees.

  **Critical:** The dashboard depends on metadata being auto-updated when agents run git/gh commands. Without this, PRs created by agents never show up.

  **Parameters:**

  * `workspacePath` - Path to workspace
  * `config` - Hooks configuration with data directory and optional session ID
</ResponseField>

## Related Types

### AgentLaunchConfig

```typescript theme={null}
export interface AgentLaunchConfig {
  sessionId: SessionId;
  projectConfig: ProjectConfig;
  issueId?: string;
  prompt?: string;
  permissions?: "skip" | "default";
  model?: string;
  systemPrompt?: string;
  systemPromptFile?: string;
}
```

<ParamField path="sessionId" type="SessionId" required>
  Session identifier
</ParamField>

<ParamField path="projectConfig" type="ProjectConfig" required>
  Project configuration from orchestrator config
</ParamField>

<ParamField path="issueId" type="string">
  Issue ID if working on a specific issue
</ParamField>

<ParamField path="prompt" type="string">
  Initial prompt for the agent
</ParamField>

<ParamField path="permissions" type="'skip' | 'default'">
  Permission handling mode
</ParamField>

<ParamField path="model" type="string">
  Model to use (e.g. `"claude-sonnet-4-20250514"`)
</ParamField>

<ParamField path="systemPrompt" type="string">
  System prompt for orchestrator context (short prompts only). Passed via:

  * Claude Code: `--append-system-prompt`
  * Codex: `--system-prompt` or AGENTS.md
  * Aider: `--system-prompt` flag
</ParamField>

<ParamField path="systemPromptFile" type="string">
  Path to file containing system prompt. Preferred for long prompts (e.g. orchestrator prompts) to avoid shell truncation. Takes precedence over `systemPrompt`.
</ParamField>

### AgentSessionInfo

```typescript theme={null}
export interface AgentSessionInfo {
  summary: string | null;
  summaryIsFallback?: boolean;
  agentSessionId: string | null;
  cost?: CostEstimate;
}
```

<ParamField path="summary" type="string | null" required>
  Agent's auto-generated summary of what it's working on
</ParamField>

<ParamField path="summaryIsFallback" type="boolean">
  True when summary is a fallback (e.g. truncated first user message), not a real agent summary
</ParamField>

<ParamField path="agentSessionId" type="string | null" required>
  Agent's internal session ID for resume
</ParamField>

<ParamField path="cost" type="CostEstimate">
  Cost estimate (tokens and USD)
</ParamField>

### ActivityDetection

```typescript theme={null}
export interface ActivityDetection {
  state: ActivityState;
  timestamp?: Date;
}
```

<ParamField path="state" type="ActivityState" required>
  Current activity state
</ParamField>

<ParamField path="timestamp" type="Date">
  When activity was last observed (e.g. agent log file mtime)
</ParamField>

### WorkspaceHooksConfig

```typescript theme={null}
export interface WorkspaceHooksConfig {
  dataDir: string;
  sessionId?: string;
}
```

<ParamField path="dataDir" type="string" required>
  Data directory where session metadata files are stored
</ParamField>

<ParamField path="sessionId" type="string">
  Optional session ID (may not be known at `ao init` time)
</ParamField>

### CostEstimate

```typescript theme={null}
export interface CostEstimate {
  inputTokens: number;
  outputTokens: number;
  estimatedCostUsd: number;
}
```

## Usage Examples

### Implementing an Agent Plugin

```typescript theme={null}
import type { Agent, AgentLaunchConfig, ActivityDetection } from "@composio/ao-core";
import { readFile } from "node:fs/promises";
import { join } from "node:path";

export function create(): Agent {
  return {
    name: "claude-code",
    processName: "claude",
    promptDelivery: "post-launch",
    
    getLaunchCommand(config: AgentLaunchConfig): string {
      const { projectConfig, model, permissions, systemPromptFile } = config;
      
      let cmd = `claude --project ${projectConfig.path}`;
      
      if (model) {
        cmd += ` --model ${model}`;
      }
      
      if (permissions === "skip") {
        cmd += " --skip-permissions";
      }
      
      if (systemPromptFile) {
        cmd += ` --append-system-prompt "$(cat ${systemPromptFile})"`;
      }
      
      return cmd;
    },
    
    getEnvironment(config: AgentLaunchConfig): Record<string, string> {
      return {
        ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY || "",
        SESSION_ID: config.sessionId
      };
    },
    
    detectActivity(terminalOutput: string): ActivityState {
      // Legacy method - prefer getActivityState()
      if (terminalOutput.includes("Thinking...")) return "active";
      if (terminalOutput.includes("Reply, or press Ctrl+C to exit")) return "ready";
      return "idle";
    },
    
    async getActivityState(session: Session, readyThresholdMs = 300_000): Promise<ActivityDetection | null> {
      if (!session.workspacePath) return null;
      
      const eventsPath = join(session.workspacePath, ".claude", "events.jsonl");
      
      try {
        const content = await readFile(eventsPath, "utf-8");
        const lines = content.trim().split("\n");
        const lastEvent = JSON.parse(lines[lines.length - 1]);
        
        const timestamp = new Date(lastEvent.timestamp);
        const age = Date.now() - timestamp.getTime();
        
        let state: ActivityState;
        if (lastEvent.type === "thinking") {
          state = "active";
        } else if (lastEvent.type === "user_input_request") {
          state = "waiting_input";
        } else if (age > readyThresholdMs) {
          state = "idle";
        } else {
          state = "ready";
        }
        
        return { state, timestamp };
      } catch {
        return null;
      }
    },
    
    async isProcessRunning(handle: RuntimeHandle): Promise<boolean> {
      const { stdout } = await execFileAsync("pgrep", ["-f", "claude"], {
        timeout: 5_000
      }).catch(() => ({ stdout: "" }));
      return stdout.trim().length > 0;
    },
    
    async getSessionInfo(session: Session): Promise<AgentSessionInfo | null> {
      if (!session.workspacePath) return null;
      
      const dbPath = join(session.workspacePath, ".claude", "session.db");
      // Read from SQLite database
      // ...
      
      return {
        summary: "Fix authentication bug",
        summaryIsFallback: false,
        agentSessionId: "session-123",
        cost: {
          inputTokens: 15000,
          outputTokens: 8000,
          estimatedCostUsd: 0.45
        }
      };
    }
  };
}
```

### Using Agent in Session Manager

```typescript theme={null}
import type { Agent, AgentLaunchConfig } from "@composio/ao-core";

const agent: Agent = registry.get("agent", "claude-code");

const launchConfig: AgentLaunchConfig = {
  sessionId: "my-app-1",
  projectConfig: project,
  prompt: "Fix the authentication bug",
  model: "claude-sonnet-4-20250514",
  permissions: "default"
};

const command = agent.getLaunchCommand(launchConfig);
const env = agent.getEnvironment(launchConfig);

// Create runtime with agent command
const handle = await runtime.create({
  sessionId: launchConfig.sessionId,
  workspacePath: "/workspace/my-app-1",
  launchCommand: command,
  environment: env
});

// If post-launch prompt delivery
if (agent.promptDelivery === "post-launch" && launchConfig.prompt) {
  await runtime.sendMessage(handle, launchConfig.prompt);
}
```

## Implementation Notes

### Prompt Delivery

For agents where `-p "prompt"` causes immediate exit after completion:

* Set `promptDelivery: "post-launch"`
* Launch agent in interactive mode
* Send prompt via `runtime.sendMessage()` after launch

### Activity Detection

Prefer `getActivityState()` over `detectActivity()`:

* Uses agent-native mechanisms (JSONL logs, SQLite)
* More reliable than terminal output parsing
* Provides timestamps for staleness detection

### Workspace Hooks

Critical for dashboard functionality:

* Claude Code: write `.claude/settings.json` with PostToolUse hook
* Hook updates metadata when agent runs `git`/`gh` commands
* Without hooks, PRs created by agents won't appear in dashboard

### Built-in Plugins

* **claude-code** - Claude Code (default)
* **codex** - Codex
* **aider** - Aider
* **opencode** - OpenCode

## See Also

* [Runtime](/api/runtime) - Runtime execution environment interface
* [Session](/api/session) - Session interface
* [Workspace](/api/workspace) - Workspace isolation interface


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.