saorsa-agent
Agent runtime with tool execution, session management, context engineering, and extension system for building AI coding agents.
Overview
saorsa-agent provides the runtime for AI agents that can execute tools, manage sessions, and integrate with terminal UIs. It builds on saorsa-ai for LLM communication and adds:
- Agent loop - Turn-based conversation with streaming, tool execution, and automatic continuation
- 7 built-in tools - bash, read, write, edit, grep, find, ls
- Session management - Tree-structured sessions with branching, forking, auto-save, and resume
- Context engineering - AGENTS.md/SYSTEM.md discovery, context compaction, merge strategies
- Skills system - On-demand capability injection from markdown files
- Templates - Prompt templates with variable substitution and conditionals
- Extension system - Lifecycle hooks, custom tools, commands, keybindings, and widgets
- Event system - Typed events for UI integration (text deltas, tool calls, turn lifecycle)
Quick Start
[]
= "0.1"
= "0.1"
= { = "1", = ["full"] }
Note: saorsa-agent is provider-agnostic. Any Box<dyn saorsa_ai::StreamingProvider> works,
including in-process providers like saorsa_ai::MistralrsProvider (feature-gated behind
saorsa-ai's mistralrs feature).
Running the Agent Loop
use ;
use ;
async
Agent Loop
The AgentLoop is the core runtime. It sends messages to an LLM, streams responses, executes tool calls, and loops until the model stops or the turn limit is reached.
Turn Lifecycle
- TurnStart - Begin a new turn
- Stream response - Receive text deltas and tool call fragments
- TextComplete - Full text assembled
- Tool execution - If
StopReason::ToolUse, execute tools and add results to history - TurnEnd - Turn complete, loop if more tools needed
Configuration
use AgentConfig;
let config = new
.system_prompt
.max_turns // Maximum tool-use turns per run()
.max_tokens; // Max output tokens per completion
Defaults:
| Setting | Default |
|---|---|
model |
claude-sonnet-4-5-20250929 |
system_prompt |
"You are a helpful assistant." |
max_turns |
10 |
max_tokens |
4096 |
Built-in Tools
Tool Trait
All tools implement the async Tool trait:
Tool Registry
use ;
// Create registry with all 7 built-in tools
let tools = default_tools;
assert_eq!;
// Or build a custom registry
let mut registry = new;
registry.register;
Bash Tool
Execute shell commands with timeout and output limits.
| Limit | Value |
|---|---|
| Default timeout | 120 seconds |
| Max output | 100 KB |
| Shell | /bin/bash -c |
Captures both stdout and stderr. Output is truncated at safe UTF-8 boundaries if it exceeds the limit.
Read Tool
Read file contents with optional line ranges.
| Feature | Detail |
|---|---|
| Line ranges | 10-20, 5- (from line 5), -10 (first 10 lines) |
| Max file size | 10 MB |
| Line numbers | Output includes N: content format |
Write Tool
Write content to files with automatic directory creation and diff display.
Creates parent directories automatically. Shows a unified diff when updating existing files. Reports "No changes" if content is identical.
Edit Tool
Surgical text replacement with ambiguity detection.
| Behavior | Detail |
|---|---|
| Single match | Replaces the one occurrence |
| Multiple matches | Returns error with match count unless replace_all: true |
| No match | Returns error with the search text |
Grep Tool
Search file contents with regex patterns.
| Feature | Detail |
|---|---|
| Pattern | Rust regex syntax |
| Scope | Recursive directory search |
| Output | file:line: content format |
| Limit | 100 matches max |
Find Tool
Find files by glob pattern.
| Feature | Detail |
|---|---|
| Pattern | Glob syntax (*.rs, test_?.log, **/*.toml) |
| Limit | 100 files max |
Ls Tool
List directory contents with metadata.
| Feature | Detail |
|---|---|
| Output format | TYPE SIZE NAME per entry |
| Types | FILE, DIR, LNK |
| Size format | Human-readable (B, KB, MB, GB) |
Event System
The agent emits typed events for UI integration:
Events are delivered via a tokio mpsc channel:
let = event_channel;
let mut agent = new;
// UI task reads events
while let Some = rx.recv.await
Session Management
Session Storage
Sessions are persisted to disk in a structured format:
~/.saorsa/sessions/
<session-uuid>/
manifest.json # SessionMetadata (title, tags, timestamps)
tree.json # SessionNode (parent/child relationships)
messages/
0-user.json # Chronological message files
1-assistant.json
2-tool_call.json
3-tool_result.json
use ;
let storage = new?;
let id = new;
// Save/load metadata
storage.save_manifest?;
let metadata = storage.load_manifest?;
// Save/load messages
storage.save_message?;
let messages = storage.load_messages?;
Tree-Structured Sessions
Sessions form a tree: forking creates a child session that shares history up to the fork point.
use ;
// Fork from an existing session
let child_id = fork_session?;
// Build and render the session tree
let tree = build_session_tree?;
let output = render_tree;
println!;
Resume & Find
use ;
// Resume the most recent session
let id = find_last_active_session?;
let messages = restore_session?;
// Find by 8-character prefix
let id = find_session_by_prefix?;
Auto-Save
Sessions auto-save with debouncing and atomic writes (temp file + rename):
// Auto-fork when editing a message mid-conversation
let forked = auto_fork_on_edit?;
Bookmarks
use ;
let mut bookmarks = new;
bookmarks.add?;
Export
use export_to_html;
let html = export_to_html?;
write?;
Context Engineering
AGENTS.md / SYSTEM.md Discovery
The agent searches for context files in precedence order:
- Current working directory (highest precedence)
- Parent directories (walking up to root/home)
~/.saorsa/(global, lowest precedence)
use ContextDiscovery;
let discovery = new?;
// Find all AGENTS.md files (highest precedence first)
let agents_files = discovery.discover_agents_md;
// Find all SYSTEM.md files
let system_files = discovery.discover_system_md;
Context Bundle
Combine discovered context into a single bundle:
use ContextBundle;
let context = builder
.agents // From AGENTS.md
.system // From SYSTEM.md
.user // Ad-hoc context
.build;
SYSTEM.md Modes
| Mode | Behavior |
|---|---|
SystemMode::Replace |
Replace the default system prompt entirely |
SystemMode::Append |
Append after the default system prompt (default) |
Context Compaction
When conversations approach the context window limit:
use ;
let config = default;
let compacted = compact?;
Skills System
Skills inject specialized knowledge on demand from markdown files:
use SkillRegistry;
// Discover skills from ~/.saorsa/skills/
let skills = discover_skills;
for skill in &skills
Skill files are markdown with front matter for metadata (name, description, trigger keywords).
Templates
Prompt templates with variable substitution:
use ;
use HashMap;
// Simple variable substitution
let mut ctx = new;
ctx.insert;
ctx.insert;
let result = render_simple?;
// "Hello Alice, using claude-sonnet-4!"
Template syntax:
- Variables:
{{name}} - Conditionals:
{{#if var}}...{{/if}} - Negated:
{{#unless var}}...{{/unless}}
Built-in templates are available via get_builtin() and list_builtins(). User templates are loaded from ~/.saorsa/templates/*.md.
Extension System
Extensions add custom functionality via lifecycle hooks:
use Extension;
Extension Registry
use ;
// Thread-safe shared registry
let registry = shared_registry;
// Register an extension
// Notify all extensions of events
Specialized Registries
| Registry | Purpose |
|---|---|
CommandRegistry |
Custom slash commands |
KeybindingRegistry |
Custom keyboard shortcuts |
ExtensionToolRegistry |
Custom agent tools |
WidgetRegistry |
Custom UI widgets |
Extensions are loaded from ~/.saorsa/extensions/.
Custom Tools
Implement the Tool trait to add your own tools:
use Tool;
use async_trait;
;
// Register it
let mut registry = new;
registry.register;
Error Handling
Dependencies
| Crate | Purpose |
|---|---|
saorsa-ai |
LLM provider abstraction |
tokio |
Async runtime |
async-trait |
Async trait support |
serde / serde_json |
Serialization |
uuid |
Session IDs |
chrono |
Timestamps |
similar |
Unified diffs (edit/write tools) |
regex |
Grep tool patterns |
walkdir |
Recursive directory traversal |
globset |
Glob pattern matching (find tool) |
dirs |
User directory paths |
tracing |
Structured logging |
thiserror |
Error type derivation |
Development
# Run all tests
# Run integration tests
Minimum Supported Rust Version
The MSRV is 1.88 (Rust Edition 2024). This is enforced in CI.
License
Licensed under either of:
at your option.
Contributing
Part of the saorsa-tui workspace. See the workspace root for contribution guidelines.