AgentAir Struct
AgentAir is the central orchestration struct in agent-air. It initializes all components, manages the Tokio runtime, coordinates background tasks, and provides the main entry point for running an agent.
Overview
AgentAir provides a complete, working agent infrastructure out of the box:
use agent_air::agent::{AgentAir, AgentConfig};
struct MyConfig;
impl AgentConfig for MyConfig {
fn config_path(&self) -> &str { ".myagent/config.yaml" }
fn default_system_prompt(&self) -> &str { "You are helpful." }
fn log_prefix(&self) -> &str { "myagent" }
fn name(&self) -> &str { "MyAgent" }
}
fn main() -> std::io::Result<()> {
let mut core = AgentAir::new(&MyConfig)?;
core.run()
}
Struct Definition
pub struct AgentAir {
// Logging
logger: Logger,
// Identity
name: String,
version: String,
// TUI Configuration
conversation_factory: Option<ConversationViewFactory>,
widgets_to_register: Vec<Box<dyn Widget>>,
layout_template: Option<LayoutTemplate>,
key_handler: Option<Box<dyn KeyHandler>>,
exit_handler: Option<Box<dyn ExitHandler>>,
commands: Option<Vec<Box<dyn SlashCommand>>>,
command_extension: Option<Box<dyn std::any::Any + Send>>,
custom_status_bar: Option<Box<dyn Widget>>,
hide_status_bar: bool,
// Runtime
runtime: Runtime,
// Controller
controller: Arc<LLMController>,
llm_registry: Option<LLMRegistry>,
tool_definitions: Vec<LLMTool>,
// Channels
to_controller_tx: ToControllerTx,
to_controller_rx: Option<ToControllerRx>,
from_controller_tx: FromControllerTx,
from_controller_rx: Option<FromControllerRx>,
// Shutdown
cancel_token: CancellationToken,
// Registries
user_interaction_registry: Arc<UserInteractionRegistry>,
permission_registry: Arc<PermissionRegistry>,
}
Field Categories
Identity Fields
| Field | Type | Description |
|---|---|---|
name | String | Agent name for display and logging |
version | String | Agent version string |
logger | Logger | Tracing-based logging infrastructure |
Runtime Fields
| Field | Type | Description |
|---|---|---|
runtime | Runtime | Tokio runtime for async operations |
cancel_token | CancellationToken | Signal for graceful shutdown |
Controller Fields
| Field | Type | Description |
|---|---|---|
controller | Arc<LLMController> | The LLM controller instance |
llm_registry | Option<LLMRegistry> | Configured LLM providers |
tool_definitions | Vec<LLMTool> | Tool schemas for LLM |
Channel Fields
| Field | Type | Description |
|---|---|---|
to_controller_tx | ToControllerTx | Send messages to controller |
to_controller_rx | Option<ToControllerRx> | Receive messages (for router) |
from_controller_tx | FromControllerTx | Send messages to TUI |
from_controller_rx | Option<FromControllerRx> | Receive messages (for app) |
Registry Fields
| Field | Type | Description |
|---|---|---|
user_interaction_registry | Arc<UserInteractionRegistry> | Manages user questions |
permission_registry | Arc<PermissionRegistry> | Manages permission requests |
TUI Configuration Fields
| Field | Type | Description |
|---|---|---|
conversation_factory | Option<ConversationViewFactory> | Factory for chat views |
widgets_to_register | Vec<Box<dyn Widget>> | Custom widgets |
layout_template | Option<LayoutTemplate> | Layout configuration |
key_handler | Option<Box<dyn KeyHandler>> | Custom key handling |
exit_handler | Option<Box<dyn ExitHandler>> | Exit cleanup handler |
commands | Option<Vec<Box<dyn SlashCommand>>> | Custom commands |
command_extension | Option<Box<dyn Any + Send>> | Extension data for commands |
custom_status_bar | Option<Box<dyn Widget>> | Custom status bar |
hide_status_bar | bool | Hide default status bar |
Constructor
new()
Creates a new AgentAir with the given configuration:
pub fn new<C: AgentConfig>(config: &C) -> io::Result<Self>
Initialization steps:
- Creates logging infrastructure
- Loads LLM configuration from file or environment
- Creates Tokio runtime
- Creates communication channels
- Creates LLMController with event handler
- Creates user interaction and permission registries
- Spawns event forwarding tasks
let mut core = AgentAir::new(&MyConfig)?;
Configuration Methods
set_version()
Sets the agent version for display:
pub fn set_version(&mut self, version: impl Into<String>)
core.set_version(env!("CARGO_PKG_VERSION"));
set_conversation_factory()
Sets a factory for creating conversation views:
pub fn set_conversation_factory<F>(&mut self, factory: F) -> &mut Self
where
F: Fn() -> Box<dyn ConversationView> + Send + Sync + 'static
core.set_conversation_factory(|| {
Box::new(ChatView::new()
.with_title("My Agent")
.with_initial_content(welcome_renderer))
});
set_layout()
Sets the layout template for the TUI:
pub fn set_layout(&mut self, template: LayoutTemplate) -> &mut Self
core.set_layout(LayoutTemplate::standard());
core.set_layout(LayoutTemplate::with_sidebar("files", 30));
core.set_layout(LayoutTemplate::minimal());
set_key_handler()
Sets a custom key handler:
pub fn set_key_handler<H: KeyHandler>(&mut self, handler: H) -> &mut Self
core.set_key_handler(VimKeyHandler::new());
set_key_bindings()
Sets key bindings using the default handler:
pub fn set_key_bindings(&mut self, bindings: KeyBindings) -> &mut Self
core.set_key_bindings(KeyBindings::emacs());
core.set_key_bindings(KeyBindings::minimal());
set_exit_handler()
Sets a handler for cleanup before exit:
pub fn set_exit_handler<H: ExitHandler>(&mut self, handler: H) -> &mut Self
core.set_exit_handler(SaveOnExitHandler::new());
set_commands()
Sets the slash commands:
pub fn set_commands(&mut self, commands: Vec<Box<dyn SlashCommand>>) -> &mut Self
core.set_commands(
CommandRegistry::with_defaults()
.add(CustomCommand::new("deploy", "Deploy app", handler))
.remove("quit")
.build()
);
set_command_extension()
Sets extension data available to commands:
pub fn set_command_extension<T: Any + Send + 'static>(&mut self, ext: T) -> &mut Self
core.set_command_extension(MyContext { api_key: "secret".to_string() });
set_status_bar()
Sets a custom status bar widget:
pub fn set_status_bar<W: Widget>(&mut self, status_bar: W) -> &mut Self
hide_status_bar()
Hides the default status bar:
pub fn hide_status_bar(&mut self) -> &mut Self
Tool Registration
register_tools()
Registers tools with the agent:
pub fn register_tools<F>(&mut self, f: F) -> Result<(), AgentError>
where
F: FnOnce(
&Arc<ToolRegistry>,
&Arc<UserInteractionRegistry>,
&Arc<PermissionRegistry>,
) -> Result<Vec<LLMTool>, String>
core.register_tools(|registry, user_reg, perm_reg| {
tools::register_all_tools(registry, user_reg, perm_reg)
})?;
register_tools_async()
Registers tools using an async function:
pub fn register_tools_async<F, Fut>(&mut self, f: F) -> Result<(), AgentError>
where
F: FnOnce(Arc<ToolRegistry>, Arc<UserInteractionRegistry>, Arc<PermissionRegistry>) -> Fut,
Fut: Future<Output = Result<Vec<LLMTool>, String>>
core.register_tools_async(|registry, user_reg, perm_reg| async move {
tools::register_all_tools(®istry, user_reg, perm_reg).await
})?;
Widget Registration
register_widget()
Registers a widget with the TUI:
pub fn register_widget<W: Widget>(&mut self, widget: W) -> &mut Self
core.register_widget(PermissionPanel::new());
core.register_widget(QuestionPanel::new());
Session Management
create_initial_session()
Creates the first session using the default provider:
pub fn create_initial_session(&mut self) -> Result<(i64, String, i32), AgentError>
Returns (session_id, model_name, context_limit).
create_session()
Creates a session with specific configuration:
pub fn create_session(&self, config: LLMSessionConfig) -> Result<i64, AgentError>
let session_id = core.create_session(LLMSessionConfig {
model: "claude-3-opus".to_string(),
system_prompt: Some("You are a code reviewer.".to_string()),
..Default::default()
})?;
Lifecycle Methods
start_background_tasks()
Starts controller and router as background tasks:
pub fn start_background_tasks(&mut self)
This is called automatically by run(), but can be called manually for custom TUI integration.
run()
The main entry point that runs the agent:
pub fn run(&mut self) -> io::Result<()>
Steps performed:
- Starts background tasks
- Creates App with configuration
- Wires up channels and registries
- Creates initial session
- Runs TUI event loop
- Shuts down on exit
fn main() -> io::Result<()> {
let mut core = AgentAir::new(&MyConfig)?;
core.register_tools(register_my_tools)?;
core.register_widget(PermissionPanel::new());
core.run()
}
shutdown()
Signals shutdown to all background tasks:
pub fn shutdown(&self)
Called automatically by run() when the TUI exits.
Accessors
Channel Accessors
pub fn to_controller_tx(&self) -> ToControllerTx
pub fn take_from_controller_rx(&mut self) -> Option<FromControllerRx>
pub fn from_controller_tx(&self) -> FromControllerTx
Component Accessors
pub fn controller(&self) -> &Arc<LLMController>
pub fn runtime(&self) -> &Runtime
pub fn runtime_handle(&self) -> tokio::runtime::Handle
pub fn user_interaction_registry(&self) -> &Arc<UserInteractionRegistry>
pub fn permission_registry(&self) -> &Arc<PermissionRegistry>
pub fn llm_registry(&self) -> Option<&LLMRegistry>
pub fn take_llm_registry(&mut self) -> Option<LLMRegistry>
pub fn cancel_token(&self) -> CancellationToken
pub fn name(&self) -> &str
Usage Patterns
Minimal Agent
fn main() -> io::Result<()> {
let mut core = AgentAir::new(&MyConfig)?;
core.run()
}
Agent with Tools
fn main() -> io::Result<()> {
let mut core = AgentAir::new(&MyConfig)?;
core.register_tools(|reg, user_reg, perm_reg| {
register_file_tools(reg)?;
register_search_tools(reg)?;
Ok(tool_definitions())
})?;
core.run()
}
Fully Customized Agent
fn main() -> io::Result<()> {
let mut core = AgentAir::new(&MyConfig)?;
// Set version
core.set_version(env!("CARGO_PKG_VERSION"));
// Configure TUI
core.set_layout(LayoutTemplate::with_sidebar("files", 30));
core.set_key_bindings(KeyBindings::vim());
core.set_exit_handler(SaveSessionHandler::new());
core.set_conversation_factory(|| Box::new(CustomChatView::new()));
// Register tools
core.register_tools(register_all_tools)?;
// Register widgets
core.register_widget(PermissionPanel::new());
core.register_widget(QuestionPanel::new());
core.register_widget(FileBrowserWidget::new());
// Set commands
core.set_commands(custom_commands());
core.run()
}
Custom TUI Integration
For integrating with a custom TUI instead of using run():
fn main() -> io::Result<()> {
let mut core = AgentAir::new(&MyConfig)?;
// Get channels for custom TUI
let to_controller = core.to_controller_tx();
let from_controller = core.take_from_controller_rx();
// Start background tasks
core.start_background_tasks();
// Create initial session
let (session_id, model, limit) = core.create_initial_session()?;
// Run custom TUI
my_custom_tui::run(to_controller, from_controller, session_id)?;
// Shutdown
core.shutdown();
Ok(())
}
Next Steps
- Controller Events - Events emitted by the controller
- UiMessage Types - Messages sent to the TUI
- Message Handling - The controller event loop
