I build agents that take real actions: resolving support tickets, issuing refunds, replying to customers. The moment those agents stop being demos and start touching money or reputation, the same question shows up every time: what happens on the action that is too risky to automate?
You have two bad options. Either you let the agent act on everything and hope its judgment holds, or you make it stop and wait for a human. The second is correct, but it usually means a human parked at a dashboard, refreshing a queue. That does not scale, and in practice it means the "human in the loop" is asleep, in a meeting, or nowhere near a laptop when the agent actually needs a decision.
Greenlite is my answer to that: the human-in-the-loop layer that lives in your pocket. An agent wants to act, your phone buzzes, you see the full context and the proposed action, and you approve or deny in one tap.
The core idea
The insight is that autonomy is only safe to turn on if the escape hatch is fast. If asking for human approval is expensive, you will avoid asking, and you will end up over-automating. So the whole design goal was to make the approval itself trivial: notification to decision in a couple of taps, wherever you happen to be standing.
That reframes the agent's job. The agent handles the safe cases automatically and pushes only the rest to a human. A refund under the auto-limit just goes through. A $900 refund, or a message dripping with high-urgency negative sentiment, gets escalated. You stay in control of exactly the decisions that need a person, and nothing else.
The other design choice was to keep the approval contract generic. An escalation is just an item with a proposedAction, a reason it escalated, and the underlying detail. Any agent that can produce that shape and expose an approve endpoint plugs straight into the same feed. Greenlite is not tied to one agent, it is a control surface for a whole suite.
How it works
Greenlite is one Expo (SDK 51) plus expo-router codebase in TypeScript that builds to native Android and iOS. There are three moving parts: the feed, the decision, and the buzz.
The feed. Pending approvals are escalations pulled from each agent's own server-side API. Today that source is my support agent's /api/approvals endpoint. One thing I want to call out, because it is a deliberate security decision: Greenlite reads from the agent's HTTP API, never from a database directly. The app carries no database credentials. It sends a token in an x-resolvd-token header and gets back a list of approval items. Each item carries an id, the source agent, a title, the full detail, the proposed action, and the reason it escalated. If the fetch fails, the feed simply comes back empty rather than crashing or guessing.
The decision. Tap any item and you see the full message plus the proposed action. Approve or deny is one tap. That decision routes back to the originating agent's approve endpoint, a POST to /api/approve carrying the item id and a boolean. The agent owns what "approved" actually means; Greenlite just relays the human's verdict back to the system that raised the question. Clean separation: the phone is the judgment, the agent is the executor.
The buzz. Registration uses expo-notifications. On first run the app requests notification permission, sets up a high-importance "approvals" channel on Android, and fetches an Expo push token. You store that token server-side to target the device, so an agent can buzz the phone the exact moment it needs a decision instead of waiting for you to open the app and check. That is what turns this from a queue you have to remember to check into something that reaches out to you.
There is also a demo mode. When no backend credentials are configured, the app runs against sample approvals, the $900 refund and the angry customer, so you can explore the whole flow immediately without wiring up an agent first. It made the thing shippable and explorable on day one, which matters for something you want people to actually try.
One honest limitation
The approval source is not yet plural in practice. The contract is generic and designed for many agents to feed one queue, but right now the wired-up source is a single agent's endpoint, and the token model reflects that: it is one shared token in the request header, not per-agent auth or per-user identity. For a real multi-agent, multi-approver deployment you would want scoped credentials, an audit trail of who decided what, and probably signed requests so a leaked token is not a master key to approve anything. The mechanism is right; the trust model around it is still early. I would not put this in front of a production money-moving flow without hardening that layer first.
That trade-off was intentional for where the project is: prove the interaction, prove that notification-to-one-tap-decision feels good and safe, then invest in the auth story once the shape is validated. It is a lot easier to add per-agent tokens to a working approval loop than to design the whole thing around auth you have not tested.
Closing
The thing I keep coming back to is that agent safety is often framed as a model problem, better guardrails, better judgment inside the agent. A lot of it is actually an interface problem. If the human gate is slow, people route around it. If it is one tap on a device you already have on you, keeping a human in the loop stops being a tax and starts being the default. Greenlite is my attempt to make the safe choice the easy one.
Code and setup are here: https://github.com/AgentPostmortem/Greenlite
Top comments (0)