← Back to blogs

Building Også - Week 0: An Agent Harness in Go

October 2, 2026•5 min read

Week zero of building my own terminal coding agent: what got built, what went wrong, and what's next.

aiagentsgollmcli

Introduction

The most common coding agent that everyone has been using is Anthropic's Claude Code. It is so popular that my team at work has gotten the enterprise subscription for everyone.

But here is a common misconseption, Claude Code is not the model, but it is the harness that makes it an agent. I'd write an article on that but unlike tokens, weights, temperature, embeddings etc... it is just some names given to things to sound smarter. Grab a model grab a harness boom you have an agent. I might be using agent instead of model interchangably btw.

Now I have no experience developing a harness, but I have a thought of how it works. Recently for work I have been working on Agentic RAG. Although I cannot disclose the use case, what really seemed to stand out about that was a pattern. We have the root agent, and all these subagents which have access to tools.

What if I can have a root agent, and give it a tool to , and edit? Have it go through a similar eval loop as agentic rag.

I did that and named it Også, and it's written in Go.

Two reasons for building another one. The open-model world is fragmented, and switching models usually means switching tools. I wanted one interface that works as a harness for whatever model is behind it. And the tools I tried first are npm packages. A coding agent has read and write access to my projects, and after this year's run of npm supply-chain attacks I didn't want the thing with the most access to have hundreds of transitive dependencies. Også has one dependency outside the Go standard library.

What got done

By the end of the week it can:

  • Talk to any model behind an OpenAI-compatible or Anthropic endpoint. Local Ollama models and hosted ones go through the same interface.
  • Check whether a model can actually use tools. When you pick one, Også sends a tiny probe with a fake tool. Models that error out get marked unsupported, models that call it are supported, and models that answer in text while ignoring the tool get marked unreliable. Unsupported models get no tools and a prompt that asks the user to paste code instead.
  • Search and read the project with glob, grep, and read, all confined to the project directory at the OS level. The model can ask for ../../.ssh/id_rsa and gets "outside the project directory" back. (Maybe we would like to give it view permissions at certain times but not now)
  • Edit and write files, with approval. Mutating tools produce a diff first. I see the diff, say yes or no, and a no goes back to the model as "the user declined; ask what they want instead."
  • Stop looping. If the model makes the same failing call twice, the error gets a note telling it to change approach. Three times and the turn ends.
  • Remember. Conversations are saved outside the project and can be resumed with ogsa -c or /resume.
  • See a map of the project. A ranked summary of the source files and their main definitions, about 1,500 tokens, goes into the system prompt so the model can go straight to the right file.
  • Write its own guide. /init has the model write a project description that gets loaded into every future session. Også wrote the one for its own repo and it's accurate.

Hurdles

Ctrl+C erased everything - Session Persistance

The first version kept the conversation in memory. I knew it was temporary. Then I lost a long debugging session to a reflex keystroke and built session persistence the next morning.

Cancelling threw away finished work - History Management

Subtler version of the same problem. When I cancelled mid-turn, the loop returned an error and no messages, so tool results that had already come back were dropped. Resuming put the model back before work it had already done. Now the loop always returns what it accumulated, and any tool calls that didn't get to run are recorded as "cancelled before this ran" so the history stays consistent.

The model read everything - Build a Repo Map

With only glob, grep, and read, a question like "how does model selection work?" turned into eight tool calls: list all the Go files, read half of them, grep, read more. On a local model with a small context window that's slow and expensive. Watching the tool call log is what convinced me to build the repo map. It cut most questions to one or two reads and was the biggest quality jump of the week.

I guessed instead of looking - :)

My day-one notes say "I believe each file gets embedded for faster searching." I spent a morning thinking about vector search before I watched what existing tools actually do, which is mostly grep. If I'd started by logging tool calls from the agent I already use, I'd have saved half a day.

What I learned

  • The loop is the easy part. The core agent loop is about 80 lines. Everything else is making it safe, cheap, and resumable.
  • Write error messages for the model. "File not found" leads to retries. "File not found: paths are relative to the project root, try glob to locate it" leads to a different next call.
  • Small models need more structure. The failure guard, the capped search results with "narrow the search" hints, and the map all exist because a 7B model will flail without them. The big hosted models barely need any of it, which is exactly why it's easy to forget.
  • Build the fence first. The path sandbox was the first thing I wrote in the tools package and I never had to think about it again.

Next steps

I really don't know. I think I still lack confidence in the tool writing itself. It did edit the README and that was cool. I think I will simply try to use it and if it does something wrong or if it needs another tool then I'll build it.