Giving an agent real power without giving it a loaded gun

There's a moment, building with autonomous agents, where the demo stops being the point. The agent can read your knowledge base, draft the email, update the CRM record, file the expense. It works. And then you have to decide whether to actually let it, unattended, on a schedule, with your real systems and your real clients on the other end.

That decision is where most of the interesting engineering lives, and almost none of the marketing does. The hard problem isn't can the agent do the thing. Modern models clear that bar. The hard problem is: what happens the first time it's confidently wrong, or quietly steered by something it read on the internet, or fired at 3 a.m. by a cron job with nobody watching? Capability and liability are the same wire. You don't get one without the other, and the work is in deciding where to put the resistor.

We build and run agents that touch the systems a service business actually runs on, knowledge bases, a CRM, client comms, spend. This is what we've learned about making that safe enough to live with. Not safe in the abstract. Safe enough that you'd let it run while you sleep.

ChatGPT Image Jun 22, 2026, 03_24_41 PM.png

The two answers that dont work

There are two tempting non-answers.

The first is "just give it access." Hand the model the API keys, write a good system prompt, trust it to behave. This works right up until it doesn't, and the failure mode is unbounded. An agent with unrestricted write access to your CRM is one bad inference away from archiving a client's pipeline. An agent that can send messages is one prompt injection away from emailing your contact list. The system prompt is not a security boundary, it's a suggestion the model usually follows. "Usually" is doing catastrophic work in that sentence.

The second non-answer is "gate everything." Make the agent ask permission before every action. This is safe and it is useless. An assistant that interrupts you to confirm reading a file is an assistant you turn off by Tuesday. Worse, constant prompting trains you, you start reflexively approving, which means the one prompt that actually mattered gets the same rubber stamp as the two hundred that didn't. Friction that isn't aimed is just noise, and noise degrades the human judgment the gate was supposed to invoke.

The real answer is neither. It's to spend friction the way you'd spend money: deliberately, where the risk is, and nowhere else.

Put judgment in the middle

The structural decision that makes everything else possible: the agent should not talk to your systems directly. Every action it takes should pass through a single chokepoint that classifies what's about to happen and decides whether it's allowed, allowed-with-confirmation, or refused.

Call it the difference between a mediator and a dumb pipe. A dumb pipe forwards whatever the model emits to whatever system it named. A mediator sits at the dispatch boundary, the one place every tool call funnels through, regardless of which model produced it or which channel the request came in on, and applies policy before the call reaches the outside world. One checkpoint, not a checkbox sprinkled hopefully through the prompt.

ChatGPT Image Jun 22, 2026, 03_26_33 PM.png

This matters because it relocates trust. You are no longer trusting the model to be well-behaved. You're trusting a small, auditable, deterministic layer that the model doesn't get to negotiate with. The model can propose anything; the mediator decides what actually happens. That's a property you can reason about. "The model will probably be sensible" is not.

A consequence worth stating plainly: the safety layer must be something the agent cannot edit. If the agent can rewrite its own rules, or even read its way to the credentials behind them, you don't have a safety layer, you have a speed bump with good PR. Certain resources sit entirely outside the agent's reach: credential stores it can't read, and the gate's own configuration it can't modify. Not because the prompt asks nicely. Because the boundary is enforced below the level the model operates at.

Classify by effect, not by intent

So the mediator has to decide. Decide on what basis?

The instinct is to classify by the name of the action, "this is a read, reads are fine." That instinct is a trap, and it's worth understanding why before you build anything.

The right axis is effect, specifically two dimensions of effect: reversibility and blast radius. Reading data is reversible and contained, it changes nothing. Adding something new, a draft, a record, an appended note, is reversible in practice; if it's wrong you delete it, and nothing was destroyed to make it. Those actions can run frictionlessly. The agent earns its keep precisely because it doesn't stop to ask before doing the safe, additive, recoverable work.

The actions that earn friction are the ones you can't cleanly undo, or that reach beyond your own walls. Destroying something, deleting, archiving, overwriting, mutating a schema, fails the reversibility test. Touching other people, sending a message, leaving a comment that notifies someone, anything that puts a side effect in another human's inbox, fails the blast-radius test, because the damage lands outside your system where you can't reach in and fix it. Those get gated. A human authorizes them, every time, even at the cost of friction, because the cost of not gating them is paid by someone who didn't agree to be part of your experiment.

ChatGPT Image Jun 22, 2026, 03_28_24 PM.png

Two principles fall out of this that are easy to get wrong:

Classify by what the action does, not by what it's called. A write that presents itself as a harmless property update can, depending on its payload, perform a destructive operation. If you classify on the verb, "it's an update, updates are additive", that destructive write sails through frictionless. You have to inspect the effect, including the payload, not just the label the caller chose. We learned this the way you learn these things; more on that below.

Fail toward friction. When the mediator encounters an action it doesn't recognize, the answer is not "allow." Unknown actions, especially anything that looks like it sends or writes or pushes to the outside world, default to gated or refused. New capabilities show up over time, a new tool, a new integration, and the window between "it exists" and "someone classified it" must fail safe, not open. The default for the unclassified is suspicion.

I'm deliberately not publishing our tier definitions or the rules that implement them. The model, reversibility and blast radius, frictionless-additive versus gated-destructive, is worth sharing because it's a way of thinking. The specific ruleset is both proprietary and, frankly, a map of our live attack surface. Abstract up a level and the idea transfers fine.

One agent, many businesses, no bleed

If the agent serves more than one domain, say it handles your own operations and, separately, work for a marketing client and a CRM you administer, isolation stops being optional. The failure you're guarding against is the confused deputy: the agent, holding legitimate access to several places, gets convinced to use the access for one in the context of another. Pull a client's confidential note into your own workspace. Apply an instruction meant for domain A to the data in domain B.

The defense is boring and total: each domain is reached through its own connector, with its own credential, through its own code path, with no shared plumbing between them. There is no single key that opens everything, and no common channel where a mistake in one can spill into another. They're separate systems that happen to share an operator.

ChatGPT Image Jun 30, 2026, 03_10_29 PM.png

Routing between them is explicit. The agent picks a domain by an unambiguous signal in the request, not by guessing from context, and when a request is ambiguous about which domain it means, the correct behavior is to ask, not to default. Defaulting is how data crosses a boundary it shouldn't. A half-second question is cheap; a client's information in the wrong workspace is not.

One nuance that took us a while to be comfortable with: the policy is a dial, not a wall. A low-stakes domain that the operator personally owns, where there are no clients to harm and the operator accepts the downside, can be deliberately set to run everything frictionlessly, including destructive actions. That's a legitimate choice when it's made consciously, by the owner, and recorded. The point of the system isn't maximum friction everywhere; it's that the friction level for each domain is an explicit, authorized decision rather than an accident. Widening the gate for a domain you own is policy. Widening it because the agent talked you into it mid-task is not.

Assume the model can be turned against you

Here is the assumption that should sit under the whole design: the agent reads untrusted text, and untrusted text contains instructions.

A web page, an email, a CRM note, an inbound chat message, any of it can carry a prompt injection: text crafted to make the model do something its operator never asked for. You cannot prompt-engineer your way out of this completely. As long as the model consumes outside content and can act, some fraction of hostile content will steer it. Treating "the model won't fall for it" as a control is wishful.

This is the real reason the gate stays on, always, with no blanket "trust me" mode in normal operation. The gate's entire value is that it does not trust the model's intent, it classifies the action's effect and applies policy regardless of why the model decided to act. A model that's been fully hijacked still can't cross a tier that requires a human. It can want to archive the client database all it likes; the mediator doesn't care what it wants. It sees a destructive, irreversible action and stops for authorization. The injection succeeds at changing the model's intent and fails at changing what the system permits. That gap is the whole game.

ChatGPT Image Jun 22, 2026, 03_30_11 PM.png

It's why a global bypass switch is a liability, not a convenience. The moment "just let it run unattended without the gate" exists as a setting, it becomes the thing an attacker, or a tired operator, reaches for. The gate that's sometimes off is a gate you can't reason about.

The unattended path is the only test that counts

Everything above is theory until it runs on a schedule with nobody watching. Manual runs have a human in the loop by definition, you're right there, you'll notice. The cron job at 3 a.m. is the real deployment, and it has different failure modes. Two we've hit, abstracted:

ChatGPT Image Jun 22, 2026, 03_32_05 PM.png

The silent gate-off. In an early version, the safety layer installed itself during startup, and under certain timing, when the system came up idle or under load, the first action could run before the gate finished attaching. A narrow window where the agent was live and ungated, and nothing announced it. Nothing crashed. It just wasn't safe for a moment, silently. The fix was less about the specific race than about the principle it taught: a safety layer that can be quietly absent is worse than one that's loudly broken, because you'll trust it either way. We made its installation announce itself, retry until it succeeds rather than give up, and emit a line you can actually check. The lesson generalizes: a control you can't observe is a control you can't trust. Make the gate's presence verifiable, and verify it on the path that actually runs unattended, not just the one you watch by hand.

The additive write that wasn't. The classify-by-effect story from earlier, in the flesh. A write that looked additive carried a payload that would have performed a destructive operation, and because it was labeled benign, an effect-blind classifier would have waved it through frictionlessly. The repair was two-layered: the benign action now refuses to carry a destructive payload at all, and the classifier inspects the payload rather than trusting the action's name. Defense in depth, because either layer alone is one bug away from failing open. The lesson: the verb is not the effect. Don't let an action's friendly name vouch for what it actually does.

Both stories share a moral that's worth more than either incident: fail loud, never stale. An unattended job that errors should make noise, not silently write last week's answer and call it fresh. Silent staleness is the most dangerous failure an automated system has, because it looks exactly like success. If something breaks on the 3 a.m. run, you want to find out at 3:01, not discover three weeks later that the agent has been confidently doing nothing.

Humans authorize the irreversible

Pulling it together, the rule we actually operate by is small:

The agent moves freely through everything reversible and contained, reading, drafting, the additive and recoverable work that's most of the job. It stops for a human on everything it can't take back or that reaches another person. When it isn't sure, it asks. When something must never be touched, the boundary is enforced beneath the agent, not requested in its prompt. And every decision, allowed, confirmed, refused, is written down, so the question "what did it do, and why was that allowed" always has an answer.

ChatGPT Image Jun 22, 2026, 03_35_14 PM.png

None of this makes the agent less capable. That's the part that surprised us. Aiming the friction increases how much real power you're willing to delegate, because you're no longer betting the business on the model being sensible. You've made being wrong survivable. An agent that can't cause an unrecoverable disaster is an agent you can actually let off the leash for everything else, which is most of what you wanted it to do anyway.

The capability was never the hard part. Making the capability safe to grant is the work, and it's most of the reason our agent gets to touch anything that matters.


A note on what this is built on, and what's ours

The agent runtime underneath this, the multi-platform gateway, tool dispatch, skills, scheduling, memory, is Hermes Agent by Nous Research, which is open source under the MIT license. Credit where it's due: it's a genuinely good foundation, and it ships sensible baseline hygiene, including content-level warnings on risky code writes and a clear recommendation to sandbox the runtime to limit blast radius from prompt injection.

What we've described here is the layer we built on top of it: the capability classification, the fail-closed authorization gate, the per-domain isolation, and the operational discipline around running it unattended. That layer is our original contribution and our moat, which is why this piece shares the principles and withholds the implementation. The thinking transfers. The ruleset stays home.