Releases: fischerf/aar
v0.3.2
[0.3.2] - 2026-04-22
Added
Providers
- Gemini provider - Google Gemini Pro and Flash support via the official
google-genaiSDK
and a custom HTTP backend (aar/providers/gemini.py); documented indocs/providers_gemini.md.
ACP / Zed Integration
- Official ACP SDK transport -
aar acpnow uses theagent-client-protocolPython SDK for
Zed stdio communication; HTTP/SSE mode remains available viaaar acp --http. - Full session lifecycle -
load_session,list_sessions,close_session,fork_session,
resume_session, andset_mode/set_config_optionimplemented in the ACP stdio transport. - Session mode and config discovery -
new_sessionandload_sessionreturn
modes=SessionModeStateandconfig_optionsderived fromSafetyConfig(auto / review /
read-only;auto_approve_writes,auto_approve_execute,read_onlytoggles). - Thinking and tool-call event streaming -
ReasoningBlockemitsAgentThoughtChunk;
ToolCallemitsToolCallStart;ToolResultemitsToolCallProgress. @-mention context support -_extract_texthandlesResourceContentBlock(URI links)
andEmbeddedResourceContentBlockwithTextResourceContents.acp_terminalbuilt-in tool - registers only when the client advertises
ClientCapabilities(terminal=True)duringinitialize.- Approval process for ACP - tool calls can be approved or rejected from the editor UI;
acp_approval_timeoutconfig field (default: wait forever, validated against neg/NaN/inf/bool). - Slash commands (
/status,/tools,/policy) available in Zed and returned via
AvailableCommandsUpdateon session open. - MCP server bridge in ACP - stdio and HTTP MCP servers passed in
session/neware started
and their tools registered for the lifetime of that session. - Plan update notifications - ACP clients receive live plan/step updates during tool execution.
- SSE byte framing -
data: <json>\\n\\nframing verified by new wire-level tests. - VSCode integration -
.vscode/configuration and launch profiles for local development.
Sandbox
- Docker sandbox - run agent tools inside an isolated Docker container.
- Linux Landlock sandbox - process isolation using Linux kernel >= 5.13 Landlock LSM.
- Windows Job Object sandbox - process isolation using Windows Job Objects.
- Distro profiles - predefined WSL distro setup profiles shipped with the package under
agent/data/distros/;aar sandbox setupreads them automatically. aar sandbox status- new subcommand to inspect config and live distro state.
TUI
- File picker -
@in the fixed TUI input opens a modal file browser. - Log viewer -
aar tui --fixednow includes a dedicated log viewer panel. think/channel tag handling - inline<think>and channel tags parsed in the input stream.
Core / CLI
aar prompt --layers- shows ordered prompt sources with file paths, character counts,
and skipped files.- Configurable provider timeout -
provider_timeoutfield in config. - Configurable command timeout -
command_timeoutfor bash/shell tool calls (raised defaults). - Budget proximity warning - core loop emits a warning when approaching the token/cost budget.
- Guardrails - configurable guardrail rules (Opus-style) for autonomous loop safety.
- Search directories for prompt extensions - additional system prompt directories configurable.
- Misconfiguration warnings - startup checks warn on likely config errors.
jsonschemaadded as a core dependency.
Changed
- ACP transport refactored - split into
agent/transports/acp/stdio.py,
agent/transports/acp/http.py, andagent/transports/acp/common.py. - Core loop refactored - cleaner separation between run logic and event dispatch.
- Sandbox modes reworked - unified config model covering WSL, Docker, Landlock, Job Object.
- Agent timeout - default changed to infinite (no hard cutoff); configurable per-session.
- WSL setup timeout - raised to 600 s to accommodate large package installs.
- ToolResult error prefixes - unified format
Error [<category>]: ...across all tools. Provider.stream()fallback - replays text, reasoning, and tool calls with terminal
metadata when the underlying stream errors mid-response.- Path normalization -
_normalize_pathhandles UNC paths, lowercase drive letters, and
./..collapse on both Linux and Windows. - Workspace escape guard -
cwdis validated to stay inside the configured workspace in
WslDistroSandbox.execute. - System prompt for Alpine WSL - expanded with Alpine-specific shell idioms.
- Autonomous loop - enhanced step sequencing and recovery logic.
- Dependencies updated -
pydantic,httpx,rich,textual,anthropic,openai,
google-genai,mcp,agent-client-protocolall updated to latest compatible versions.
Fixed
- ACP session load/resume -
load_sessionwas silently no-op; now correctly restores
persisted sessions and replays message history to the client before resolving. - ACP session listing -
list_sessionsreads all.jsonlfiles and returnsSessionInfo
with title derived from the first assistant message. - ACP unknown session on
prompt- creates a fresh session instead of crashing. - Stream chunk finalisation -
StreamChunk(finished=True)now always fires even when the
stream raises mid-way (wrapped intry/finally). - Safety/approval edge cases - fixed races and missing approval callbacks in the policy engine.
- ACP concurrent prompt rejection - a second
prompton the same session while one is
in-flight is now correctly rejected with an error response. - Keybinds - external keybind configuration removed (caused setup issues); bindings are now
defined in code viaagent/transports/keybinds.py.
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