Zurück zu allen Artikeln

Notizen aus der Praxis

A lighter harness, built on Pi

I built a deliberately small agent harness on Pi-Agent and published it under Apache 2.0. It is a possible Hermes or OpenClaw replacement for persistent agents, without chasing feature parity.

A lighter harness, built on Pi

Most of my agents run on Hermes today, and Hermes is good. It is also big: lots of tools, lots of features, lots of configuration, and whatever sits in the context costs tokens on every turn, whether the task needs it or not. For a personal agent that lives in a chat room, runs a few cron jobs and answers API calls, I wanted less of that. So I built a harness of my own on top of Pi-Agent, and as of today it is public under Apache 2.0.

A minimal harness with a single carabiner on a dark surface

The VanillaCore Agent Harness is deliberately small. I see it as a possible replacement for Hermes in the way I run persistent agents, and for OpenClaw where people use it for the same job. It is not a clone of either, it does not aim for feature parity, and it does not try to cover every function those projects have. It covers what a persistent agent of mine actually uses and leaves the rest to Pi.

Why Pi

I have written before about using PI-Agent as the one harness for development, infrastructure and research. Pi's idea is that you adapt the harness instead of adapting to it. It ships a minimal system prompt, skills that load on demand, and extensions for everything it leaves out on purpose, such as sub-agents, plan mode and permission popups. If you need a command, a tool or a provider, you ask Pi to build it, reload, and keep going.

That self-modifiability is the reason I wanted Pi underneath. A coding agent that extends itself is useful at a terminal. The same property inside a long-running agent that lives in Matrix and takes runs from a control plane is worth a lot more, because that agent can grow the tools it needs in the place where it works.

The harness keeps Pi at arm's length on purpose. The Pi session runtime sits behind one adapter, src/runtime/pi.ts, and the rest of the harness talks to a small runtime interface; apart from that adapter, only the CLI borrows Pi's config-path helper. Pi is the runtime today, and the design leaves room to swap it later. OpenClaw already embeds Pi through its SDK, so the idea of putting a real agent product on top of Pi is not new; doing it with this little code is the point.

What the harness adds

Pi is a session engine. A persistent agent needs more than sessions, and that part comes from the harness.

It runs as one long-lived process with a FIFO queue per conversation, so each conversation gets one turn at a time while different conversations run in parallel. A periodic tick re-enters idle sessions for heartbeats, /loop prompts and standing /goals, which keeps the agent working between messages. When a message arrives during a running turn, /busy decides whether it waits in the queue, steers the running turn or interrupts it.

Identity comes from SOUL.md and tone from VOICE.md, with optional named personalities on top. Skills come from Pi's own discovery and from the harness home directory. Cron jobs use the Hermes cron/jobs.yaml format, and the Matrix adapter reads the same MATRIX_* variables and defaults as the Hermes adapter, so existing manifests carry over. The HTTP API speaks the Hermes gateway protocol with run submission, status, SSE events, stop, models and capabilities, which means AgentOS drives it through its existing hermes_gateway adapter. The gateway-relevant subset of Hermes slash commands is there too, from /new and /model to /goal, /cron and /approve.

Approvals are off by default. You can switch to manual approvals for all tools or for a named list, but the harness does not nanny anyone out of the box.

The boring parts

A persistent agent that loses messages is worse than no agent, so a large share of the code goes into delivery. Inputs are stored before the Matrix sync cursor advances, replies go through a durable outbox with stable transaction ids, and transient errors retry with backoff. An event that fails permanently is parked in a dead-letter file after five attempts instead of blocking the room. A turn interrupted by a crash is replayed up to three times; after that, the next interruption produces a failed reply instead of a crash loop. The documentation says plainly that this is at-least-once delivery, not exactly-once.

For containers there is a memory guard. On cgroup v2 with group OOM kills, running out of memory can take down the whole container, harness included. The guard watches the working set and, above a configurable threshold, kills the largest process the harness itself started, typically a runaway build launched by a tool, so the turn can continue. The operations guide calls it best-effort, and that is the right word.

The source is about 4,500 lines of TypeScript with four runtime dependencies, plus roughly 3,000 lines of tests. On a fresh checkout, all 171 tests pass and the typecheck is clean. Installation is documented for Linux, macOS, WSL2, Docker Compose and Kubernetes, and the container runs as a non-root user.

Token efficiency

Lighter should mean cheaper per turn. Pi's own system prompt is short, and the harness appends only identity, voice, an optional personality and a few lines of chat rules. Skills load on demand instead of sitting in every prompt. That is the design intent and the main reason for building this at all.

I do not have benchmark numbers yet, and I will not invent any. Once we have run the same agents on Hermes and on this harness, the measurements get their own post.

What is not there yet

This is version 0.1.0. Matrix works without end-to-end encryption, and media, edits of the agent's own messages and reactions as acknowledgements are still missing. There is no web UI and no Slack adapter yet. MCP and CLI management are planned, and so is self-modifying runtime version activation.

What works today is the file level of self-modification: The agent can edit its skills and persona files, new sessions pick those changes up, and /reload-skills rescans the skill catalog. Changed Pi extensions currently need a process restart, which /restart triggers through the supervisor. The harness does not yet swap its own runtime. The README and the configuration guide both say so, which is how a 0.1.0 should be documented.

Why smaller

Hermes and OpenClaw cover far more ground than this harness, and they should. That is not my goal. I want a harness I can read in an afternoon, on a runtime that extends itself, with the delivery guarantees a persistent agent needs, and when a feature is missing, the agent can build it on Pi instead of every turn paying for features I never use.

Sources used