Stop Assuming Your AI Agent Is Done: Lessons From Building Tools That Actually Finish Tasks
Admin User
Author
I was debugging a Claude agent integration at 2 AM last month when I realized something deeply embarrassing: I had no idea if the thing was actually finished working or just... silent. The agent's last output was innocuous—a file saved, a command run—but was it complete? Was it waiting for me? Did something fail three layers deep? I was checking process status like an amateur, comparing timestamps like a caveman, and getting nowhere.
That's when I found myself down a rabbit hole of how to actually determine if an AI agent has truly finished its work. Not whether it's running, not whether it wrote something to stdout, but whether the meaningful work—the task I asked it to do—is actually complete. Turns out, this is harder than it sounds, and there's a much better way to think about it than what I was doing.
The Ambiguity Problem: Why Silence Means Nothing
Here's the brutal truth: a quiet agent tells you almost nothing. It could be finished. It could be hanging. It could be waiting for input. It could be stuck in a nested subprocess that's actually still churning. The last thing you see in a transcript might be a tool result, a completion marker, or just... nothing.
The original problem I was facing was that I was relying on weak signals. File modification times? Worthless—the filesystem writes metadata long after the actual conversation state changed. Process state? Misleading—a running process might be waiting for the user while a "finished" process might have spawned children still doing work.
The key insight here is that you need to look at the conversation events themselves, not the plumbing around them. When Claude Code or similar agents actually transition states, they emit specific events—completion markers, stop sequences, that kind of thing. That's your ground truth, not the filesystem timestamp or whether the CPU is spinning.
Finding the Signal in the Noise
The approach that actually works is walking backward through your transcript until you find a real state-change event. For Claude implementations, that means looking for explicit markers like end_turn, stop_sequence, or stop. These are semantic—they represent actual decisions, not environmental noise.
But here's where it gets tricky: later events can invalidate earlier completions. If you see a completion marker followed by a new user event, that completion is dead. The session is active again. You have to keep scanning forward after you find your marker to make sure nothing newer invalidates it.
I've also learned that some records look like completions but aren't. Child agent traffic in sidechains can finish while the main agent keeps working. API errors can arrive dressed up as normal stop markers. If you're building this yourself, you need actual test fixtures covering these edge cases, not intuition.
When and Why States Expire
This part genuinely changed how I think about agent monitoring. A completion can't stay "actionable" forever. A stalled alert can't persist indefinitely. You need bounded windows—thresholds that acknowledge you're making educated guesses, not reading minds.
My approach now is: recent activity is clearly active, a session without completions is working through an initial period, a detected stall only stays stalled for a limited window, and a completed turn expires after enough time for the user to reasonably act on it. These thresholds aren't claiming to understand the agent's internals—they're just sensible decay functions for local evidence.
The other part I've adopted is retracting stale alerts. If I detect a completion and alert the user, but then new activity arrives before they act, I cancel the alert. If the user replies after the notification, the next scan removes it from the active set. This prevents the UX disaster of spamming "your turn!" notifications repeatedly.
My Take: This Is About Building Trust in Automation
The core issue here isn't technical—it's about reliability. When you're building tools that use agents, you're asking users to trust that the system will tell them when it needs them. Get that wrong and your tool becomes noise. Get it right and it's genuinely useful.
I've realized I was over-engineering my state detection because I was treating silence as data. It's not. Silence is ambiguity. Real data is the event log, and you have to interpret it carefully—checking for semantic state changes, invalidating old conclusions with newer evidence, and applying reasonable expiry windows.
The practical rule I now follow: find the latest meaningful turn event, compare it with newer activity, apply bounded freshness windows, and update your conclusion when evidence changes. It's simple but required me to stop thinking like a systems monitor and start thinking like someone reading a conversation.
What Are You Doing to Detect Agent State?
I'm curious how other developers are handling this. Are you relying on timestamps? Process state? Or have you found a cleaner signal? Drop your approach in the comments—I'm still learning the best practices here.
Source: This post was inspired by "A Quiet Coding Agent Is Not Necessarily Finished" by Dev.to. Read the original article