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 is not a finished agent architecture. It is a meta-architecture for evolving agent-architectures:
seed → candidate → gate → promote → ledger → rollback. Baked into the image, out of the agent's reach. The agent can never touch it.
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 frameworks | EVA | |
|---|---|---|
| Architecture | fixed, shipped up front | evolves through generations |
| Self-improvement | prompt/config tweaks | rewrites its own code as a gated release |
| Safety of self-change | manual / none | test ratchet · immutable kernel gate · gated prompt surface · rollback ledger |
| New capability | you code a plugin | earned from real usage and self-inspection, then promoted |
organism.py — the kernel: ~900 lines, baked into the image. It seeds the
genome (verifying SHA-256 manifest hashes), runs the final gate, keeps the ledger.seed/v001/ — the genome: real, reviewable, hash-pinned Python files.runtime/releases/vN/ — the live, evolvable generations. EVA builds a
candidate and, only when the gates pass, swaps itself for it.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:
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.
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:
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.
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:
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.
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.
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.
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.
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.
| Command | What it does |
|---|---|
work | Useful work in workspace/. Can inspect itself, but never edits its own code. You describe the task in the chat. |
review | Read-only inspection — no writes, no evolution. |
improve | Directed 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> resume | Continue your previous session — each mode keeps its own; work keeps many. |
status / rollback | Show the active/last-good release · roll back along the ledger. |
changes | What changed per promotion: reason, files (+/− lines), new checks — --all for the full lineage, --diff [file] for full unified diffs. |
reseed | Re-seed v001 from seed/ after editing the genome (no rebuild). |
# 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.
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.
With a vision-capable model, reference an image path, Markdown
, or type /paste for your latest screenshot —
clipboard screenshots are auto-staged while a session runs.
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.
The core is provider-neutral: it talks to a ModelAdapter, never to a
provider directly. Pick the adapter explicitly in .env:
EVA_PROVIDER | Backend |
|---|---|
openai_chat | Any 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. |
anthropic | Anthropic 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. |
fake | Offline, deterministic — for smoke tests / dry runs (no key). |
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.
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.
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.
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.
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.
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.
| Module | Responsibility |
|---|---|
organism.py | The immutable kernel: seed · gates · promote · ledger · rollback. Out of the agent's reach. |
core.py | Provider-neutral turn loop + Event/Tool/ToolCall types. Knows nothing about providers or wire formats. |
adapters.py | ModelAdapters: openai_chat, anthropic (+ prompt caching), offline fake. |
tools.py | Canonical tools + sandboxed runtime + the explicit mode-policy table (who may write/shell/promote). |
human.py | HumanInterface + ApprovalPolicy (approve [y/N/f]) + host clipboard bridge. |
session.py | Append-only event log = the source of truth; image blobs kept out of the log. |
context.py | Deterministic, 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.py | Cross-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.py | Generated self-knowledge: anatomy/skills/capabilities/policy read from the live release on demand. |
tui.py | The status view — live, human-readable "what is EVA doing now", with Markdown rendering. |
agent.py | Wiring + the CLI/chat loop; the four modes; friction backlog and improve-pivot. |
supervisor.py | Release gates: required files, the ratchet, smoke, dry-runs, qualification rounds. |
tests.py | LLM-free checks — the ratchet itself (every promotion runs them). |
evals.py | Golden traces: recorded provider responses replayed through the real adapters + loop + runtime. |
A research experiment, not production software.