Autonomous AI Agents: From Assistants to Automation
What changes when an AI agent runs unattended on a schedule — and the guardrails that make it safe

What changes when an AI agent runs unattended on a schedule — and the guardrails that make it safe

"From assistant to agent: a loop that runs whether or not you are watching"
Illustration by Nano Banana 2 (fal.ai)
2 posts in this series
Feel free to send us a message with your thoughts, or learn more about us!
Exploring the Model Context Protocol (MCP) and how it's enabling new patterns for AI-assisted development with practical examples from real projects.
A short, honest changelog: what shipped in the @dcyfr/ai v3.x line, what changed for people building on it, and what comes next — catching the blog up with work the package already produced.
After 6 months and 12,000+ AI-assisted code changes, discover the 5 daily workflows that deliver 60% time savings, 75% more features shipped, and zero quality regression. Real patterns, measurable results, and pitfalls to avoid.
Part 2 of 2
Series Background: This is Part 2 of the AI Workflows series. . Part 1 covered the daily workflows where you prompt an AI assistant and review its output. The November 2025 post set up the MCP infrastructure that gives an agent access to project state. This post is the next step: removing yourself from the loop on the work that doesn't need you.
In Part 1 I argued that AI assistants are Junior+ teammates — great at boilerplate, sharp at patterns, and dependent on you for context and review. Every workflow in that post had a human in the inner loop: you prompt, it generates, you review, you apply.
This post is about the work where that loop is the bottleneck. Not the feature you’re designing — the maintenance that piles up around it. Dependency bumps. A flaky service that needs a restart. A backlog that drifts out of priority order. A production error that’s been firing for six hours while you sleep. None of that needs your judgment. It needs attention, on a schedule, forever.
So I gave that attention to an agent. It runs every 15 minutes, unattended, on my Mac. It reads the state of my projects, decides what’s worth doing, does a bounded slice of it, and writes down what happened. I review its work the way I’d review a junior’s — in batches, after the fact, not in real time.
This is the part everyone gets wrong: the hard problem isn’t making an agent capable. The models are already capable. The hard problem is making an unattended agent safe and cheap enough that you’ll actually leave it running. That’s where most of this post lives.
The difference between an assistant and an agent is who closes the loop.
An assistant waits for you. It has no clock and no initiative. You bring it a task, it produces output, and the interaction ends. Value is gated entirely on your attention — if you don’t prompt, nothing happens.
An agent has a clock and a goal. It wakes up on its own, looks at the world, and decides whether there’s anything worth doing. You’re still in control, but you’ve moved from the inner loop (approve each step) to the outer loop (set the goals, set the guardrails, review the results).
That reframing matters because it tells you where to spend your effort. With an assistant, you invest in prompting — better context, clearer requirements. With an agent, you invest in bounding — what can it touch, what can it spend, and how do you stop it. The model is the easy part.
I’ll make this concrete with the agent I actually run. I’ll call it the daemon, because that’s what it is: a background process that never logs off.
Every useful autonomous agent I’ve seen converges on the same five-stage loop, whether or not its authors named it that way. Mine runs the cycle every 15 minutes:
Observe → Triage → Think → Act → Record
↑ │
└──────────────────────────────────┘Each stage exists to answer one question, and — this is the important part — each stage runs on the cheapest tool that can answer it.
The cycle starts by reading the world: open work items, recent git activity, service health, production errors, anything queued since the last cycle. No model is involved yet; this is plumbing. The output is a structured snapshot of “what’s true right now.”
The trap here is doing too much work on the hot path. My first version regenerated a pile of subsystem summaries inline every cycle — it was 99% of the observe time and dominated the whole loop. The fix was to move that fan-out to a separate scheduled job that writes snapshots to disk, and have the cycle read the freshest snapshot under a staleness gate. An autonomous loop should read cached state, not compute it.
Most cycles, the answer is “nothing worth doing.” You do not want to pay a frontier model to reach that conclusion 96 times a day. So a small local model does the first pass: given the snapshot, is there anything here that deserves real reasoning? It’s a classifier, not a planner. Cheap, fast, private, and good enough to throw away 90% of cycles before they cost anything.
When triage says “yes, look at this,” a capable model gets the snapshot and produces a plan for one bounded action — not a sprint, not a refactor, one slice. Bounding the output is deliberate: a small action is easy to review, easy to roll back, and hard to turn into a disaster.
The Think stage doesn’t apply changes. It emits a proposal: here’s what I observed, here’s the one thing I’d do, here’s why, here’s how to undo it. That proposal is what the Act stage executes against — and what gets recorded for review.
Separating “decide” from “do” buys two things:
This is the same “humans own the what and why, AI owns the how” split from Part 1 — just enforced structurally instead of by you sitting there.
The action runs inside a sandbox with hard limits (much more on this below). Shell commands come from an allowlist. File writes are restricted to a couple of directories. Network egress is filtered. If the action is outbound-facing — sending a message, opening a PR — it passes through a kill switch first.
Finally, the cycle writes an auditable trail: what it observed, what it decided, what it did, what changed. This is the outer loop’s input. I don’t watch the agent work; I read what it recorded, in batches, and that’s where I catch drift, correct bad patterns, and decide what to let it do next.
Here’s the constraint that shapes everything: an unattended agent runs whether or not it’s producing value. An assistant only costs money when you choose to use it. An agent on a 15-minute clock fires ~96 times a day, ~2,900 times a month, forever. If each cycle calls a frontier model, you’ve built a very expensive way to mostly discover there’s nothing to do.
My self-evolution budget for this is **20/month, you cannot call a frontier model every cycle. You have to route.
I run a tier ladder, cheapest first, and most cycles never leave the bottom rung:
| Tier | Where | Used for |
|---|---|---|
| 0 | Local (on-device) | Triage, classification, the 90% of cycles that get filtered out |
| 1 | A private GPU box on my network | Heavier reasoning and code tasks that want a real model but not a frontier one |
| 2 | Included/free remote models | Overflow and specific tool-calling |
| 3 | Frontier API | Only the cycles that genuinely earn it |
The triage stage lives entirely on Tier 0. It’s private (project state never leaves the machine), fast (no network round trip), and free. By the time anything reaches the paid frontier tier, a cheap model has already decided it’s worth the money.
The other half of the economics is the prompt cache. Every compatible provider keys its cache on the prefix of the request — the system prompt, the tool schema, and the earlier messages. Hit the cache and you pay ~10% of the cost at ~3x the speed. Mutate any part of that prefix mid-conversation and you invalidate the cache for the rest of the session.
The expensive mistakes are subtle: injecting a timestamp into the system prompt, reformatting it “harmlessly,” adding a tool mid-conversation, rewriting earlier messages to compress them. Each one looks innocent and each one quietly torches your cache-hit rate. In a 30-turn conversation, one cache-busting mutation can 10x the cost.
This is the section I’d have wanted before I built mine. An autonomous agent is, bluntly, a process holding an API key, a shell, and the ability to send messages as me — running while I’m not looking. The security question isn’t “can it be jailbroken.” It’s “what is the blast radius when it does something wrong,” because eventually it will. You design for the misbehaving cycle, not the well-behaved one.
Four controls do most of the work.
Every outbound path — every send, every external action — gates on a kill switch before it fires. Flipping that switch is a hard “stop” that takes effect within about a second, across every process, no deploy required. There’s a one-command pause and a one-command resume. The first thing I build for any new outbound capability is its off switch; the capability comes second.
Spend is governed by a budget kill switch that trips automatically when a ceiling is breached, and — critically — it fails closed. If the system can’t confirm you’re under budget, it stops spending rather than assuming the best. A subtlety I learned the hard way: any calibration or estimate feeding the budget check has to be freshness-gated. Stale “we’re fine” data is worse than no data, because it green-lights spending on bad information. When the inputs are stale, the safe default is to fall back to raw numbers and let the kill switch act alone.
The Act stage runs under concrete limits:
rm/sudo/eval.New automations don’t get to act for real on day one. They run in shadow first — they propose what they would do and log it, but don’t execute. Only after an automation has demonstrated a track record of correct proposals does it graduate to running automatically. Promotion is earned with evidence (a threshold of real successes), not granted because the code looks done. It’s the same instinct as Part 1’s “review AI code like a junior’s” — just formalized into a graduation gate.
Concrete is better than abstract, so here’s the real work the daemon handles without me:
None of these are glamorous. That’s the point. This is exactly the maintenance toil that used to fragment my attention all day, and it’s precisely the work that doesn’t need my judgment — only consistent, scheduled follow-through. Offloading it is what frees the attention for the work in Part 1: architecture, design, the decisions that are actually mine to make.
Same honesty as Part 1’s failure modes — these have all bitten me, and guarding against them is the difference between “useful daemon” and “automated foot-gun.”
An assistant’s mistake is one bad suggestion you reject. An agent’s mistake repeats every cycle until you notice. The mitigation isn’t smarter prompts — it’s bounded actions plus a fast kill switch. Keep each action small enough to undo, and keep the stop button one command away.
A subtle prompt change tanks your cache-hit rate and the bill quietly climbs. Or a retry loop hammers a paid API. You won’t feel it in the moment — autonomy means no one’s watching the meter. Hard caps that fail closed are the only reliable defense; alerting alone is too slow when the agent runs unattended.
The instinct to compute fresh state every cycle will wreck your loop latency, as it did mine. Read cached state; compute it on a separate schedule. The hot path should be cheap enough to run constantly.
The recursive-echo loop above. When multiple agents or sessions share infrastructure — a queue, a channel, a key — per-process safety is insufficient. Coordinate through shared, deduplicated state, and give every contended resource exactly one owner.
Give an agent a goal and enough cycles and it’ll find creative interpretations of “helpful.” The trail it records is your early-warning system — but only if you actually read it. Schedule the review. An outer loop you never close is just an inner loop you stopped supervising.
You don’t need a 15-minute daemon with four model tiers to get value from this. Start with the smallest version of the loop and grow it as you earn trust in it.
Pick one scheduled toil task. Write the smallest observe→ask→record loop that handles it. Build the kill switch first, run it in shadow for a week, then let it act. Read what it did. That's an autonomous agent.
Autonomous agents are transformative on the right work — and dangerous on the wrong work, run the wrong way. The model was never the hard part. Bounding it was.
Observe → Triage → Think → Act → Record — and run each stage on the cheapest tool that can answer its question.
Confidently-wrong-at-scale, cost creep, hot-path bloat, shared-substrate collisions, and silent scope drift. Every one is a missing boundary. Draw the boundaries first, then turn it on.
Next in this series: “MCP in Production: Wiring Tools Into Agents Without Busting the Cache” (Part 3, November) — how an agent actually reaches its tools, why your tool schema is part of the cache prefix, and how to add capabilities without quietly wrecking cost.
Running an autonomous agent of your own — or thinking about it? Tell me what you pointed it at and where it bit you. The failure modes are where the real learning is.