Skip to main content

Overview

The Terminal interface manages how humans view and interact with running sessions. Terminal plugins open IDE tabs, browser windows, or terminal sessions for direct agent interaction. Plugin Slot: terminal
Default Plugin: iterm2

Interface Definition

export interface Terminal {
  readonly name: string;
  
  openSession(session: Session): Promise<void>;
  openAll(sessions: Session[]): Promise<void>;
  isSessionOpen?(session: Session): Promise<boolean>;
}

Methods

name
string
required
Plugin name identifier (e.g. "iterm2", "web", "none").
openSession
(session: Session) => Promise<void>
required
Open a session for human interaction.Parameters:
  • session - Session to open
Implementation:
  • iTerm2: Create new tab with tmux attach command
  • Web: Open browser to web terminal URL
  • None: No-op (headless mode)
openAll
(sessions: Session[]) => Promise<void>
required
Open all sessions for a project.Parameters:
  • sessions - Array of sessions to open
Implementation:
  • iTerm2: Create window with multiple tabs
  • Web: Open browser with multiple tabs
  • None: No-op
isSessionOpen
(session: Session) => Promise<boolean>
Optional: Check if a session is already open in a tab/window.Parameters:
  • session - Session to check
Returns: true if session is open, false otherwise

Usage Examples

Implementing a Terminal Plugin (iTerm2)

import type { Terminal, Session } from "@composio/ao-core";
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);

export function create(): Terminal {
  return {
    name: "iterm2",
    
    async openSession(session: Session): Promise<void> {
      if (!session.runtimeHandle) {
        throw new Error("Cannot open session: no runtime handle");
      }
      
      const tmuxSession = session.runtimeHandle.id;
      
      // AppleScript to create iTerm2 tab with tmux attach
      const script = `
        tell application "iTerm"
          activate
          tell current window
            create tab with default profile
            tell current session
              write text "tmux attach -t ${tmuxSession}"
            end tell
          end tell
        end tell
      `;
      
      await execFileAsync("osascript", ["-e", script], { timeout: 5_000 });
    },
    
    async openAll(sessions: Session[]): Promise<void> {
      if (sessions.length === 0) return;
      
      // Create window with tabs for each session
      const tmuxSessions = sessions
        .map(s => s.runtimeHandle?.id)
        .filter(Boolean);
      
      if (tmuxSessions.length === 0) return;
      
      const script = `
        tell application "iTerm"
          activate
          create window with default profile
          tell current window
            ${tmuxSessions.map((id, i) => `
              ${i > 0 ? 'create tab with default profile' : ''}
              tell current session
                write text "tmux attach -t ${id}"
              end tell
            `).join('')}
          end tell
        end tell
      `;
      
      await execFileAsync("osascript", ["-e", script], { timeout: 10_000 });
    },
    
    async isSessionOpen(session: Session): Promise<boolean> {
      // Check if iTerm has a tab attached to this tmux session
      // This is complex - simplified implementation returns false
      return false;
    }
  };
}

Implementing a Web Terminal Plugin

import type { Terminal, Session } from "@composio/ao-core";
import { execFile } from "node:child_process";
import { promisify } from "node:util";

const execFileAsync = promisify(execFile);

export function create(config: { baseUrl: string }): Terminal {
  return {
    name: "web",
    
    async openSession(session: Session): Promise<void> {
      const url = `${config.baseUrl}/terminal/${session.id}`;
      
      // Open in browser
      await execFileAsync("open", [url], { timeout: 5_000 });
    },
    
    async openAll(sessions: Session[]): Promise<void> {
      // Open browser with multiple tabs
      const urls = sessions.map(s => `${config.baseUrl}/terminal/${s.id}`);
      
      for (const url of urls) {
        await execFileAsync("open", [url], { timeout: 5_000 });
      }
    }
  };
}

Using Terminal in CLI

import type { Terminal } from "@composio/ao-core";

const terminal: Terminal = registry.get("terminal", "iterm2");

// Open single session
await terminal.openSession(session);

// Open all sessions for project
const sessions = await sessionManager.list("my-app");
await terminal.openAll(sessions);

Implementation Notes

Runtime Integration

Terminal plugins should use runtime.getAttachInfo() to get connection details:
import type { AttachInfo } from "@composio/ao-core";

const runtime = registry.get("runtime", session.runtimeHandle!.runtimeName);
const attachInfo: AttachInfo | undefined = await runtime.getAttachInfo?.(session.runtimeHandle!);

if (attachInfo) {
  switch (attachInfo.type) {
    case "tmux":
      // Open iTerm2 tab with tmux attach
      break;
    case "docker":
      // Run docker exec in terminal
      break;
    case "web":
      // Open browser to URL
      break;
  }
}

Platform Detection

Terminal plugins should check platform:
if (process.platform === "darwin") {
  // Use iTerm2 or Terminal.app
} else if (process.platform === "linux") {
  // Use gnome-terminal, konsole, or xterm
} else {
  // Fallback to web terminal
}

Session Deduplication

The isSessionOpen() method helps avoid opening duplicate tabs:
if (terminal.isSessionOpen && await terminal.isSessionOpen(session)) {
  console.log("Session already open");
  return;
}

await terminal.openSession(session);

Built-in Plugins

  • iterm2 - iTerm2 on macOS (default)
  • web - Web-based terminal (browser)
  • none - No-op for headless/CI environments
Future plugins could support:
  • terminal-app - macOS Terminal.app
  • gnome-terminal - GNOME Terminal on Linux
  • konsole - KDE Konsole on Linux
  • vscode - VS Code integrated terminal
  • warp - Warp terminal

See Also

  • Runtime - Runtime execution environment interface
  • Session - Session interface
  • Notifier - Notification interface

Build docs developers (and LLMs) love