Releases: fischerf/aar
v0.3.1
Aar v0.3.1 — Better UX, Customizable Hotkeys, More Configs
January 2025
What's New
🎨 Proof of Concept
- Shadow Branching Protocol — Idea as project rulesmd added
config/rules/ideas/rules.mdfor a full automated time travel git workflow with branching!!!
🎨 User Experience Enhancements
- Multiline Input — Write complex prompts across multiple lines. Use
Enterfor new lines,Ctrl+Sto send. - Collapsible Think Panel — Reasoning output now appears in a hideable sidepanel. Toggle with
Ctrl+Tto maximize workspace.
⌨️ Keyboard Control
- Customizable Keybindings — Remap shortcuts via external config files without code changes.
- Ctrl+S Send — Quick alternative to Enter for sending messages in the TUI.
⚙️ Configuration & Providers
- OmniCoder Preset — New
config_omnicoder.jsonfor code-focused workflows with Ollama. - Enhanced System Prompt — Improved minimal ReAct loop in
config/rules/rules.mdwith clearer action rules and failure recovery. - Config Documentation — Completely rewritten
config/README.mdwith quick start, examples, and troubleshooting.
📺 Documentation
- Better Examples — OS-specific setup instructions and use-case walkthroughs.
🔧 Under the Hood
- Error-Isolated Event Dispatch — Tool and provider failures now emit events for better observability.
- Unified Logging — Consistent logging across CLI, TUI, and web transports.
🐛 Fixes
- Fixed approval modal theme color handling
- Corrected token display during streaming
- Improved focus handling in modal transitions
✅ Compatibility
Fully backward-compatible with v0.3.0. No breaking changes. Upgrade freely.
📖 Learn More
🙌 Thanks
Special thanks to everyone who provided feedback on TUI usability. Your input shaped this release!
Full Changelog: v0.3.0...v0.3.1
v0.3.0
Release Notes — v0.3.0
Released: 2026
Tag:v0.3.0
Previous:v0.2.1
Highlights
This release is a major feature drop. The two biggest gaps versus v0.2.1 — no streaming and no reliability safeguards — are both closed. On top of that: full multimodal input, a rich theming system, a new full-screen TUI powered by Textual, automatic context management, parallel tool execution, and a completely restructured documentation suite.
69 files changed, 11 559 insertions, 1 266 deletions.
Tests: 490 (up from ~310 at v0.2.1).
Core Loop
Token-level streaming
Set streaming = true in aar.json (or AgentConfig.streaming = True) to switch from
batch-response mode to token-level streaming. The loop streams deltas from the provider and emits
a StreamChunk event for every token, so TUI / web consumers can display output as it generates.
{
"streaming": true
}All three providers (Anthropic, OpenAI, Ollama) expose the new Provider.stream() async-generator
protocol. The loop falls back to batch mode when a provider does not support it.
Retry with exponential backoff
Provider failures on recoverable errors (rate limits, timeouts, transient network issues) are now
retried automatically instead of immediately setting AgentState.ERROR.
{
"max_retries": 3
}Retry schedule: 1 s → 2 s → 4 s (2^(attempt−1)). Non-recoverable errors (auth failures, bad
requests) are never retried. The --log-level DEBUG flag shows each retry attempt.
Automatic context management
Configure a token budget to prevent silent failures when sessions approach the model's context
limit. The sliding_window strategy drops the oldest messages that exceed the budget.
{
"context_window": 8192,
"context_strategy": "sliding_window"
}Token counting uses a fast heuristic (~4 chars/token). Set context_window = 0 (default) to
disable and preserve the previous unbounded behavior.
Parallel tool execution
When the model returns multiple tool calls in a single turn, they are now executed concurrently
with asyncio.gather(). This is the default behavior and requires no configuration. Set
parallel=False on ToolExecutor.execute() to force sequential execution.
Tool argument validation
ToolExecutor now validates tool call arguments against each tool's input_schema using
jsonschema before dispatching. Malformed LLM responses produce a structured ToolResult error
instead of a cryptic Python traceback.
Multiple event callbacks (sync + async)
Agent.on_event() previously accepted only a single callback. It now maintains a list; every
registered callback fires in registration order. Both synchronous and async callables are
accepted — async callbacks are scheduled with asyncio.ensure_future().
agent.on_event(log_to_disk) # sync
agent.on_event(push_to_webhook) # async defState machine fixes
AgentState.TIMED_OUT and AgentState.MAX_STEPS are now set correctly when the loop exits for
those reasons. Previously both fell back to AgentState.ERROR.
Multimodal Input
A new agent/core/multimodal.py module defines typed content blocks for mixed-media messages.
Session.add_user_message() and Agent.run() / Agent.chat() now accept either a plain string
or a list of content blocks.
from agent.core.events import ImageURLBlock, ImageURL, TextBlock
response = await agent.chat([
TextBlock(text="What is in this image?"),
ImageURLBlock(image_url=ImageURL(url="https://example.com/photo.jpg")),
])New content block types (all Pydantic models, discriminated on type):
| Block | Type tag | Notes |
|---|---|---|
TextBlock |
"text" |
Plain text |
ImageURLBlock |
"image_url" |
HTTP URL or data: URI; optional detail hint for OpenAI |
AudioBlock |
"audio" |
URL or base-64 audio; format auto-detected |
VideoBlock |
"video" |
Prepared — not yet implemented at provider level |
Provider support:
| Provider | Image | Audio | Video |
|---|---|---|---|
| Anthropic | ✅ | ✅ | — |
| OpenAI | ✅ | ✅ | — |
| Ollama | ✅ (vision models) | ❌ | — |
UserMessage.parts carries the full block list for providers; UserMessage.content holds a
plain-text summary for logging and display. The is_multimodal property is True when parts
is non-empty.
New Events
| Event | When emitted |
|---|---|
StreamChunk |
Every token delta during streaming (text, reasoning_text, finished) |
ReasoningBlock |
Model thinking / chain-of-thought content (Ollama 0.20+, Anthropic extended thinking) |
EventType.STREAM_CHUNK is the new enum value.
Configuration
New fields on AgentConfig:
| Field | Type | Default | Description |
|---|---|---|---|
streaming |
bool |
False |
Enable token-level streaming |
max_retries |
int |
3 |
Max retry attempts for recoverable provider errors |
context_window |
int |
0 |
Token budget (0 = disabled) |
context_strategy |
str |
"sliding_window" |
Context trimming strategy |
response_format |
str |
"" |
"" | "json" | "json_schema" |
json_schema |
dict |
{} |
Schema used when response_format = "json_schema" |
shell_path |
str |
"" |
Override the shell executable for tool calls |
project_rules_dir |
Path |
.agent |
Directory for rules.md project instructions |
log_level |
str |
"WARNING" |
Python logging level |
tui |
TUIConfig |
— | TUI appearance settings (see Themes) |
The system_prompt is now built automatically from shell_path and project_rules_dir if not
explicitly provided; override it by setting system_prompt directly.
Themes
A new agent/transports/themes/ package introduces a full theming system for both TUI modes.
Four built-in themes:
| Name | Description |
|---|---|
bernstein |
Dark blue / gold — the new default |
classic |
Green-on-dark terminal classic |
decker |
High-contrast amber / charcoal |
sleek |
Minimal monochrome |
Configure the theme in aar.json:
{
"tui": {
"theme": "decker"
}
}Hot-switch at runtime with /theme <name> in either TUI mode. Custom themes can be defined in a
JSON file and registered with ThemeRegistry. The Theme and TUIConfig models are fully typed
Pydantic models in agent/transports/themes/models.py.
TUI — Full-screen Fixed Mode (aar tui --fixed)
A brand-new full-screen terminal UI built on Textual,
replacing the earlier experimental prototype.
aar tui --fixedFeatures:
- Full-screen layout with a dedicated header bar, scrollable chat body, and sticky input bar
- Mouse support (click, scroll)
- Keyboard shortcuts (Ctrl+C to cancel a running turn, Ctrl+D to quit)
- Live token streaming — output appears word-by-word
- Thinking / reasoning blocks rendered as collapsible sections
- Tool call blocks: name, arguments, result, timing
- Selectable chat blocks — click to copy content
- Full theme support; hot-switch at runtime
- Non-blocking — the UI stays responsive while the agent runs
The implementation is modularized under agent/transports/tui_widgets/:
| Module | Contents |
|---|---|
bars.py |
HeaderBar, status indicator, model/session pill |
blocks.py |
AssistantBlock, ToolBlock, ThinkingBlock, UserBlock |
chat_body.py |
Scrollable chat container, auto-scroll-to-bottom |
input.py |
HistoryInput — multi-line input with command history |
Shared formatting helpers live in agent/transports/tui_utils/formatting.py and are used by
both the Rich inline TUI and the Textual fixed TUI.
TUI — Rich Inline Mode (aar tui)
- Themes applied to all Rich renderables
- Non-blocking event loop — UI stays responsive during generation
- Copy/paste improved on Windows
/statusno longer queries the LLM when no session has started- Ollama reasoning (thinking) blocks displayed correctly
- Layout polish: separator styles, button sizing, input field theming
Providers
Streaming protocol
All providers implement the new async def stream(...) async generator that yields StreamDelta
objects. Provider.supports_streaming property lets the loop opt-in per provider.
Ollama
- Thinking mode — pass
supports_reasoning = truein providerextrato enable Ollama 0.20+
thinkmode. Reasoning content is emitted as aReasoningBlockevent. - Structured output —
response_format = "json"setsformat: "json";"json_schema"passes
the full schema viaformat: {...}. - Capability flags in
extra:supports_vision,supports_audio,supports_tools,
supports_reasoning. - Warning emitted when audio input is sent to an Ollama model (not supported as of v0.20).
Sample Ollama configs
Three ready-to-use config files are included in config/samples/:
| File | Model |
|---|---|
config_gemma.json |
gemma4:e4b (vision, reasoning) |
config_qwen.json |
qwen3.5:9b |
config_deepseek.json |
deepseek-r1:8b (reasoning) |
Session & Memory
JSONL schema versioning (SCHEMA_VERSION = 1)
Session files now include a schema_version header. Loading a session saved by a newer version
of aar raises a clear ValueError with an upgrade message instead of silently producing garbage.
Loading an older session logs a migration notice. This is the foundation for safe schema
evolution going forward.
Context trimming helpers
Two new public functions in agent.core.session:
estimate_token_count(messages)— fast heuristic token counter (~4 chars/token)- `trim_to_token_budge...
v0.2.1
What's Changed
- markdown rule system added [system/global/local]
- config system added for defaults and mcp_servers
- system specific updated esp. safety
- new commands
Full Changelog: v0.2.0...v0.2.1
v0.2.0 - Intial release
A lean, provider-agnostic agent framework with a thin core loop, typed event model, sandboxed tool execution, and pluggable transports. Aar stands for Adaptive Action & Reasoning.
Design goals
Thin core loop — the main execution path is small and readable at a glance
Typed event model — every message, tool call, and result is a typed, serializable event
Provider-agnostic — swap between Anthropic, OpenAI, and Ollama without changing agent code
Safe by default — path restrictions, command deny-lists, and approval gates built in
Modular transports — the same agent runs from CLI, TUI, web API, or embedded in your code
Persistent sessions — every run is saved as JSONL and resumable; long sessions can be compacted
Observable — every provider call and tool execution is timed; sessions carry a trace_id
Cancellable — cooperative (asyncio.Event) and hard (CancelledError) cancellation built in