ag-harness Design
Design for the planned ag-harness runtime.
ag-harness - a light LLM harness🔗
ag-harness is the base layer between an application and an LLM. It is Rust-native, app-facing, and lightweight. It provides the essentials: an agent loop, three core tools, session management, and a typed event stream, leaving product decisions to the application.
flowchart LR
T["TUI"] --> H["ag-harness"]
C["CLI"] --> H
W["HTTP"] --> H
H --> M["Muse"]
H --> K["Kimi"]
H --> Q["Qwen"]Core features🔗
- Three built-in tools -
read,write, andbash. - Structured edits -
writeapplies git-style patches and emits targeted diffs. - Permission policy - every tool call passes an explicit policy check.
- Persisted sessions - session state is saved and can be resumed.
- Typed events - applications receive model, tool, diff, completion, and failure events.
Architecture🔗
Crates🔗
agentty/
└── crates/
├── ag-harness # facade
├── ag-harness-cli # interactive CLI
├── ag-harness-protocol # operations and events
└── ag-harness-core # loop, tools, sessions, storageThe crates separate the public API, protocol types, runtime behavior, and interactive CLI. A provider-neutral model contract keeps Muse, Kimi, Qwen, and future adapters behind the same structured-output and tool-calling boundary.
Session management🔗
- One host process manages multiple independent sessions.
- Active sessions run concurrently as async tasks.
- Each session processes its prompts in sequence.
- Session state is saved on disk and loaded when resumed.
- The application coordinates work across sessions.
- Stopping the host ends active turns while saved sessions remain resumable.
flowchart TB
H["Harness process"] --> S["Session management"]
S --> A["Session A"]
S --> B["Session B"]
S --> C["Session C"]
A --> AT["Async task"]
B --> BT["Async task"]
C --> CT["Async task"]Memory management🔗
- Active session state stays in memory.
- Resumable session state is persisted to disk.
- Idle sessions reload from disk when resumed.
Agent loop🔗
A turn continues until the model responds without requesting a tool:
flowchart TD
A["User prompt"] --> B["Assemble context"]
B --> C["Call model"]
C --> D{"Tool requested?"}
D -- Yes --> E["Check policy and run tool"]
E --> B
D -- No --> F["Turn complete"]One tool call🔗
sequenceDiagram
participant M as Model
participant H as Harness
participant A as App
M->>H: Request write patch
H->>H: Check policy
H->>H: Apply patch
H-->>A: Emit diff event
H->>M: Return tool resultEditing🔗
- Write - the model supplies a git-style patch and the harness applies it.
- Diff - applied changes are streamed as structured file-diff events.
- Output caps - oversized tool output is truncated with a marker.
- Focused reads -
readsupports bounded inspection and precise re-reads.
Context🔗
- Project discovery - finds the project root and applicable
AGENTS.mdguidance. - Repository exploration - the model inspects the project through bounded tools.
- Base prompt - one minimal system prompt establishes the harness contract.
Structured output🔗
- The caller supplies the expected JSON shape as a provider-neutral schema.
- Each adapter uses the strongest structured-output mechanism its provider supports.
- The harness parses and validates terminal output against the caller's schema.
- Provider-specific request fields stay inside the adapter.
- Every model adapter implements the same structured-output and tool-calling semantics.
Unsupported configurations return an explicit error instead of silently falling back to unstructured text.
External model adapters🔗
ModelRequest::messages() exposes the ordered conversation as immutable ModelMessage entries. External adapters translate system instructions, retained turns, assistant tool calls, and correlated results from this view rather than from prompt() alone. ToolCall::from_json() shares bounded built-in argument decoding with native providers; call serialization and optional reasoning are available for provider replay. History mutation, tool permissions, execution limits, and terminal schema validation remain owned by the harness.
Telemetry🔗
- Metrics - record model and tool duration, token usage, and outcomes.
- Labels - identify the provider and model without application-specific dimensions.
- Ownership - the harness records observations; the host configures export.
- Lifecycle - applications can observe calls, tools, completion, and failure.
flowchart LR
C["Model call"] --> M["Runtime metrics"]
M --> O["OTLP collector"]
O --> P["Prometheus"]
P --> G["Grafana"]Permissions🔗
All tools are denied by default. A session policy explicitly enables tools and, for bash, the commands it may run:
Policy {
read: Allow,
write: Deny,
bash: AllowCommands(["cargo test", "git status", "rg *"]),
}Model tiers🔗
- Model variety - choose fast or frontier models for the task.
- Batch/Flex processing - run latency-tolerant background work at lower cost.
- Priority processing - favor response time for latency-sensitive turns.
- Application choice - the application selects the model and processing tier.
Library API🔗
- Harness - creates and resumes sessions.
- Session - holds the model, policy, working directory, and state.
- Turn - runs one prompt and streams typed events.
- Model - provides the provider-neutral completion boundary.
- App - configures sessions, coordinates work, and renders events.
Differences from existing harnesses🔗
- User-facing products (Claude Code, OpenCode, Aider) format output for humans;
ag-harnessemits events for applications. - Minimal harnesses (Pi) are TypeScript-first and omit an enforced permission layer;
ag-harnessis Rust-native and policy-driven. - Vendor SDKs are provider-specific;
ag-harnessis model-neutral. - Rust model API crates such as
rigabstract model calls;ag-harnessadds the loop, sessions, tools, permissions, and events. - Heavy harnesses bundle orchestration;
ag-harnessleaves it to the application.
Next iterations🔗
- Complete the tool set. Add policy-constrained
bashexecution alongsidereadandwrite. - Persisted sessions. Add resumable state and the typed event stream around the existing model and tool loop.
Roadmap🔗
- v1 - library. Integrate the unified
ag-harnesscrate into Agentty. - Service wrapper. Add
ag-harness-serviceandag-harness-clientfor local and remote applications.