How Agent Zero Facilitates Interaction with the Active Shell: Architecture and Implementation
Agent Zero facilitates interaction with the active shell through a stateful CodeExecution tool that manages persistent interactive sessions—local or remote via SSH—streams real-time output, and automatically detects shell prompts to determine command completion.
Agent Zero, an open-source AI agent framework in the agent0ai/agent-zero repository, provides robust shell interaction capabilities that allow AI agents to execute commands in persistent terminal environments. The framework's code execution tool facilitates interaction with the active shell by maintaining stateful sessions across multiple tool invocations, enabling complex multi-step workflows in both local and remote environments.
Core Architecture for Shell Interaction
The shell interaction system centers on three primary data structures defined in python/tools/code_execution_tool.py.
ShellWrap and State Management
The ShellWrap dataclass (lines 31‑35) encapsulates an individual shell session:
@dataclass
class ShellWrap:
id: str
session: InteractiveSession
running: bool = False
The State dataclass (lines 38‑40) maintains a dictionary of active shells and tracks whether SSH mode is enabled:
@dataclass
class State:
shells: dict[str, ShellWrap]
ssh_enabled: bool
These structures persist across tool calls via the agent's data store (_cet_state), allowing sessions to survive multiple AI interactions.
The CodeExecution Tool Class
The CodeExecution class (starting at line 43) orchestrates the entire lifecycle. It handles session initialization, command dispatch, output streaming, and prompt detection using compiled regex patterns (lines 45‑58) that recognize Bash, PowerShell, and container prompts.
Session Lifecycle Management
Agent Zero manages shell sessions through a structured lifecycle that supports both local PTY-based terminals and remote SSH connections.
Creating Local and SSH Sessions (prepare_state)
The prepare_state() method (lines 36‑57) initializes or retrieves existing sessions. When code_exec_ssh_enabled is true, it instantiates an SSHInteractiveSession (python/helpers/shell_ssh.py, lines 11‑70); otherwise, it creates a LocalInteractiveSession (python/helpers/shell_local.py, lines 10‑19).
# Core session creation logic from prepare_state
if self.agent.config.code_exec_ssh_enabled:
shell = SSHInteractiveSession(..., cwd=cwd)
else:
shell = LocalInteractiveSession(cwd=cwd)
shells[session] = ShellWrap(id=session, session=shell, running=False)
await shell.connect()
Both session types implement the InteractiveSession interface, providing a consistent API for connection, command execution, and output retrieval.
Executing Commands (terminal_session)
The terminal_session() method serves as the entry point for all terminal-type executions. It marks the session as running and dispatches the command:
# From terminal_session (lines 98-101)
self.state.shells[session].running = True
await self.state.shells[session].session.send_command(command)
The send_command coroutine writes the command to the underlying PTY (via python/helpers/tty_session.py for local sessions) or SSH channel, clearing prior output buffers to ensure clean output capture.
Streaming and Detecting Output (get_terminal_output)
After command dispatch, get_terminal_output() enters a streaming loop that:
- Retrieves output via
session.read_output(timeout=1, reset_full_output=...) - Cleans the output to strip ANSI codes and null bytes (using
clean_stringin SSH sessions) - Updates the UI log incrementally
- Detects completion via shell prompt patterns or dialog patterns
# Prompt detection logic (lines 90-99)
for pat in self.prompt_patterns:
if pat.search(line.strip()):
# Command completed – mark session idle and return
self.mark_session_idle(session)
When a prompt or user-input dialog is detected, the session is marked idle via mark_session_idle(), allowing subsequent commands to reuse the session or enabling the UI to prompt the user for input.
Session Teardown (reset_terminal)
The reset_terminal() method closes specific sessions while preserving others:
# Reset logic (lines 42-45)
await self.prepare_state(reset=True, session=session)
The underlying close() method terminates the PTY process (via tty_session.TTYSession.kill()) or shuts down the SSH channel (SSHInteractiveSession.close()), ensuring resource cleanup.
Practical Code Examples
Example 1: Run a Local Bash Command
await agent.run_tool(
name="CodeExecution",
args={
"runtime": "terminal",
"code": "ls -la /app",
"session": 0, # creates/uses session 0
"allow_running": False,
},
)
Agent Zero creates a LocalInteractiveSession, sends the command, streams the cleaned output back, and automatically stops when it detects the Bash prompt ($ or #).
Example 2: Open an SSH-Backed Session and Keep It Alive
# First call – initialise SSH session 1
await agent.run_tool(
name="CodeExecution",
args={"runtime": "terminal", "code": "uname -a", "session": 1}
)
# Subsequent call – reuse same SSH session
await agent.run_tool(
name="CodeExecution",
args={"runtime": "terminal", "code": "df -h", "session": 1}
)
Because session: 1 already exists, prepare_state() reuses the existing SSHInteractiveSession, preserving the remote working directory and environment variables across calls.
Example 3: Detect a Dialog and Pause Execution
When a command prompts for confirmation, the tool's dialog patterns trigger an early return with a pause message, allowing the UI to surface a response button.
await agent.run_tool(
name="CodeExecution",
args={"runtime": "terminal", "code": "rm -i important.txt", "session": 0}
)
# Output includes: "Detected dialog prompt, returning output early."
The UI can then ask the user to confirm the deletion before resuming the session.
Key Implementation Files
| File | Role | Location |
|---|---|---|
python/tools/code_execution_tool.py |
Core tool handling session lifecycle, command dispatch, output streaming, prompt/dialog detection. | View on GitHub |
python/helpers/shell_local.py |
Implements a local PTY-based interactive session. | View on GitHub |
python/helpers/shell_ssh.py |
Provides an SSH-based interactive session with robust UTF-8 handling and output cleaning. | View on GitHub |
python/helpers/tty_session.py |
Low-level wrapper around pexpect/PTY for local terminals (used by LocalInteractiveSession). |
View on GitHub |
python/helpers/print_style.py |
UI-friendly colored logging used throughout the execution flow. | View on GitHub |
python/helpers/runtime.py |
Detects platform, provides the terminal executable, and supplies helper functions for development-time calls. | View on GitHub |
These components together give Agent Zero a robust, stateful shell interface that works transparently over local or remote environments, automatically detects completion, and integrates cleanly with the agent's UI and logging infrastructure.
Summary
- Agent Zero facilitates interaction with the active shell through the
CodeExecutiontool, which maintains persistent interactive sessions across multiple AI tool invocations. - The architecture uses a
ShellWrapdataclass to encapsulate session state and aStatedataclass to manage multiple concurrent shells, supporting both local PTY and remote SSH backends. - Session lifecycle methods—
prepare_state(),terminal_session(), andget_terminal_output()—handle connection, command dispatch, and real-time output streaming with automatic prompt detection. - The system detects command completion via regex patterns matching common shell prompts and can pause execution when user input dialogs are detected, enabling interactive workflows.
- All session state persists in the agent's data store (
_cet_state), allowing continuous multi-step operations that preserve environment variables and working directories.
Frequently Asked Questions
How does Agent Zero maintain shell state across multiple commands?
Agent Zero stores active shell sessions in a State dataclass within the agent's persistent data store under the key _cet_state. When a tool is invoked, prepare_state() retrieves this state or creates a new one if SSH configuration changed. This allows the ShellWrap objects—containing the underlying LocalInteractiveSession or SSHInteractiveSession—to persist across multiple AI interactions, preserving environment variables, working directories, and command history.
Can Agent Zero execute commands on remote servers via SSH?
Yes, Agent Zero supports remote shell interaction through the SSHInteractiveSession class defined in python/helpers/shell_ssh.py. When the code_exec_ssh_enabled configuration flag is true, prepare_state() instantiates an SSH session instead of a local PTY. The SSH session handles UTF-8 encoding, output cleaning, and persistent connections, allowing agents to execute commands on remote hosts while maintaining the same session persistence and prompt detection capabilities as local sessions.
How does Agent Zero know when a command has finished executing?
Agent Zero detects command completion through regex pattern matching in the get_terminal_output() method. The tool compiles a set of prompt_patterns (lines 45‑58 in python/tools/code_execution_tool.py) that match common shell prompts such as Bash ($ or #), PowerShell, and container environments. As output streams in, each line is checked against these patterns; when a match is found, the session is marked idle via mark_session_idle(), signaling that the command has completed and the output is final.
What happens when a command prompts for user input?
When a command generates an interactive dialog (e.g., rm -i asking for confirmation), Agent Zero's dialog_patterns detect the pause in execution. Rather than hanging indefinitely, get_terminal_output() returns early with the partial output and a pause indicator, allowing the UI layer to surface a response mechanism to the user. Once the user provides input, the agent can resume the session and continue execution, maintaining the interactive workflow without losing session state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →