Architecture Overview
Cargo workspace (Edition 2024, resolver 3) with 21 crates + binary root.
Requires Rust 1.94+. Native async traits are used throughout. async-trait is retained only in crates blocked by upstream dependencies (zeph-core, zeph-mcp, zeph-acp — blocked by rmcp).
Workspace Layout
zeph (binary) — thin CLI/channel dispatch, AppBuilder bootstrap, vault/skill/memory subcommands
├── Layer 0 — Primitives
│ └── zeph-common Shared primitives: Secret, VaultError, common types
├── Layer 1 — Configuration & Secrets
│ ├── zeph-config Pure-data configuration types, TOML loader, env overrides, migration
│ └── zeph-vault VaultProvider trait + env and age-encrypted backends
├── Layer 2 — Core Domain Crates
│ ├── zeph-db Database abstraction (SQLite + PostgreSQL)
│ ├── zeph-llm LlmProvider trait, Ollama/Claude/OpenAI/Gemini/Candle backends, router
│ ├── zeph-memory SQLite + Qdrant, SemanticMemory, summarization, document loaders
│ ├── zeph-tools ToolExecutor trait, ShellExecutor, FileExecutor, TrustLevel
│ ├── zeph-skills SKILL.md parser, registry, embedding matcher, hot-reload
│ └── zeph-index AST-based code indexing, hybrid retrieval, repo map (always-on)
├── Layer 3 — Agent Subsystems
│ ├── zeph-context Context assembly, budget, compaction (extracted from zeph-core)
│ ├── zeph-sanitizer Content sanitization, PII filter, exfiltration guard
│ ├── zeph-experiments Autonomous experiment engine, LLM-as-judge evaluation
│ ├── zeph-subagent Subagent lifecycle, grants, transcripts, hooks
│ └── zeph-orchestration DAG-based task orchestration, planner, router, aggregator
├── Layer 4 — Agent Core & Commands
│ ├── zeph-core Agent loop, context builder, metrics
│ └── zeph-commands Slash command handlers, CommandHandler registry
├── Layer 5 — Protocol & I/O
│ ├── zeph-channels Telegram, Discord, Slack adapters
│ ├── zeph-mcp MCP client via rmcp, multi-server lifecycle (optional)
│ ├── zeph-acp ACP server — IDE integration (optional)
│ ├── zeph-a2a A2A protocol client + server (optional)
│ ├── zeph-gateway HTTP webhook gateway (optional)
│ └── zeph-scheduler Cron task scheduler (optional)
└── Layer 6 — UI
└── zeph-tui ratatui TUI dashboard with real-time metrics (optional)
See Crates Overview for the full layered architecture with dependencies.
Dependency Graph
The layered architecture enforces a strict dependency direction: higher layers depend on lower layers, never the reverse. zeph-core (Layer 4) orchestrates all subsystems. Protocol crates (Layer 5) are feature-gated and wired by the binary. Sub-agent lifecycle state is defined in zeph-subagent (Layer 3) to keep zeph-core focused on the agent loop.
Agent Loop
The agent loop processes user input in a continuous cycle:
- Read initial user message via
channel.recv() - Build context from skills, memory, and environment (summaries, cross-session recall, semantic recall, and code RAG are fetched concurrently via
try_join!) - Stream LLM response token-by-token
- Execute any tool calls in the response
- Drain queued messages (if any) via
channel.try_recv()and repeat from step 2
Queued messages are processed sequentially with full context rebuilding between each. Consecutive messages within 500ms are merged to reduce fragmentation. The queue holds a maximum of 10 messages; older messages are dropped when full.
Key Design Decisions
- Generic Agent:
Agent<C: Channel>— generic over channel only. The provider is resolved at construction time (AnyProviderenum dispatch). Tool execution usesBox<dyn ErasedToolExecutor>for object-safe dynamic dispatch, eliminating the formerT: ToolExecutorgeneric parameter. Internal state is grouped into domain sub-structs:MessageState(message buffer, image staging),MemoryState(semantic memory, graph, summaries),SkillState(registry, matcher, prompt),RuntimeConfig(security, hooks, persona config),McpState(MCP tools, manager),IndexState(code retriever, indexer),DebugState(dumper, trace, anomaly detector),SecurityState(sanitizer, quarantine, exfiltration guard), andToolState(schema filter, dependency graph, iteration bookkeeping). Logic is decomposed intostreaming.rs,persistence.rs, and three dedicated subsystems:ContextManager(budget / compaction),ToolOrchestrator(doom-loop detection / iteration limit), andLearningEngine(self-learning reflection state). Concurrency usesparking_lotlocks throughout (no poison handling) - TLS: rustls everywhere (no openssl-sys)
- Bootstrap:
AppBuilderin the binary’sbootstrap/module (split intomod.rs,config.rs,health.rs,mcp.rs,provider.rs,skills.rs) handles config/vault resolution, provider creation, memory setup, skill matching, tool executor composition, and graceful shutdown wiring.main.rs(thin entry point) delegates torunner.rsfor channel/mode dispatch - Binary structure:
zephbinary is decomposed into focused modules —runner.rs(dispatch),agent_setup.rs(tool executor + MCP + feature extensions),tracing_init.rs,tui_bridge.rs,channel.rs,cli.rs(clap args),acp.rs,daemon.rs,scheduler.rs,commands/(vault/skill/memory subcommands),tests.rs - Errors:
thiserrorfor all crates with typed error enums (ChannelError,AgentError,LlmError, etc.);anyhowonly for top-level orchestration inrunner.rs - Lints: workspace-level
clippy::all+clippy::pedantic+clippy::nursery;unsafe_code = "deny" - Dependencies: versions only in root
[workspace.dependencies]; crates inherit viaworkspace = true - Feature gates: optional crates (
zeph-mcp,zeph-a2a,zeph-tui) are feature-gated in the binary;zeph-indexis always-on with all tree-sitter language grammars (Rust, Python, JS/TS, Go) compiled unconditionally - Context engineering: proportional budget allocation, semantic recall injection, message trimming, runtime compaction, environment context injection, progressive skill loading, ZEPH.md project config discovery
- Graceful shutdown: Ctrl-C triggers ordered teardown — the agent loop exits cleanly, MCP server connections are closed, and pending async tasks are drained before process exit
- LoopbackChannel: headless
Channelimplementation using two linked tokio mpsc pairs (input_tx/input_rxfor user messages,output_tx/output_rxforLoopbackEventvariants). Auto-approves confirmations. Used by daemon mode to bridge the A2A task processor with the agent loop - Streaming TaskProcessor:
ProcessorEventenum (StatusUpdate,ArtifactChunk) replaces the former synchronousProcessResult. TheTaskProcessor::processmethod accepts anmpsc::Sender<ProcessorEvent>for per-token SSE streaming to connected A2A clients