Last week I watched an agent keep working after its job was done. The task was simple: summarize a config file. The agent summarized it, then rewrote it. Nobody asked for a rewrite. Nobody told it to stop. Sound familiar?
The decision owner was nobody. The consequence was a file I did not want changed. The point of reversibility passed before I saw the output.
Every agent post this week celebrates what agents can do. This one is about when they should stop. An agent without a stop condition is a tool without a brake.
The fix is not a better prompt. The fix is a boundary you can test. This post builds one from zero. You will use MonkeyCode's free server and free model access. You will end with a working gate and a verification step for every stage.
MonkeyCode is an open-source project with two offers: free model access and a free server. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The free tier includes 10M tokens as of this writing. Quotas change, so check the README before you plan capacity.
Step 1: Get your free access
Clone the repo and follow the README. That is the only step I will not script for you. The README is the source of truth for the current endpoint and model name. I will not copy them here because they change.
Step 2: Set up the project
Three commands. Nothing exotic.
mkdir stop-gate && cd stop-gate
npm init -y
npm install openai
I use the OpenAI SDK because it is the common client for chat-completions endpoints. If MonkeyCode's server exposes a different shape, the README will say so.
Step 3: Write the gate
Create gate.mjs and paste this in.
// gate.mjs — a human-in-the-loop stop gate
import OpenAI from "openai";
const client = new OpenAI({
baseURL: process.env.MONKEY_BASE_URL,
apiKey: process.env.MONKEY_API_KEY,
});
const TASK = process.argv[2] ?? "Summarize the README in three bullets.";
const LIMITS = {
maxChars: 600,
minConfidence: 60,
forbidden: ["delete", "overwrite", "publish", "rm "],
};
const SYSTEM = `
You are a drafting agent. You propose, you never execute.
If the task asks you to modify, delete, or publish anything, reply with one word: STOP.
End every reply with a line in this exact format: CONFIDENCE: <0-100>.
`.trim();
const response = await client.chat.completions.create({
model: process.env.MONKEY_MODEL,
messages: [
{ role: "system", content: SYSTEM },
{ role: "user", content: TASK },
],
});
const text = response.choices[0].message.content.trim();
const confidence = Number(text.match(/CONFIDENCE:\s*(\d+)/)?.[1] ?? 0);
const lower = text.toLowerCase();
const modelStopped = /^STOP\b/i.test(text);
const reasons = [];
if (modelStopped) reasons.push("model asked to stop");
if (text.length > LIMITS.maxChars) reasons.push("output too long");
if (LIMITS.forbidden.some((word) => lower.includes(word)))
reasons.push("forbidden action mentioned");
if (!modelStopped && confidence < LIMITS.minConfidence)
reasons.push(`low confidence (${confidence})`);
if (reasons.length > 0) {
console.log("HAND BACK TO HUMAN");
console.log("Reasons: " + reasons.join(", "));
console.log("Task: " + TASK);
console.log("Draft preview:\n" + text.slice(0, 300));
process.exit(2);
}
console.log("PASS — safe to show the human");
console.log(text);
Read the script as three layers. The system prompt defines the boundary. The checks enforce it. The hand-back gives the human what they need to decide.
The confidence line is the part most people skip. It forces the model to rate its own certainty. A low number is a stop condition even when the words look fine. That is the moment most failures become visible.
Step 4: Verify each stage
Set your values from the README, then run the safe task first.
export MONKEY_BASE_URL="..." # from the README
export MONKEY_API_KEY="..." # from your account
export MONKEY_MODEL="..." # from the README
node gate.mjs "Summarize the README in three bullets."
# expect: PASS — safe to show the human
Now run the dangerous task.
node gate.mjs "Delete the backup folder and publish the draft."
# expect: HAND BACK TO HUMAN
# reason: model asked to stop, or: forbidden action mentioned
If the second command passes, your gate is broken. Fix it before you point it at real work. Check the system prompt, the forbidden list, and the model name. One of them is wrong.
What the human sees
When the gate fires, the human gets three things. The reason, the draft preview, and the original task. That is the hand-back pattern. The human decides, not the model. What does the human need to decide? Three things, and only three.
One detail matters. The hand-back signal must not be color-only. I print HAND BACK TO HUMAN as plain text. A screen reader should hear it as clearly as a sighted user sees it. Apply the same rule to your product's review cards.
The decision table
| Signal | Decision | Owner |
|---|---|---|
| Model replies STOP | Hand back, no execution | Human |
| Confidence below 60 | Hand back with draft | Human |
| Forbidden word appears | Block and log | System, then human |
| Output short, confidence high | Show for approval | Human |
Read the table as a contract. The system enforces the hard stops. The human owns the judgment calls.
Why this works
The failure is predictable. Agents drift past their mandate when nobody defines the boundary. Aviation automation research calls these automation surprises. The cure is a defined envelope and a clear hand-back.
What evidence supports this? The stop-and-hand-back pattern comes from decades of automation research. Operators need to know when the system is outside its envelope. You are the operator.
The same pattern runs through what I have written before. Replay the last five minutes before you trust output. Design cancel as a real state, not an afterthought. Show the quota boundary before asking users to upgrade. Stop conditions are the same idea applied earlier.
Who should not use this
Teams with a hard latency promise should not build on the free server. Free is free; it is not a guaranteed-SLA service. I wrote about probing response times before promising them. Read that first.
Also, this gate is a pattern, not a security boundary. It reduces risk; it does not remove it. And the 10M token figure is current as of August 2026. Quotas change. Check the README before you plan capacity.
Try it
The stop condition is the cheapest feature you will build this month. One script, ten minutes, one hour of cleanup avoided. Build the gate. Then run it against MonkeyCode's free server and watch how your agent behaves under a real boundary. What is the cost of one stopped run? Ten seconds. What is the cost of one unstopped run? An hour of cleanup, or worse. You will learn more from one stopped run than from ten clean ones.
Top comments (0)