Model or dataset
unreallabsai/unreal-agent avatar
unreallabsai/unreal-agent

unreal-agent puts every tool call behind a serializable operation

Async-first agent harness

2,211 stars132 forksGoMIT

At a glance

What is it?
unreal-agent is a Go agent harness from Unreal Labs that turns a validated tool call into serializable operations instead of work that blocks the coordinator loop. The contract is unusually precise, and so are the gaps: the tool set is fixed, the persistence back end is yours to supply.
Who is it for?
Adopt unreal-agent when you run agents as a service and your tools are slow, remote or crash-prone, because the harness forces every call through a serializable operation instead of the event loop and leaves you a forkable history. Leave it alone for a local script that should finish on the next line, where versioned sessions, inbox deduplication and an operation manager buy nothing.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • unreallabsai/unreal-agent2,211 ★ · +14
Star history
Repository2026-10-142026-10-23
unreallabsai/unreal-agent2.2k2.2k

14 Stars in the last 9 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The coordinator turns a validated tool call into queued work

An agent that calls tools is doing two different jobs inside one turn: deciding what to call, and doing the call. unreal-agent draws a deliberate line between them. Its glossary splits a tool call, which is a model-produced request, from a tool translator, which validates that request and rewrites it into one or more operations, and from an operation, a serializable description of work destined for asynchronous execution. Because operations are serializable and versioned, the process that executes them need not be the process that produced them, which is how one library under `harness/` carries both executables in `cmd/`: `cmd/unreal-agent-runner` for cloud agent work and `cmd/unreal-agent-tui` for the terminal. Assume you already have a model provider and want the loop around it. The last push was on 2026-10-06, the same day the v0.3.1 tag landed, and the repository is not archived.

Inbox deduplication lives in memory and dies with the process

Deduplication here rests on an identifier the caller supplies. An Input is defined as an event carrying a caller-supplied globally unique ID that stays stable across redeliveries, and the `Inbox` as session-scoped, in-memory deduplication of external, control, and crash inputs. Crash inputs are where that design bites hardest, because a crash is the exact event that makes work arrive twice, and the structure absorbing them is the one labelled in-memory. Durable state sits one layer down, where the coordinator persists accepted inputs into the session store. The README does not say whether an ID accepted before a crash is rechecked against the persisted record after the restart, so what you can read off the design is narrower than the glossary wording: the volatile half of the deduplication is gone on every restart, and crash safety rests entirely on what the session store kept. Because the inbox is scoped to a session, a brand new session also starts with an empty deduplication table even when the event source replays old work.

Tool translators run on the event loop without I/O

A tool translator runs synchronously on the coordinator's event loop and must not perform I/O or suspend it. The split pays off in how results travel back, because a tool call status is not an execution result: the status is the translation outcome, either a validation error or references to submitted operations, execution state is tracked separately, and the translator formats both into something the model can read. The model learns that its work was accepted, and the later outcome arrives by another road.

Here is the tension worth naming. The tool registry owns a fixed Bash, ViewImage, and skill-use set and exposes whichever of those the host selected, which sits awkwardly beside the claim that components are composable and that alternative implementations of their interfaces are encouraged. Open interfaces, closed tool inventory. Adding a capability means writing another implementation of the registry interface instead of adding an entry to a list, and the repository root carries no plugin directory, no tool manifest and no discovery file. The usual alternative is to run a handler inline inside the turn, which is less code and works until a tool takes seconds or has to reach another machine; this harness forbids that inline path and pays for the ban with an operation queue.

A proxy operations manager can move work to a sandbox

Operation management is an actor runtime for durable operations, and its local implementation is swappable. That one line is what lets the design leave a single machine. The worked example in the documentation has a proxy operations manager send serialized operations to a local operations manager running in a process inside a remote sandbox, so tools execute over there while the coordinator stays put, and the two topologies differ in nothing but where the actor runs. The invariants the project intends to hold make that swap defensible, namely that session-store items are serializable under a versioned storage format, that operations are versioned and always serializable, and that session compatibility is maintained on a best-effort basis. What the documentation leaves open is the storage question. No default durable back end for operation state is named anywhere, and the context builder is explicitly barred from taking persistence dependencies, so a team running many concurrent sessions supplies its own store before the word durable means anything. For a single local script that needs nothing but a shell tool, all of this costs more than it returns.

The context builder hands back a record of what it dropped

A context builder assembles model input in memory, statefully, and returns two things together: the input itself and a record of anything omitted, truncated, or compacted on the way. It performs no I/O and accepts no persistence dependencies, which reads like a restriction until you notice what it buys, since the compaction decision then comes from state already in memory rather than from a query at send time. The record is the safeguard. A harness that silently drops context hands the model a partial world and says nothing about it, and this design makes the loss inspectable at the boundary where it happens.

The gap sits on the far side of that boundary. No component in the table is named as the consumer of the record, and nothing states whether an omission reaches an operator, an event stream, or only the next turn's input. Authentication, cancellation, and provider errors belong to the LLM Adapter, the one component described as talking to a provider at all, which leaves context assembly deliberately ignorant of both the wire and the disk.

Session history forks, and unknown versions fail at resume

A session is append-only persisted history that can be forked, and the session store owns it along with operation state, recovery, and the atomic recording of tool-call status together with its operations. That last part is the one with teeth, because writing a status and its operations in one atomic step is what stops a crash from leaving behind a call the model believes was submitted and that nothing can execute. Forking then hands you a branch of that history for a different prompt or a regression case, without mutating what came before it.

The compatibility policy is blunt about its limits. Backwards compatibility for sessions is best effort, yet an unsupported session version will always cause an explicit error on resume, so a stale file fails at load time instead of half-restoring, and a harness that refuses loudly beats one that resumes into a corrupted turn. Whether a fork carries in-flight operation state with it, or only committed history, is not stated anywhere in the README.

The image installs the runner and leaves the TUI behind

The Makefile builds two binaries into `bin/`, the runner and the TUI, in a single target that calls Go at the repository root and again inside `cmd/unreal-agent-tui`. The Dockerfile builds exactly one of them, compiling `./cmd/unreal-agent-runner` with `CGO_ENABLED=0` and `-trimpath`, then copying that one file into a Debian slim base whose entrypoint is the runner alone, so the terminal client has no container path in the repository as it stands and the image covers a headless worker only. The build stage runs on a Go 1.27.1 builder while go.mod declares 1.27.0, a one patch gap that keeps the compiled binary inside the module's own floor.

The runtime side is pinned rather than configurable, with a fixed 10001:10001 user, a working directory at `/workspace`, and three directories created at build time where `/state` is left mode 1777 for state that has to outlive the process:

dockerfile
COPY --from=build /out/unreal-agent-runner /usr/local/bin/unreal-agent-runner
ENV HOME=/home/agent SHELL=/bin/bash XDG_STATE_HOME=/state
USER 10001:10001
WORKDIR /workspace
ENTRYPOINT ["/usr/bin/tini", "--", "unreal-agent-runner"]

Those five lines are the entire runtime contract the project commits to. The build stage is narrower still, copying `go.mod`, `go.sum`, `cmd/`, `harness/` and `internal/` and nothing else, so anything under `third_party/` and the runners in `benchmarks/` stays outside what the image compiles. A comment beside the runtime base records Debian 13.7 trixie and an image build date of 2026-09-18.

Building and testing from the repository Makefile targets

There is no install command to copy here, and for a Go library that is ordinary: the module is `github.com/unreallabsai/unreal-agent`, `go.mod` asks for Go 1.27.0, and the whole dependency list fits on one screen:

go
module github.com/unreallabsai/unreal-agent

go 1.27.0

require (
	github.com/oapi-codegen/runtime v1.6.0
	go.yaml.in/yaml/v3 v3.0.5
	golang.org/x/image v0.46.0
	golang.org/x/sys v0.48.0
)

A user who wants the two binaries runs the three targets the repository ships, and the first result worth checking for is a pair of files under `bin/`:

makefile
build:
	go build -trimpath -o bin/unreal-agent-runner ./cmd/unreal-agent-runner
	go -C cmd/unreal-agent-tui build -trimpath -o ../../bin/unreal-agent-tui .

test:
	go test -race ./...
	go -C cmd/unreal-agent-tui test -race ./...

check:
	go vet ./...
	go -C cmd/unreal-agent-tui vet ./...

The race detector carries more weight here than in an average service, because translator work shares an event loop with the coordinator by contract. The check target also fails when gofmt lists anything under `cmd`, `harness` or `internal`, so formatting is enforced by the same command. No CLI flags, ports, or config file appear anywhere in the documentation: the only settings visible in the repository are the three environment variables baked into the image, and the only TUI-specific material is the directory the Makefile reaches with `go -C`. The project ships an MIT LICENSE file, so code linked against the harness carries no copyleft condition.

Editorial conclusion

Adopt unreal-agent when you run agents as a service and your tools are slow, remote or crash-prone, because the harness forces every call through a serializable operation instead of the event loop and leaves you a forkable history. Leave it alone for a local script that should finish on the next line, where versioned sessions, inbox deduplication and an operation manager buy nothing. Verify first that your own tool registry can satisfy the translator contract, then run `go test -race ./...` and the gofmt check over cmd, harness and internal before you trust it with sessions that matter.

Frequently asked questions

What is unreal-agent and who is it built for?

unreal-agent is an async-first agent harness written in Go. The library sits in harness/, cmd/ holds a cloud agent runner and a terminal client, and it is aimed at engineers who already have a model provider and need the loop around it.

Can I add my own tool to unreal-agent?

Not by editing a list. The tool registry owns a fixed set of Bash, ViewImage and skill-use definitions and exposes the subset the host picks, so a new capability arrives by supplying another implementation of the registry interface, which is what the project means by composable components.

How do I build unreal-agent and run its tests?

The repository ships three Makefile targets. Build produces bin/unreal-agent-runner and bin/unreal-agent-tui, test runs the suite with the race detector across both trees, and check runs go vet plus a gofmt check, while go.mod asks for Go 1.27.0.

What happens when I resume a session written by an older version?

Backwards compatibility for sessions is a best-effort promise, and an unsupported session version will always cause an explicit error on resume. A stale session file therefore fails at load time rather than half-restoring.

Do I need Docker to run unreal-agent?

No. The Makefile builds both binaries straight from the tree, and the Dockerfile is one packaging path that copies the runner alone into the image. Nothing in cmd/unreal-agent-tui is covered by any image in the repository.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. unreallabsai/unreal-agent on GitHub
Community

Where developers are discussing it

Posts on Hacker News, dev.to and Lobsters that link to this repository, found by our scan of those communities. 0 in the last 30 days; newest first.

Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/unreallabsai-unreal-agent.svg)](https://hysenlabs.com/projects/unreallabsai-unreal-agent)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/unreallabsai-unreal-agent.svg)](https://hysenlabs.com/projects/unreallabsai-unreal-agent)

Was this useful?