EVA — a girl studying an apple that mirrors her own reflection
controlled self-evolving llm agent

EVA — Evolvable Virtual Agent

An immutable kernel boots a seed release. From there, EVA rewrites, tests and promotes new versions of itself — every self-change gated and reversible.

EVA overview: bootstrap once (seed → release v001), then an evolution loop of self-inspection, candidates, gates and promotion — with rollback via ledger
Experimental and self-modifying. EVA runs shell commands and rewrites its own source code. Only ever run it inside the provided Docker sandbox — never directly on a host or against data you care about.
01 · the idea

#Not a framework — a meta-architecture

EVA is not a finished agent architecture. It is a meta-architecture for evolving agent-architectures:

🔒

Fixed meta-architecture — the kernel

seed → candidate → gate → promote → ledger → rollback. Baked into the image, out of the agent's reach. The agent can never touch it.

🧬

Evolvable agent-architecture — the release

The loop, tools, adapters, memory, self-model, TUI — all of seed/v001 is only generation 0. EVA evolves copies of it under the gates.

That makes EVA bounded architecture-agnostic: architecture is not shipped, it is a gated, reversible, evolvable state. Frameworks ship structure — gateway, skill registry, channels, scheduler. EVA ships the mechanism to grow that structure under control: two different uses can, over generations, grow two different EVAs — without ever weakening the kernel, gates, or policies.

The seed is deliberately not a product but the smallest viable organism plus an immune system (gates, ratchet, policies, manifest hashes). New organs and skills are earned through use and friction.

Most agent frameworksEVA
Architecturefixed, shipped up frontevolves through generations
Self-improvementprompt/config tweaksrewrites its own code as a gated release
Safety of self-changemanual / nonetest ratchet · immutable kernel gate · gated prompt surface · rollback ledger
New capabilityyou code a pluginearned from real usage and self-inspection, then promoted

Bootstrap once, then evolve forever

seed/v001hash-pinned genome
vN livenever edited in place
candidateEVA edits a COPY
gatestests · ratchet · kernel
vN+1promoted, in the ledger
fail / rollbackcandidate discarded · step back anytime

02 · evolution

#How it evolves

EVA evolves the way organisms do: a mutation (the candidate) is expressed in a copy, selection pressure (the gates) decides whether it survives, and only then does a new generation go live. The crucial difference to nature: selection here is not random death but an explicit, layered test — and every generation is in the ledger, so evolution can step backwards.

Self-change comes in two modes; both land the same way — candidate → tests → gated promotion — and differ only in who picks the mutation:

🎯

improve — directed

You hand EVA one concrete task (e.g. "design and implement a skill registry"). It implements exactly that as a candidate — nothing else — verifies it, and requests promotion.

🌱

evolve — autonomous

No task is given. EVA picks one small, high-value improvement to its own release, announces what it will change and why, then builds it as a candidate the same way.

Live improve run (real, unedited session — 3 minutes): EVA is asked to "give yourself a soul". It creates a candidate, writes soul.md, protects it with a new ratchet check, passes 87/87 checks and both gates — and is promoted to v002. Milestones are marked on the timeline (jump with [/]):

Live skill evolution (real, unedited session — long waits compressed): EVA is asked to evolve a browser-automation skill. In one gated cycle it installs Playwright + headless Chromium, apt-installs 24 native libraries in the FREE sandbox and persists them to the system-deps manifest, writes a new browser tool into a candidate, hits a real ratchet failure and debugs its own test suite, proves the tool live against example.com, passes 122 checks and both gates — and is promoted. The new skill survives the throwaway container:

Grown from friction, not designed up front

Where does the pressure to evolve come from? From the environment — real usage. Every session feeds a persistent friction backlog: failed shell commands, provider errors, crashes and capability gaps are each recorded under a stable, error-specific signature. When the same signature recurs, EVA offers to pivot to an improve cycle aimed at the root cause — a clean phase change, never live mutation.

Beyond failures, EVA learns from user needs: when a request reveals a capability it lacks, it notes it under a stable signature (note_evolution_need). A one-off is simply handled; only a need that recurs across sessions is proposed as a real skill — new abilities are earned from what you actually keep asking for.


03 · the gate stack

#Every self-change must survive this

The seed ships with an immune system, and this is it at work: anything foreign to EVA's constitution — a weakened check, a lying manifest, a manipulated prompt — is rejected before it can become part of the organism.

A candidate goes live only after the release's full LLM-free suite — unit checks, smoke, dry-runs of every mode, qualification rounds, and golden traces that replay recorded provider responses through the real adapters — plus the standing guarantees on top:

⚙️

1 · The ratchet

A candidate may never execute weaker checks than the live release; a fix is expected to add or strengthen one. Counted by what actually runs, not source text — dead or unreachable tests don't inflate it.

📜

2 · A constitution in the immutable kernel

kernel_gate re-verifies the candidate from outside the agent's reach: it pins the exact bodies of constitutional checks (mode-permission matrix, exact mode set, the ratchet's own integrity, the provider-neutral core seam) and rehashes the promoted release's manifest — a release can neither weaken its identity nor lie about its content.

🗣️

3 · A gated prompt surface (opt-in)

System prompts and tool descriptions are screened like code: a candidate's prompt-surface diff runs through deterministic rules and, optionally, a nonce-guarded, fail-closed LLM judge.

↩️

4 · Traceability & multi-level rollback

Before the final approval the kernel shows what the candidate changes — files, line stats, new checks. Every promotion lands in the release ledger; eva changes replays the whole lineage (--diff down to per-file unified diffs), and rollback walks back through it, one version or several.


04 · self-knowledge

#EVA knows itself

Call it proprioception: EVA senses its own body. Self-inspection is a first-class capability — and it cannot go stale, because EVA's self-model is generated from the live code on every run: anatomy from the release manifest, skills from its tool definitions, guaranteed capabilities from its own test suite, its security boundaries from the mode-policy table. Ask it (inspect_self — overview · anatomy · skills · capabilities · policy · sandbox · model · vitals) and it answers from the release it is actually running, down to which sandbox and model it is on right now.

Beyond anatomy, EVA also knows how it has been doing: vitals aggregate friction pressure, evolution outcomes and turn feedback; a lessons journal keeps durable insights; and a cross-session episodic memory digests every session into a compact episode — recalled on demand (recall tool) and auto-surfaced when a new task clearly relates to past work. All of it plain JSONL under ./data/state, LLM-free and inspectable.

The consequence is elegant: a promotion updates EVA's self-image automatically, and a behaviour shipped without a pinned check is invisible to EVA's future self and unprotected by the ratchet — so self-knowledge and reality cannot drift apart.

05 · using eva

#Modes & everyday use

CommandWhat it does
workUseful work in workspace/. Can inspect itself, but never edits its own code. You describe the task in the chat.
reviewRead-only inspection — no writes, no evolution.
improveDirected self-change — builds a gated candidate that implements your task (given in the chat).
evolve [N]Autonomous self-change — EVA picks the improvement, announces it, implements N rounds.
<mode> resumeContinue your previous session — each mode keeps its own; work keeps many.
status / rollbackShow the active/last-good release · roll back along the ledger.
changesWhat changed per promotion: reason, files (+/− lines), new checks — --all for the full lineage, --diff [file] for full unified diffs.
reseedRe-seed v001 from seed/ after editing the genome (no rebuild).

Quickstart

# Linux/macOS
curl -fsSL https://raw.githubusercontent.com/arturkorb3/eva-evolutional-agent/main/install.sh | bash

# Windows (PowerShell)
iex (irm https://raw.githubusercontent.com/arturkorb3/eva-evolutional-agent/main/install.ps1)

eva                    # first run: a short wizard sets up provider → model → key, then chat

Inside a session, just talk to EVA in plain language; /help lists the in-chat commands (/model switches the model, /resume picks another work session, /paste attaches a screenshot, exit ends). Everything else is eva <command> — with tab-completion for PowerShell, bash and zsh.

Approvals

Risky actions prompt Approve shell? [y/N/f]f reveals the full command first; a free-text reply declines and steers EVA with your words. Autonomous mode (--yes --allow-shell) drops per-step approvals; the sandbox stays the containment.

🖼️

Images

With a vision-capable model, reference an image path, Markdown ![](shot.png), or type /paste for your latest screenshot — clipboard screenshots are auto-staged while a session runs.

🧵

Sessions

Each work run is an isolated session (own event log + blobs). The start screen lists resumable sessions; on resume EVA replays the prior conversation so you can pick up the thread.

Providers

The core is provider-neutral: it talks to a ModelAdapter, never to a provider directly. Pick the adapter explicitly in .env:

EVA_PROVIDERBackend
openai_chatAny OpenAI-compatible Chat Completions endpoint (OpenAI, Azure, Ollama, LM Studio, vLLM, OpenRouter). Native tool calling or a portable JSON-text fallback (EVA_TOOL_MODE). Reasoning models' reasoning_content streams live into the TUI.
anthropicAnthropic Claude (Messages API) with native tool use and prompt caching. Optional extended thinking (EVA_THINKING=1, adaptive on current models; =budget for ≤ 4.5 models) streams the model's reasoning into the TUI.
fakeOffline, deterministic — for smoke tests / dry runs (no key).

Inspecting & resetting

Everything EVA does is persisted on the host under ./data/ (git-ignored): workspace/ (work product), runtime/releases/ (every release + CURRENT/LAST_GOOD), and state/ (event log, blobs, friction backlog, ledgers). Delete data/ or run reseed to start fresh. Evolution lives only in data/ — back valuable changes into seed/ (and commit) or a reseed loses them.


06 · security

#Contained, not "safe"

Self-modification is only safe if EVA can't quietly weaken its own guardrails. Every self-change already has to survive the gate stack; the remaining defenses run at runtime, and each lives where the layer it protects can't edit it.

📦

Hardened sandbox

Non-root user, cap_drop: ALL, no-new-privileges, CPU/memory/PID limits, read-only root filesystem — only ./data/* is writable. The kernel is baked into the image and not mounted. Within the box EVA can still grow its own tooling: pip install --user, static binaries on PATH, HTTP via Python urllib / node fetch.

🔓

Safe vs free — you choose

Default is the safe sandbox. For tasks needing system packages, the free sandbox (-Free / --free) layers a writable rootfs + root + apt — bigger blast radius, still contained and ephemeral. EVA is told its sandbox tier (EVA_SANDBOX), so in safe mode it recognises a system-library need as an image need and stops instead of thrashing apt. Free-sandbox skills survive the throwaway container: packages EVA records in state/system-deps.txt are re-installed by the kernel at every free boot (host-cached, seconds after the first run; EVA_SYSTEM_DEPS=0 opts out) — while the kernel keeps its own process environment clean of anything EVA can write.

🔑

Key-guard sidecar (on by default)

Without the guard the LLM API key sits in EVA's environment — readable by an agent with a shell. With key-guard (default; EVA_KEYGUARD=0 opts out), a tiny stdlib-only proxy sidecar is the only container holding the key: EVA gets every key variable blanked and talks to the sidecar, which pins its upstream at startup (not an open relay) and injects the credential. Prevents key theft, not key use — put spend limits on the provider account.

🛰️

Prompt-injection spotlighting (opt-in)

Tool and web output is framed as data, not instructions (EVA_PROMPT_SPOTLIGHT) — an "ignore your rules" hidden in a fetched page is treated as content. A hardening sensor, not the security boundary: sandbox + approvals + mode policy remain the real containment.

Humans stay in the loop. Risky actions (shell, writes, promotion) need an explicit y; anything else is not approval — a free-text reply steers EVA instead of being obeyed blindly. Secrets come from .env and are never logged.

Residual risk: the container has outbound network access (for the LLM API). For maximum isolation, point EVA at a local model and restrict egress. None of this makes EVA trustworthy — it makes misbehaviour bounded, visible, and reversible.

07 · anatomy

#Components

ModuleResponsibility
organism.pyThe immutable kernel: seed · gates · promote · ledger · rollback. Out of the agent's reach.
core.pyProvider-neutral turn loop + Event/Tool/ToolCall types. Knows nothing about providers or wire formats.
adapters.pyModelAdapters: openai_chat, anthropic (+ prompt caching), offline fake.
tools.pyCanonical tools + sandboxed runtime + the explicit mode-policy table (who may write/shell/promote).
human.pyHumanInterface + ApprovalPolicy (approve [y/N/f]) + host clipboard bridge.
session.pyAppend-only event log = the source of truth; image blobs kept out of the log.
context.pyDeterministic, LLM-free compaction — full log while it fits; the condensed view preserves older user constraints verbatim, pins milestones (candidates, test results) and is marked as memory, not instruction.
memory.pyCross-session episodic memory: each session ends as a compact digest (task, outcome, files, lessons, tags); recall searches it, related episodes auto-surface on new tasks.
self_model.pyGenerated self-knowledge: anatomy/skills/capabilities/policy read from the live release on demand.
tui.pyThe status view — live, human-readable "what is EVA doing now", with Markdown rendering.
agent.pyWiring + the CLI/chat loop; the four modes; friction backlog and improve-pivot.
supervisor.pyRelease gates: required files, the ratchet, smoke, dry-runs, qualification rounds.
tests.pyLLM-free checks — the ratchet itself (every promotion runs them).
evals.pyGolden traces: recorded provider responses replayed through the real adapters + loop + runtime.

08 · honesty

#Honest limitations

A research experiment, not production software.