Adding a New Capability to Our AI Agent Is a Two-File Change. Here's Why That's the Point.
When a client asks for one new thing their AI agent should be able to do, the honest answer to "how long will that take" depends entirely on how narrow the underlying tool interface is.
A client asks for one new thing their AI agent should be able to do. Maybe it's looking something up, maybe it's taking a specific action. The honest answer to "how long will that take" depends almost entirely on one architectural decision made long before that request ever came in: how narrow and disciplined the tool interface is. On our harness, adding a new capability is deliberately a small, contained change, one new file describing the capability, one line wiring it into a product, not a change to the reasoning loop itself. That's not an accident, it's the specific thing the interface was designed to guarantee.
The interface every capability has to fit
Every tool the harness can call implements the same narrow contract: a name, a description, a typed input shape, and a single method that actually does the work. Alongside that is one more property, whether the tool requires human approval before it runs, which defaults to true unless a tool deliberately opts out. That's the entire interface. Nothing about it knows or cares what a specific tool does internally, whether it's fetching a video transcript, querying a curriculum database, or looking up saved marketing references. As far as the reasoning loop is concerned, every tool looks identical: something with a name, a description the model reads to decide when to use it, and a function to call when it does.
A worked example: fetching a YouTube transcript
The simplest real tool on our harness fetches a YouTube video's transcript, and walking through it end to end shows exactly what "a two-file change" actually means in practice. The tool itself is defined in one file: a small typed input model describing what it needs, in this case a video URL, a description written less like documentation and more like an instruction telling the model exactly when this tool is useful, and a run method that shells out to fetch and parse the transcript, with an explicit timeout so a hung request can't stall the whole turn indefinitely. That's the entire capability, self-contained in one place, with nothing about it touching the reasoning loop, the approval system, or any other tool.
Making it available to a product is the second file, or more precisely, one line inside an existing configuration file: adding the tool to a Skill's tool list. That's the full wiring. No changes anywhere in the code that actually drives the conversation, streams responses, or persists history, because none of that code needs to know this tool exists in order to work correctly. It only needs to know how to call whatever's in the list it was handed for this turn.
Why the loop never imports a specific tool
This is the actual architectural discipline behind the two-file claim, and it's worth stating plainly: the core reasoning loop has no code anywhere that references a specific tool by name or by type. It only ever talks to the generic interface, asking a registry for whatever tool the model requested by name and calling it the same way regardless of what that tool actually does underneath. That indirection is what makes adding a hundredth tool exactly as contained a change as adding the second one. If the loop ever needed a special case for a particular tool, an if-statement checking whether this one needs different handling, the whole guarantee would already be broken, so keeping that from happening is an active discipline, not a one-time decision.
Safe by default, not safe by remembering to be careful
The approval property defaulting to true is what makes this fast without also making it reckless. A new tool is opt-out of human approval, not opt-in, so someone writing a new capability under time pressure for a client doesn't have to remember to think carefully about whether it's safe to run unsupervised, the safe behavior is what happens automatically unless they deliberately decide otherwise and say so explicitly. Every tool we've exempted from that default, a memory search, a curriculum lookup, a read-only database query, was exempted for a specific, stated reason, structurally incapable of changing anything, not because someone forgot to add the check. Speed and safety aren't actually in tension here, because the interface makes the safe path the path of least resistance.
How a stateless declaration gets a live database connection
One detail worth explaining, because it's what lets tools stay simple to declare: a Skill's tools are defined statically, once, with no database connection in scope at definition time. Any tool that needs to persist something beyond a single call, saving a fetched transcript, recording a quiz attempt, gets a hook that hands it a live connection at the moment a real request actually needs it, rather than requiring every Skill definition to wire that up by hand. Most tools don't need this at all and can ignore it entirely. The ones that do get it without the person defining a Skill having to think about database plumbing just to list which tools a product should have.
A realistic example: a client wants a new lookup added
Say a client running one of our AI agents wants it to also be able to check stock levels before answering a product question. Scoping that request against this interface is a short conversation: the input is a product identifier, the action is a read against their inventory system, and because it's a lookup that can't change anything, it's a reasonable candidate for skipping the approval pause, the same reasoning already applied to every other structurally read-only tool on the harness. Writing it is a self-contained file: the input shape, the description telling the model when to reach for it, and the actual lookup logic. Wiring it in is one line adding it to that product's Skill. Nothing about the reasoning loop, the streaming protocol, or any other product running on the harness needs to change, or even needs to be touched, for that capability to exist.
Compare that to bolting a feature onto a plain chatbot
It's worth contrasting this directly with the more common pattern: an AI feature built as a single script wired straight into one product, with no separation between "what this specific capability does" and "how the chat loop calls things." Adding a new capability to that kind of system usually means touching the same file that handles everything else the chatbot does, increasing the chance a change meant to add one new lookup accidentally affects how an unrelated part of the conversation behaves. That's not a hypothetical risk, it's the direct, predictable consequence of not having a boundary between the loop and the things it calls. The two-file pattern isn't a nicety, it's what makes it possible to say with confidence that adding capability number thirty didn't quietly change how capability number twelve behaves.
What this actually means for a client request
When a client asks for a new capability, the scoping conversation is narrow and concrete: what input does this need, what does it actually do when called, and does it change anything or only look something up. That's a fundamentally smaller conversation than "how do we bolt a new integration onto our AI chat feature without breaking anything else it currently does," which is the conversation a less disciplined architecture forces every single time. The difference between those two conversations is most of the difference between an agency that can turn around a new AI capability in days and one that treats every new request as its own small integration project.
Why we don't treat this as a nice-to-have
It would be easy to treat this kind of interface discipline as an engineering preference with no real business consequence, the sort of thing that matters to developers and nobody else. In practice it's the opposite: it's the single biggest lever on how quickly a new client request turns into a working feature, and how confidently we can say yes to it without secretly worrying it'll destabilize something else already running. A narrow, disciplined interface isn't a constraint on what we can build for a client, it's what makes saying yes to a new request a small decision instead of a risky one, which is exactly the difference a client actually feels when they ask for something new and get an honest, fast answer instead of a hedge.
The discipline that keeps it true
This only stays a two-file change for as long as the interface stays genuinely narrow, and it's worth admitting that's a constraint we have to actively protect rather than something guaranteed to hold forever. Every time a new tool's requirements look like they might need the core loop to behave differently, that's a moment to push back and ask whether the tool can be reshaped to fit the existing interface instead, because the moment the loop starts accumulating tool-specific logic is the moment every future addition stops being a small, contained change and starts being a negotiation with everything that came before it.