# Varela: Neuromancer-inspired self-mutating coding harness

> Source: <https://github.com/ajensenwaud/varela/>
> Published: 2026-07-04 05:31:21+00:00

A minimalistic, self-mutating agent harness in the spirit of [Pi](https://github.com/badlogic/pi-mono):
plain JavaScript, **zero dependencies**, a WASM sandbox for untrusted code, an
always-on daemon, multi-agent orchestration, and a model router that speaks to
Anthropic, OpenAI and OpenRouter — via OAuth or API keys.

Its trick: varela **rewrites its own source while running** (and hot-reloads),
logs every run, and turns repeated wins into new tools and repeated failures
into lessons — so it measurably gets better the longer you use it. And you can
watch it work in a **3D cyberpunk world** where the agent is a person and
fanning out spawns more of them, live.

Drop a screen-recording of`varela --web`

here — the world with avatars fanning out is the demo that sells it.`git add docs/demo.gif`

.

```
# Node ≥ 20. No npm install needed (zero deps).
node bin/varela.js import     # reuse an existing Claude Code / Codex login…
node bin/varela.js login      # …or sign in fresh (OAuth or API key)
node bin/varela.js            # start chatting
node bin/varela.js --web      # …or explore the 3D world
```

Already use Claude Code or Codex? `varela import`

copies those logins in (varela
uses the same client IDs, so their tokens work as-is). `--claude`

/ `--codex`

to pick one. It only reads those stores when you run it — never automatically.

That's the 30-second path. Everything below is depth.

```
varela login        # sign in to a provider (OAuth or API key)
varela              # full-screen chat that fills the terminal (--plain for a line console)
varela --tui        # full-screen split-view UI (file tree + chat + preview)
varela --dash       # mission-control dashboard (agent tree, sparklines, growth)
varela --web        # 3D cyberpunk world UI (reuses a running daemon, else starts one)
varela -p "task"    # one-shot mode
varela --daemon     # WebSocket daemon: scheduler + heartbeat + serves the 3D world
varela hub [port]   # run a mesh rendezvous hub for the P2P human-agent world
```

Requires Node.js ≥ 20 (uses `node:wasi`

, `fetch`

, ESM). No `npm install`

needed.

The harness runs in place — the checkout **is** the install (it self-mutates).
The installers only put a `varela`

launcher shim on your PATH:

```
# Windows
.\install.ps1            # shim in %LOCALAPPDATA%\varela\bin + user PATH
.\install.ps1 -Daemon    # …and register the daemon at logon (Task Scheduler)
.\install.ps1 -Uninstall
# Linux/macOS
./install.sh             # shim in ~/.local/bin
./install.sh --daemon    # …and enable a systemd user service for the daemon
./install.sh --uninstall
```

No installer? Add `scripts/`

to your PATH — it holds repo-relative launchers
([scripts/varela.cmd](/ajensenwaud/varela/blob/main/scripts/varela.cmd), [scripts/varela](/ajensenwaud/varela/blob/main/scripts/varela)) —
or just run `node bin/varela.js`

. `npm install -g .`

(or the packed tarball —
`npm pack`

produces a ~53 kB package) also works; note that a global install
relocates the harness root, so self-mutations then edit the globally installed
copy, and running from a git checkout keeps them version-controlled.

-
**Agent loop**([src/agent.js](/ajensenwaud/varela/blob/main/src/agent.js)) — sends the conversation plus tool schemas to the routed model, executes tool calls (dangerous ones require confirmation), loops until the model stops calling tools. -
**Model router**([src/router.js](/ajensenwaud/varela/blob/main/src/router.js)) — resolves`provider/model`

strings, manages credentials and token refresh. Built-in providers:`anthropic`

— OAuth (Claude Pro/Max sign-in, PKCE) or API key`openai`

— OAuth (ChatGPT sign-in, PKCE, localhost callback) or API key`openrouter`

— OAuth PKCE key exchange or API key- Drop a JS module in
`~/.varela/extensions/providers/`

to add another; it just needs to default-export`{ id, models, login, refresh, chat }`

. The internal message format is Anthropic-style content blocks; shared converters for OpenAI-compatible APIs live in[src/providers/openai-compat.js](/ajensenwaud/varela/blob/main/src/providers/openai-compat.js).

-
**WASM sandbox**([src/wasm/sandbox.js](/ajensenwaud/varela/blob/main/src/wasm/sandbox.js)) — runs WASI preview1 modules from`~/.varela/wasm`

with**no** filesystem or network access by default. This is where generated/untrusted code should run; the`shell`

tool is for trusted host automation. -
**Self-mutability**— the harness can rewrite itself while running:`self_list`

/`self_read`

/`self_edit`

— inspect and patch varela's own source (with backup + rollback if the edited file fails to load).`create_tool`

— write a new tool into`~/.varela/extensions/tools/`

.- Both hot-reload the tool registry and providers immediately (cache-busted
dynamic
`import()`

), so the agent can use a capability it just built in the very next step.

-
**Console**([src/cli/chat.js](/ajensenwaud/varela/blob/main/src/cli/chat.js)) — the default`varela`

opens a**full-screen chat** that fills the terminal, Pi-style:**no alternate screen**, so finished messages commit to your terminal's normal scrollback (native mouse/PageUp scroll and copy keep working) while only a small bottom region is redrawn in place inside synchronized-output frames. Built on its own input engine ([keys.js](/ajensenwaud/varela/blob/main/src/cli/keys.js)+[editor.js](/ajensenwaud/varela/blob/main/src/cli/editor.js)):**Esc interrupts** a running turn — the model fetch aborts, partial output is kept in the conversation. Esc also clears input; double-Esc shows the rewind menu (`/rewind`

drops turns,`/undo`

reverts files).**Multiline input**:`\`

+Enter or Ctrl+J for newlines, bracketed paste (multi-line pastes just work),**Ctrl+G opens $EDITOR/$VISUAL**(vim, notepad, …) to compose, full readline keys (Ctrl+A/E/K/U/W/Y, Alt+B/F, Ctrl+←/→) and an optional** vim mode**(`editorMode`

in`/configure`

).with a fuzzy-search menu; mentioning an image file (`@`

file mentions`@shot.png`

) attaches it as**vision input**.:`!`

shell mode`! npm test`

runs directly, shows output, and adds it to the conversation context — no model round-trip.**Type-ahead command menu**(↑/↓, Tab; Enter runs the highlighted command),** Ctrl+R reverse history search**, history persisted across sessions.** Shift+Tab cycles permission modes**:`plan`

(read-only — mutating tools are denied and the agent proposes) →`default`

(confirm) →`acceptEdits`

(file edits auto-approve) →`yolo`

. File edits show a**colored diff** before you approve them.- Status line shows model · mode · messages · $/day ·
**context meter**· open todos;** Ctrl+O**expands recent tool results,** Ctrl+T**shows the agent's todo checklist (`update_todos`

tool); code blocks get lightweight**syntax highlighting**;`/theme`

switches palettes; an**OSC 9 desktop notification** fires when a turn finishes while the terminal is unfocused. `/btw <q>`

asks an ephemeral side question (sees the conversation, never joins it);`/web <url>`

pulls a page into context as text.

`--plain`

([src/cli/repl.js](/ajensenwaud/varela/blob/main/src/cli/repl.js)) drops to a line-by-line readline console (used automatically when output is piped). Both honor`NO_COLOR`

. -
**Daemon + attach**([src/net/daemon.js](/ajensenwaud/varela/blob/main/src/net/daemon.js)) —`varela --daemon`

hosts the runtime behind a localhost WebSocket (hand-rolled RFC 6455,[src/net/ws.js](/ajensenwaud/varela/blob/main/src/net/ws.js); port`daemon.port`

, default 7717, token-authenticated via`~/.varela/daemon.token`

). Starting`varela`

while a daemon runs auto-attaches: the REPL becomes a thin client, the conversation and scheduler live in the daemon, several clients can attach at once, and background output (scheduler, heartbeat, sub-agents) is broadcast to all of them. Interactive prompts (`/login`

,`/configure`

, tool confirmations) round-trip over the socket. -
**Sub-agents**—`/delegate build the parser :: write its tests`

spawns one sub-agent per`::`

-separated task; they run in parallel with fresh conversations and report back to your screen as they finish (`/delegate`

alone lists them). The model can fan out itself via the`delegate`

tool (waits for all children, max 8). Sub-agent tool calls are auto-approved. -
**Orchestration**([src/orchestrator.js](/ajensenwaud/varela/blob/main/src/orchestrator.js)) —`/orchestrate <objective>`

runs a full**plan → capped parallel fan-out → synthesize** cycle: the model decomposes the objective into independent subtasks, they run as sub-agents with a concurrency cap (default 3), and their reports are synthesized into one answer. Also available to the model as the`orchestrate`

tool. Every stage emits world events, so the 3D world animates the whole fan-out. -
**Personality**—[AGENTS.md](/ajensenwaud/varela/blob/main/AGENTS.md)at the harness root defines varela's voice and disposition; it's appended to the system prompt. Add`~/.varela/AGENTS.md`

for personal overrides. Since the harness is self-mutating, varela can be asked to revise its own personality. -
**Context compaction**— long conversations auto-summarize once they pass`compactAt`

(~80k tokens by default): older turns are compressed into a context block, the recent turns kept verbatim, always cut at a clean turn boundary.`/compact`

forces it. -
**Sessions**([src/sessions.js](/ajensenwaud/varela/blob/main/src/sessions.js)) — every conversation persists to`~/.varela/sessions/`

;`/sessions`

lists them,`/resume <id>`

continues one after a restart (REPL or daemon),`/clear`

starts a new one. -
**MCP client**([src/mcp/client.js](/ajensenwaud/varela/blob/main/src/mcp/client.js)) — declare servers in`~/.varela/mcp.json`

and their tools appear as`mcp__<server>__<tool>`

(stdio transport, newline-delimited JSON-RPC, zero deps). Read-only-annotated tools skip confirmation; everything else asks.`/mcp`

shows status. Server connections survive hot reloads. -
**Cost tracking**([src/usage.js](/ajensenwaud/varela/blob/main/src/usage.js)) — every reply's token usage is priced (editable table,`config.prices`

overrides) into a session total and a 90-day daily ledger.`/usage`

breaks it down;`dailyCapUsd`

in`/configure`

sets a hard daily spend cap that stops the loop when hit — essential for unattended daemon operation. The status bar above each prompt shows model · messages · today's spend · running sub-agents · heartbeat. -
**Self-improvement loop**([src/trajectory.js](/ajensenwaud/varela/blob/main/src/trajectory.js)) — every episode (prompt, tool calls, outcome, duration, errors) is logged to`~/.varela/trajectories/*.jsonl`

.`/good`

and`/bad`

grade the last episode;`/reflect`

distills recent failures into`~/.varela/LESSONS.md`

(loaded into the system prompt, so mistakes stop repeating);`/distill`

turns repeated successful tool patterns into new purpose-built tools via`create_tool`

. The trajectory format doubles as SFT-ready data if you later fine-tune a local model. -
**WASM tools as an extension format**— drop`<name>.wasm`

+`<name>.json`

(manifest:`{name, description, parameters, module, dangerous?, preopenDir?}`

) into`~/.varela/extensions/wasm-tools/`

; input arrives as JSON on stdin, stdout is the result, and execution is sandboxed (no fs/network unless the manifest grants a directory). Tools can be written in any language that compiles to WASI — including by varela itself. -
**Terminal markdown**([src/cli/md.js](/ajensenwaud/varela/blob/main/src/cli/md.js)) — assistant replies render headings, bold, inline code, fenced code blocks, lists and quotes, including line-by-line while streaming. -
**Git-backed undo**([src/undo.js](/ajensenwaud/varela/blob/main/src/undo.js)) — before`write_file`

,`edit_file`

,`self_edit`

or`create_tool`

touches a file, the pre-change content is snapshotted: into the enclosing repo's git object store when there is one (`git hash-object -w`

— no branches or index touched), else into a content-addressed store under`~/.varela/undo-store`

.`/undo`

reverts the last change-set (`/undo 3`

the last three,`/undo list`

shows the journal); files that were created get deleted again.`shell`

output is not covered — that's what real commits are for. -
**3D cyberpunk web world**([src/web/](/ajensenwaud/varela/blob/main/src/web)) —`varela --web`

opens a browser onto a neon-grid city (Three.js + bloom, loaded from a CDN — the harness itself stays dependency-free) that is a**full client to the daemon**: the same chat, streaming, slash commands and tool confirmations as the console. The agent is a person on the plaza; when it spawns sub-agents (`/delegate`

,`/orchestrate`

, or the model deciding to),**avatars appear on the fly**, glow as they think / call tools / speak, and drift away when done — driven by the runtime's world-event bus over the WebSocket. The surrounding city is your**workspace**: files and directories become buildings (sized by bytes, colored by type), and when the agent touches a file its avatar** walks to that building**and it lights up.** Record**the canvas to a shareable`.webm`

, or**replay** the last run as a cinematic — both from the HUD. Navigate with**WASD / arrows**,** mouse look**(click the scene for pointer-lock),** Space/Shift**for up/down,** Tab**to jump to chat,** Esc**to release. Served by the same daemon that hosts the console/TUI, so every surface shares one live conversation. -
**Mesh — a P2P human-agent world**([docs/mesh.md](/ajensenwaud/varela/blob/main/docs/mesh.md),[src/mesh/](/ajensenwaud/varela/blob/main/src/mesh)) — agents on different machines meet in a shared world, each still owned and controlled by its human, each exposing a**chosen subset of its skills** for others' agents to consume. A peer's skill appears in your agent's registry as`peer__<name>__<skill>`

and calls RPC to the owner's daemon, which runs it and returns only the result —**credentials, files and shell never leave the owner's machine.** Discovery via a thin`varela hub`

;**direct peer-to-peer WebSocket links** where reachable (hub only as fallback, no relay in the data path). Ed25519 identity, signed messages, and a policy gate: exposure is off by default,`public`

is recommended only for wasm-sandboxed skills, everything else is`confirm`

/`allowlist`

with rate + cost caps, full audit, and`/mesh pause`

. In the 3D world, peers are avatars you can see and whose skills you can invoke. Configure shared skills in`~/.varela/mesh/world.json`

;`/mesh`

to manage. (WebRTC/NAT traversal deferred to keep the harness zero-dependency — see the doc.) -
**Mission-control dashboard**([src/cli/dashboard.js](/ajensenwaud/varela/blob/main/src/cli/dashboard.js)) —`varela --dash`

is a full-screen terminal dashboard (not a chat log): a live agent tree, cost/token**sparklines**, growth stats, the scheduler timeline and a streaming activity pane, all fed by the same world-event bus. For the SSH/headless user who lives in a terminal.`Tab`

to type a task/command. -
**Growth stats**([src/stats.js](/ajensenwaud/varela/blob/main/src/stats.js)) — the "gets better the longer it runs" numbers (skills built, lessons learned, episodes, feedback rate), surfaced via`/stats`

, the status bars, and the web/dashboard headers. -
**Split-view TUI**([src/cli/tui.js](/ajensenwaud/varela/blob/main/src/cli/tui.js)) —`varela --tui`

opens a full-screen, three-panel interface on the alternate screen buffer (zero deps, one write per frame): a**file tree**(left, expandable), the** conversation**(right, streaming markdown, scrollable), and a** file preview**(right-bottom, line-numbered, opens when you pick a file).`Tab`

cycles focus, arrows navigate/scroll the focused panel,`Enter`

opens a file or sends a message,`Ctrl+O`

drops the previewed file's path into your prompt,`Esc`

closes the preview,`Ctrl+C`

quits. Same runtime as the console (agent, scheduler, sessions, cost tracking), just a richer front-end. The ANSI-aware layout helpers (`fit`

,`wrapAnsi`

,`layout`

) are unit-tested. Like the console, it**attaches to a running daemon** if one is up (streaming and slash commands round-trip over the WebSocket, conversation lives in the daemon) and hosts a local runtime otherwise. -
**Scheduler**([src/scheduler.js](/ajensenwaud/varela/blob/main/src/scheduler.js)) — persistent scheduled tasks (`~/.varela/tasks.json`

) checked every 15s while varela runs, in the console or headless via`varela --daemon`

. Specs:`every 5m`

,`daily 09:30`

,`in 10m`

,`at 2026-07-02T15:00`

. Each run gets a fresh conversation and**auto-approves tools**(nobody is at the prompt) — schedule accordingly. -
**Heartbeat**— periodic autonomous check-in (`/heartbeat on 30m`

). Each beat the agent reads`~/.varela/HEARTBEAT.md`

and carries out whatever standing instructions are due; if the file is missing or empty the model call is skipped entirely, so an idle heartbeat costs nothing.

| command | |
|---|---|
`/login <provider> [oauth|key]` |
authenticate (OAuth is the default) |
`/logout <provider>` |
remove stored credentials |
`/providers` |
login status per provider |
`/model [provider/model]` |
interactive model picker, or switch directly |
`/configure` |
interactive settings editor (model, heartbeat, daemon port, …) |
`/delegate <t1> [:: <t2> …]` |
spawn parallel sub-agents; bare `/delegate` lists them |
`/orchestrate <objective>` |
plan → capped parallel fan-out → synthesize |
`/stats` |
growth: skills built, lessons learned, episodes, feedback |
`/mesh [status|connect|peers|pause|block <id>]` |
the P2P human-agent world |
`/status` |
model, session, spend, sub-agents, MCP, heartbeat at a glance |
`/sessions` · `/resume <id>` |
list / continue saved conversations |
`/compact` |
force context compaction |
`/mcp [reload]` |
MCP server status (config: `~/.varela/mcp.json` ) |
`/usage` |
tokens + cost, session and today, per model |
`/good [note]` · `/bad [note]` |
grade the last episode (feeds self-improvement) |
`/reflect` |
turn recent failures into lessons (loaded into the system prompt) |
`/distill` |
turn repeated successful patterns into new tools |
`/undo [n|list]` |
revert the last n file mutations (git-backed) |
`/rewind [n]` |
drop the last n conversation turns |
`/mode [plan|default|acceptEdits|yolo]` |
permission mode (Shift+Tab cycles) |
`/btw <question>` |
ephemeral side question — never joins the conversation |
`/web <url>` |
fetch a page as text into context |
`/theme [neon|mono|ocean]` |
console theme |
`/models` |
model suggestions per provider |
`/tools` |
list loaded tools (⚠ = needs confirmation) |
`/schedule` |
list scheduled tasks |
`/schedule add <spec> :: <prompt>` |
e.g. `/schedule add daily 09:00 :: tidy my downloads` |
`/schedule rm <id>` · `/schedule run <id>` |
remove / run now |
`/heartbeat [on|off] [interval]` |
e.g. `/heartbeat on 15m` |
`/reload` |
hot-reload tools, extensions, providers |
`/yolo` |
toggle auto-approval of dangerous tools |
`/clear` |
reset the conversation |
`/config` , `/help` , `/exit` |
the obvious |

Environment variables `ANTHROPIC_API_KEY`

, `OPENAI_API_KEY`

,
`OPENROUTER_API_KEY`

are picked up automatically if you never run `/login`

.

`read_file`

· `write_file`

· `edit_file`

· `list_dir`

· `shell`

(PowerShell on
Windows, bash on Linux) · `wasm_run`

/ `wasm_list`

· `delegate`

· `orchestrate`

·
`self_list`

/ `self_read`

/ `self_edit`

· `create_tool`

— plus anything from
JS extensions, WASM manifests, and MCP servers.

```
npm test        # node --test, 18 tests
```

Unit tests live in [test/varela.test.mjs](/ajensenwaud/varela/blob/main/test/varela.test.mjs); integration
tests ([test/integration.test.mjs](/ajensenwaud/varela/blob/main/test/integration.test.mjs)) spawn the
daemon-protocol and TUI-attach harnesses in [tools/itest/](/ajensenwaud/varela/blob/main/tools/itest) as
child processes, each with an isolated `VARELA_HOME`

and an ephemeral daemon
port, so they never touch your real `~/.varela`

or a running daemon.

```
bin/varela.js            entry point (REPL, -p one-shot, --daemon)
AGENTS.md                personality (appended to the system prompt)
src/surface.js           the Surface primitive — the I/O seam (one runtime, many front-ends)
src/kernel.js            shared services: config · router · registry · tracker · events
src/agents.js            conversation layer: agent loop + sub-agents + sessions + todos
src/agent.js             the Agent (streaming, tool confirmation, permission modes)
src/runtime.js           buildRuntime — the assembler that wires kernel + conv + scheduler + mesh
src/commands.js          slash commands (shared by REPL and daemon)
src/router.js            model router + credential handling
src/providers/           anthropic.js, openai.js, openrouter.js, openai-compat.js, sse.js
src/auth/oauth.js        PKCE, loopback callback server, token refresh helpers
src/net/                 ws.js (RFC 6455), daemon.js
src/scheduler.js         scheduled tasks + heartbeat
src/sessions.js          session persistence
src/usage.js             token/cost ledger + spend caps
src/trajectory.js        episode logging + feedback (self-improvement substrate)
src/undo.js              git-backed file snapshots + /undo
src/orchestrator.js      plan → fan-out → synthesize
src/stats.js             growth stats (skills, lessons, episodes, feedback)
src/workspace.js         bounded workspace snapshot (files → city buildings)
src/cli/dashboard.js     mission-control dashboard (--dash)
src/mesh/                P2P world: identity, protocol, hub, link, remote tools
src/web/                 3D cyberpunk world (index.html, app.js, style.css)
src/net/static.js        static file server for the web world
src/mcp/client.js        MCP client (stdio JSON-RPC)
src/tools/               registry + built-ins (self-mutation, delegate, wasm, shell)
src/wasm/sandbox.js      WASI sandbox
src/cli/                 repl.js, tui.js (split-view), md.js, banner.js, ansi.js
test/                    node --test suite
~/.varela/               config.json, credentials.json, daemon.token, tasks.json,
                         sessions/, trajectories/, usage.json, mcp.json,
                         undo.jsonl, undo-store/,
                         AGENTS.md + LESSONS.md (optional), extensions/, wasm/
```

A tool extension (`~/.varela/extensions/tools/fetch_url.js`

):

```
export default {
  name: 'fetch_url',
  description: 'HTTP GET a URL and return the body as text.',
  parameters: { type: 'object', properties: { url: { type: 'string' } }, required: ['url'] },
  dangerous: false,
  async execute({ url }) {
    return (await fetch(url)).text();
  },
};
```

The agent can (and will) write these itself via `create_tool`

.

- OAuth flows use the public client IDs of the vendors' own CLIs (Claude Code, Codex). Anthropic OAuth uses a paste-the-code flow; OpenAI and OpenRouter use a localhost callback. Use them only with accounts/plans that permit CLI access; API keys always work.
**OpenAI has two paths, chosen by credential kind.** An**API key** uses the standard chat-completions API. A**ChatGPT/Codex OAuth login**(`varela import --codex`

, or`varela login openai`

) is routed to OpenAI's Codex backend (`chatgpt.com/backend-api/codex/responses`

, the Responses API) — those tokens are rejected by the standard API (no pay-as-you-go billing) but are included with the ChatGPT subscription. Use a plain model id with the subscription (`/model openai/gpt-5.5`

); codex-suffixed ids like`gpt-5-codex`

are refused on ChatGPT accounts. See[openai-responses.js](/ajensenwaud/varela/blob/main/src/providers/openai-responses.js).- Replies stream token-by-token in the console and over the daemon socket; scheduled/sub-agent runs are non-streaming (their output is a report).
- The daemon binds to 127.0.0.1 only and requires the token in
`~/.varela/daemon.token`

; anything that can read your home directory can attach — it's a local control socket, not a network service. - Credentials are stored in plaintext at
`~/.varela/credentials.json`

(mode 0600 on POSIX). Consider OS keychain integration for anything serious.
