An autonomous AI agent that stops at your budget

The demo always works. You hand an autonomous agent a goal, it prints a plan, ticks through it, and stops somewhere near the end looking pleased with itself. The second run is the one that decides whether you use the thing, and it usually goes differently: forty minutes spent re-searching the same question, an unknown amount of money gone, a browser tab closed by accident that took the whole run with it, and a result you have to extract from a wall of Markdown with a mouse.
Operator is built for the second run. One goal goes in. What comes out is a task queue that is planned, executed one task at a time, reviewed after every result and re-planned from there — inside a credit ceiling checked before each model call rather than reported after it.
One goal in, a task queue out
A run starts with one instruction of up to 4,000 characters, an optional title, and up to five files read into its context at creation. The planner turns that into an ordered queue where every row carries the task itself (500 characters), the tool it should use, and a one-line why (200 characters). The queue is capped at twelve open tasks on purpose: past roughly a dozen, a run is meandering rather than working, and the review pass exists to keep it short.
Two more numbers bound the run before it starts. max_steps is how many tasks may actually execute — ten by default, thirty as a hard ceiling whatever is asked for. The ceiling is what it may spend: 25,000 credits by default, which is about five dollars of generation value, with 1,000 as the floor and 500,000 as the most any single run may be given.
K models draft the plan, and one model fuses it
The planning phase is not one model talking to itself. Up to four proposer models each draft a task list, and a separate fuser reads all of them and emits one ordered queue. After every executed task the same shape repeats: the reviewers judge progress, the fuser rewrites the remaining queue and says continue or done.
Set the fan-out to 1 and you have the classic single-model loop, which is cheaper and often enough. Raise it and you are buying the thing a single model is worst at — noticing that its own plan skipped a step. It is the same fusion the council of models uses on a single answer, applied to a plan instead.
Model output is data, never action
Every task list a model returns is parsed and clamped before anything runs: unknown or disabled tools fall back to reason, text is cut to its limit, and the queue is capped. Every review is parsed the same way. Only validated rows reach the executor that maps them onto real calls.
The prompt does say “do not invent tools”. The guard is not the prompt. A model that invents deploy_to_production produces a row that becomes a reasoning step, because the vocabulary a run may act on is a fixed table on the server, not a suggestion in a system message.
The ceiling is checked before the call, not after
This is the part that separates a run you can leave alone from a demo. An autonomous loop chooses its own step count, and a fused phase multiplies each reasoning step by the number of proposers plus the fuser. Reporting spend afterwards, the way most agent frameworks do, means the number you read is the number you have already paid.
So every reservation is checked against what the run has left. If the next call cannot fit under the ceiling, the run ends with the status budget instead of overdrawing. Where the model stream accepts a cap, it is given everything the ceiling has left rather than the cost of one call — so the ceiling bounds the tokens too, not just the arithmetic afterwards.
Picking the ceiling back up is a separate, deliberate gesture: Continue asks for new headroom rather than assuming it, because raising it is you spending more of your own money and nothing should decide that on your behalf.
Six tools, and a run only gets the ones you tick
- Reason — think and write. Always available; it is the fallback for everything else.
- Web search — search the live web and ground the answer on the results, with sources.
- Generate image — a picture from a description the task writes.
- Write code — code delivered as text, and where the deployment allows it, Python is also run in a sandbox with the printed output returned alongside.
- Data analysis — parse what the goal or an earlier result provided, compute the statistics, surface the outliers.
- Save to graph — record a finding as a note in your Grafy graph so later work can build on it.
A run created through the API with no tool list gets web search, because a run that can only think is rarely worth starting. A tool you enabled that this deployment cannot serve is dropped at creation rather than failing halfway through a queue.
The loop runs on the server; your tab is only a window
A run is an asyncio task on the server. It appends coarse events to the store and fine-grained streaming deltas to an in-memory job, and the live endpoint merely watches. Closing the tab closes a reader, nothing else. A browser coming back replays the stored transcript from the last event it saw and re-attaches to the live stream.
That inversion is deliberate. Browser-side agent loops — the shape most open-source autonomous agents ship — lose a twenty-step run to a closed laptop. Here the transcript is the record, and the window onto it is disposable.
The deployment is not one process either. Two replicas run, scaling to six, so a run carries its own liveness on its row: which process owns it, a heartbeat refreshed every twenty seconds, and a control flag any replica can write. A pod that is not driving a run answers questions about it from those fields rather than from its own silence — before that, a request landing on the sibling pod would declare a perfectly healthy run interrupted.
A worked example: the Todo CLI that could not complete a todo
The first real run to reach the deliverable stage was asked for a Python todo CLI. It produced a working todo.py inside a fenced code block — and a bug that one execution would have caught: the listing printed todos by ordinal (1., 2.) while done and remove matched on a UUID the listing never showed. The program as delivered could not complete a todo.
Two things came out of that. A deliverable you cannot save is not delivered, and a deliverable nobody ever ran is not finished.
The deliverable is read back into files
Operator still finishes by writing one Markdown document — that is the right shape for a report and the wrong shape for software — and the document is then read back into named files. Nothing about the run changed to make that work, which is the point: a run from last month parses exactly like one from today.
Naming is a best effort with an explicit fallback rather than a promise. An unlabelled code fence becomes snippet-1.py, not a guess dressed up as a filename, and the parser reports what it did so the interface can say so. A package holds up to 40 files, 200 KB each, about a megabyte in total — bounds for a pathological deliverable, not a normal one. package.zip is built from the standard library, and Run the demo assembles a single script that recreates the package in the sandbox and runs its entry point, because the code sandbox takes one file. A language the sandbox cannot execute still belongs in the package; it just cannot be the entry point.
Nine states, and six of them are endings
A run is draft, running or paused, and then it is finished: complete, stopped, budget, limit, stalled or error. Naming six endings rather than one is what makes the transcript readable a week later — budget and limit are caps you can raise, stopped is you, error is us.
stalled is the honest one. It is the state where the review said continue — the goal is not met — and then named no work to do it with, on a queue the last task had already emptied. Nothing is left to run and nothing was achieved, so it is neither limit (steps remained) nor complete. Most systems would call that success.
You can pause and resume a live run, stop it outright, re-run a single step with a different task, tool or model, or retry the whole thing — and a retry inherits the original configuration, with one exception. Premium-model consent is never inherited, because lowest-cost-only mode may have been switched on since, and a retry must not become a way to spend around a preference you set afterwards. Superseded attempts stay listed, so what a re-run replaced is still readable.
Starting a run from CI or from cron
An API key scoped operator:write can start runs and collect their deliverables without a browser anywhere in the loop, and the key page says what that means in consequences rather than in scope names: it keeps spending credits up to each run's ceiling. The same key cannot mint another key, cannot reach billing, account or admin endpoints, and cannot touch any other app's API — key reach is an allow-list per app, so a credential built for a nightly research run is exactly that. Pair it with lowest-cost routing and an overnight run has two independent bounds on what it can spend.
Compared with an agent that runs in your browser
The browser-side agent is genuinely simpler: no server state, no replicas, no heartbeat. What it costs you is everything that makes a run survivable. The tab is the process, so closing it ends the work. The spend is whatever the loop happened to do, visible only afterwards. The plan is one model's first idea, with no second opinion. And the output is the transcript, which is why so many of those runs end with somebody copying code out of a chat log.
Grafy's own graph auto-pilot is the lighter tool in the same family — it works a canvas you are watching, node by node. Operator is for the goal you want to hand over and come back to, and it is built to be judged on what it hands back rather than on how convincing the plan looked.
Frequently asked questions
What happens when a run hits its credit ceiling?
It ends with the status budget before making the call it could not afford, not after. The work already done, the transcript and any partial deliverable are all kept. Picking it back up is a Continue, which asks you for a new ceiling — raising it is you choosing to spend more, so it is never assumed.
Does closing the tab kill the run?
No. The loop is an asyncio task on the server and your browser is only watching a stream. Come back later and the transcript replays from the last event you saw, then re-attaches live if the run is still going. This is the single biggest difference from agent loops that run in the page.
How many steps will an autonomous run take?
As many as it needs up to max_steps, which defaults to ten and cannot exceed thirty. The queue itself is capped at twelve open tasks, and the review pass after each result prunes and re-orders what remains, so the number of steps is bounded by policy rather than by the model's enthusiasm.
Can it produce software I can actually run?
Yes. The Markdown deliverable is parsed back into named files — up to 40 of them — downloadable as a zip, and the demo panel executes the package's entry point in a sandbox with whatever argv a CLI needs. That path exists because the first real deliverable was a program with a bug that one execution would have caught.
Can another system start a run for me?
Yes, with an API key scoped to Operator. It can create runs, read their transcripts and fetch deliverables, and it can do nothing else: no billing, no account changes, no minting more keys, no other app's API. That makes it the credential to give CI or a cron job.
How is this different from asking a chat model to do the same thing?
A chat turn has no queue, no review pass, no ceiling and no state that survives the tab. Everything here is the difference between one long answer and a run you can leave, audit, pause, cost and repeat — which is also why the provenance rules that apply to a graph apply to a run.
Open Operator and give it a goal. Set the ceiling first: it is the one number that decides how a run ends when things go wrong.


