Skip to content

Architecture Overview

Peri uses a layered architecture: Frontend (TUI/ACP) → Agent Core (ReAct loop) → Middleware Layer (feature implementations) → External Systems.

Layered Structure

┌─────────────────────────────────────┐
│  peri-tui / peri-acp (Frontend)     │  TUI full-screen / ACP stdin/stdout
├─────────────────────────────────────┤
│  peri-agent (Agent Framework Core)  │  ReAct loop, middleware chain, LLM interface
├─────────────────────────────────────┤
│  peri-middlewares (Features)         │  18 built-in middleware
├─────────────────────────────────────┤
│  External Systems                   │  Filesystem, terminal, web, MCP, LSP
└─────────────────────────────────────┘

The frontend communicates with the Agent core via mpsc channel (TUI) or stdio (ACP), sharing the same Agent backend.

Crate Responsibilities

CrateResponsibility
peri-agentReAct loop, middleware chain, LLM interface, Thread persistence, message system, Compact compression
peri-middlewares18 built-in middleware: filesystem, terminal, web, sub-agent, Tool Search, HITL, Hooks, Plugins, Cron, Skills
peri-tuiRatatui terminal UI, CLI entry point (clap), event handling, Markdown rendering
peri-acpACP service layer: SessionManager, Transport abstraction, system prompt construction, Langfuse tracing
peri-widgetsTUI reusable component library
peri-lspLSP client integration
langfuse-clientLangfuse LLM observability client

Data Flow

User Input

TUI (Ratatui) / ACP (stdio JSON-RPC)
  ↓ mpsc / StdioTransport
AcpServer → SessionManager

ReActAgent (peri-agent)

MiddlewareChain
  ├── before_agent   → Inject CLAUDE.md, Skills
  ├── before_model   → Compact check
  ├── LLM Call       → OpenAI / Anthropic API
  ├── after_model    → Token tracking
  ├── before_tool    → HITL permission check
  ├── Tool Execution → File read/write, command execution
  └── after_tool     → Logging

AgentOutput → User

ReAct Agent Execution Flow

1. Add human message to state
2. Collect tools from all middleware (collect_tools)
3. chain.run_before_agent(state)
4. Prepend system_prompt
5. for step in 0..max_iterations:
   a. chain.run_before_model(state)    ← Compact checkpoint
   b. call_llm(messages, tools) → Reasoning
   c. chain.run_after_model(state)
   d. if needs_tool_call:
      - chain.run_before_tool()        ← Permission check
      - Tool execution
      - chain.run_after_tool()
   e. else:
      - Return AgentOutput
6. chain.run_after_agent(state)

Key Design Decisions

DecisionRationale
Rust implementationMemory safety + high performance, ~50MB memory footprint, single binary distribution
Middleware-drivenAll features injected via Middleware trait, pluggable and testable
Context optimizationSystem prompt cache boundary + Tool Search lazy loading + Compact auto-compression
.claude/ compatibleDirectly reuse Claude Code's directory structure, zero migration cost
Multi-frontendTUI (mpsc) and IDE (stdio/ACP) share the same Agent backend
ThreadStore abstractionPluggable storage backends (SQLite/Redis/filesystem), supports session recovery

See Also

Released under the Apache 2.0 License.