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

FieldTypeDescription
nameStringAgent name for display and logging
versionStringAgent version string
loggerLoggerTracing-based logging infrastructure

Runtime Fields

FieldTypeDescription
runtimeRuntimeTokio runtime for async operations
cancel_tokenCancellationTokenSignal for graceful shutdown

Controller Fields

FieldTypeDescription
controllerArc<LLMController>The LLM controller instance
llm_registryOption<LLMRegistry>Configured LLM providers
tool_definitionsVec<LLMTool>Tool schemas for LLM

Channel Fields

FieldTypeDescription
to_controller_txToControllerTxSend messages to controller
to_controller_rxOption<ToControllerRx>Receive messages (for router)
from_controller_txFromControllerTxSend messages to TUI
from_controller_rxOption<FromControllerRx>Receive messages (for app)

Registry Fields

FieldTypeDescription
user_interaction_registryArc<UserInteractionRegistry>Manages user questions
permission_registryArc<PermissionRegistry>Manages permission requests

TUI Configuration Fields

FieldTypeDescription
conversation_factoryOption<ConversationViewFactory>Factory for chat views
widgets_to_registerVec<Box<dyn Widget>>Custom widgets
layout_templateOption<LayoutTemplate>Layout configuration
key_handlerOption<Box<dyn KeyHandler>>Custom key handling
exit_handlerOption<Box<dyn ExitHandler>>Exit cleanup handler
commandsOption<Vec<Box<dyn SlashCommand>>>Custom commands
command_extensionOption<Box<dyn Any + Send>>Extension data for commands
custom_status_barOption<Box<dyn Widget>>Custom status bar
hide_status_barboolHide 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:

  1. Creates logging infrastructure
  2. Loads LLM configuration from file or environment
  3. Creates Tokio runtime
  4. Creates communication channels
  5. Creates LLMController with event handler
  6. Creates user interaction and permission registries
  7. 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(&registry, 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:

  1. Starts background tasks
  2. Creates App with configuration
  3. Wires up channels and registries
  4. Creates initial session
  5. Runs TUI event loop
  6. 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