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. Rust-native, app-facing and lightweight. It provides just the essentials: an agent loop, three core tools, session management, and a typed event stream, leaving the actual product decisions entirely to your app.

flowchart LR
    T["TUI"] --> H["ag-harness"]
    C["CLI"] --> H
    W["HTTP"] --> H
    H --> A["Anthropic"]
    H --> O["OpenAI"]
    H --> Q["Qwen"]

Core features🔗

  • Three built-in tools - read, write, bash.
  • Structured edits - the write tool applies git-style diff patches and emits clean, targeted diffs.
  • Permission policy - every tool call passes a policy check.
  • Persisted sessions - session state and required data are saved.

Architecture🔗

Crates🔗

agentty/
├── crates/
│   ├── ag-harness            # facade
│   ├── ag-harness-protocol   # Op, Event, Diff, Usage
│   ├── ag-harness-core       # loop, tools, sessions, context, storage

Process model🔗

Session management🔗

  • One host process manages multiple independent sessions.
  • An active session runs as an async task; sessions run in parallel.
  • Each session processes its prompts in sequence.
  • Session state is saved on disk and loaded when resumed.
  • The app coordinates work across sessions.
  • Closing the host process stops active turns; 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 is kept in memory.
  • Session state is saved on disk.
  • Idle sessions reload from disk when resumed.

Agent loop🔗

A turn loops until the model responds without requesting a tool:

flowchart TD
    A[user prompt] --> B[assemble context]
    B --> C[call model, stream reply]
    C --> D{tool requested?}
    D -- yes --> E[policy check → run tool]
    E --> B
    D -- no --> F[turn complete]

One tool call🔗

sequenceDiagram
    participant M as Model
    participant H as Harness
    participant App
    M->>H: write(parser.rs, patch)
    H->>H: policy → allowed?
    H->>H: apply patch
    H-->>App: Structured diff event
    H->>M: tool result → loop continues

Editing🔗

  • Write - the model produces a git diff patch; the harness applies it. Alternative edit methods (exact-match string replacement, etc.) are future experiments.
  • Diff - applied changes are streamed back as structured file-diff events, renderable directly as git diffs.
  • Output caps - oversized tool output is truncated head+tail with a marker, so one careless command can't flood the session's context. Per-tool limits; read supports line ranges for precise re-reads.

Context🔗

  • Project discovery - finds the project root and AGENTS.md; the model explores the rest via bash.
  • Base prompt - one minimal system prompt.

Permissions🔗

All tools are denied by default. The session policy explicitly allows tools and, for bash, the permitted commands:

Policy {
    read: Allow,
    write: Deny,
    bash: AllowCommands(["cargo test", "git status", "rg *"]),
}

Model tiers🔗

ag-harness will provide the ability to work with different AI model API tiers for the best cost and performance:

  • Model variety - frontier vs fast models: cheaper models for simpler tasks.
  • Batch/Flex processing - designed for latency-tolerant, non-critical workloads, such as async jobs completed within 24 hours at 50% off.
  • Priority processing - faster responses at a premium for latency-critical turns.

Library API🔗

  • Harness - creates and resumes sessions.
  • Session - holds model, policy, working directory, and state.
  • Turn - runs one prompt and streams text, diffs, completion, and failure events.
  • App - configures the session and renders its events.

Differences from existing harnesses🔗

  • User-facing products (Claude Code, OpenCode, Aider): format output for humans; ag-harness emits events for apps.
  • Minimal harnesses (Pi): TypeScript-first, no permission layer; ag-harness is Rust-native with an enforced policy hook.
  • Vendor SDKs: vendor-locked; ag-harness is model-agnostic.
  • Rust model-API crates (rig): abstract the model call only; ag-harness adds the loop, persisted sessions, tools, permissions.
  • Heavy harnesses: bundle orchestration; ag-harness leaves it to the app.

Roadmap🔗

  1. v1 - library. ag-harness - unified crate integrated into agentty.
  2. Service wrapper. ag-harness-service (JSON-RPC 2.0 over Unix socket, WebSocket for remote) + ag-harness-client.