The most annoying bug in my local AI assistant was not that it refused to ask for permission.
It was that it asked too often.
I would give it a normal task like "read this PDF and tell me what is inside." The assistant would make a reasonable first move, ask for approval, run the command, and then immediately ask again for the next tiny probe.
One task turned into a permission treadmill.
The approval loop felt safe but unusable
Approval prompts are good. I want a local assistant to stop before it runs commands, writes files, opens apps, or touches the desktop.
The problem is that real work is rarely one tool call.
Reading a PDF might require checking whether Python exists, finding a converter, running the converter, reading the text output, and then summarizing it. A desktop task might require focusing a window, entering text, pressing a button, and verifying the result.
If every step asks the same kind of question, the user stops evaluating risk and starts clicking through interruptions.
That is worse than unsafe. It trains the user to ignore the safety system.
The bug was treating approval as a single event
The old flow in CliGate looked roughly like this:
assistant chooses tool
-> policy says confirmation required
-> user approves
-> tool runs
-> assistant continues
-> next tool asks again
That was technically correct. Each tool call had its own confirmation.
But it missed the shape of the user's intent. The user was not approving "run one tiny probe." They were approving the assistant to continue one bounded task.
So I changed the behavior after the first approved action in a chat conversation.
Once the user approves the first execution tool for the task, CliGate flips a conversation flag that lets later steps continue without another approval roundtrip. The assistant still feeds the real tool result back into the continuation run, so it can keep working from what actually happened instead of pretending the approval itself completed the task.
The escape hatch matters too:
/safe
That turns the conversation back into explicit confirmation mode.
The continuation had to carry the real result
There was another subtle part.
When a user approves a pending action, the system cannot just say "confirmed" and stop. It has to continue the original job.
The continuation prompt now tells the assistant that the previous tool call was approved, already executed, and produced a real result. That stops the assistant from asking for the same approval again or forgetting why the tool ran in the first place.
This made the assistant feel much less like a form with a chatbot attached to it. The flow became:
- ask once for the risky boundary
- execute the first step
- continue the task with real observations
- only interrupt again when the user explicitly returns to safe mode or a genuinely different boundary appears
The interesting part is that the fix is not "disable approvals." It is "remember what approval was for."
I also hit a language bug
This same continuation path exposed a smaller but very visible bug.
The continuation message was system-generated and written in English. When the original user task was Chinese, the assistant sometimes detected the system continuation as the latest "user" text and switched the next confirmation prompt to English.
That produced an ugly mixed-language workflow: first approval in Chinese, later approval prompts in English.
The fix was simple in principle: system-authored continuation turns should not decide the reply language. The assistant now looks back to the latest genuine user message in the conversation before choosing the response language.
That is the kind of detail that seems minor until you use the tool for a multi-step task. Then it is the difference between "this assistant is tracking me" and "this assistant is reacting to its own plumbing."
The rule I am keeping
I do not think agent approval should be all-or-nothing.
For local tools, the useful middle ground is task-scoped trust:
- ask before crossing a meaningful boundary
- remember that approval for the current task
- keep an obvious way to return to strict mode
- do not let system continuation messages masquerade as user intent
That is now how I am shaping approvals in CliGate, the local control plane I use for Claude Code, Codex CLI, Gemini CLI, desktop automation, channels, and model routing.
The project is open source here: CliGate.
If you are building local agents, how are you handling approval fatigue: per tool call, per task, per session, or something more granular?
Top comments (0)