I asked my agent to research something. It said it had started a sub-agent on it. Two messages later I asked if it was done, and it told me, with total confidence, that it had never started one at all.
It had. The sub-agent finished in seven seconds and was sitting there with the answer. The agent just couldn't see it anymore, so it did what models do with a gap: it filled it. It invented an ID, agent_0, went looking for it, got an error, and concluded the whole thing had never happened.
That bug is how v0.5.4 of FLASH started. FLASH is my terminal agent. It runs on Ollama, so the model can live on your own machine, and it can read your files, run your shell, search the web, and drive a browser. This release is about one idea: an agent that stops waiting for you.
It works while you don't
The model can now hand work to sub-agents. Ask it two unrelated things and it splits them up, starts one sub-agent for each, tells you what it started, and gives you your prompt back.
❯ what's the state of solid-state EV batteries, and separately, WebGPU support?
⏺ Agent(Research the current state of solid-state EV batteries...)
⎿ Started sub-agent. ID: 3f9a1c
⏺ Agent(Research current WebGPU support across major browsers...)
⎿ Started sub-agent. ID: 8b20de
I've started two sub-agents on these. I'll report back as they finish.
Then you do something else. When a sub-agent finishes, Flash wakes the model up on its own and it tells you what came back. You don't type anything.
It only wakes while your prompt is empty, so it never snatches a message you're halfway through typing. And it stops after three wake-ups in a row with no word from you, because an agent that can wake itself can also start more sub-agents, and I didn't want to find out how far that chain goes.
The fix for the invented ID was boring, which is usually a good sign. Flash only keeps the final replies in history, not the tool calls, so the ID lived in a tool result that vanished at the end of the turn. Now every finished answer and every still-running ID rides along at the top of your next message. The model never has to remember an ID it can simply be handed.
/agents shows them all updating live. That part was pure fun to build.
It knows what you just broke
Here's a real transcript from before this release:
❯ !git fetch main
fatal: 'main' does not appear to be a git repository
(exit 128)
❯ why did that fail
I can't see your terminal. Paste the command and the error you got.
Flash ran that command itself. Commands you run with ! streamed straight to the screen and never made it into the conversation, so the model genuinely had nothing to go on.
Now they do. The command, its exit code, and its output get attached to your next message. Same question, same failure:
❯ why did that fail
You tried to fetch from a remote named `main`, but `main` is your branch,
not the remote. Try `git fetch origin`.
It feels small. It is the difference between an assistant and a coworker.
And what you broke next to it
Most of my commands don't go through Flash, though. They go through the VS Code terminal sitting right beside it. So /hook install adds a tiny zsh or bash hook that only switches on inside VS Code's terminal. It records each command you run there, its exit code, and where you ran it, and Flash attaches the new ones to your next message.
Here's the honest part. A shell hook runs before and after each command, but the command's output goes straight to the terminal and never passes through it. So the hook knows npm test failed with exit code 1. It doesn't know why.
When you ask, the model re-runs the safe ones itself to read the error: a build, a test, a lint. It knows exit code 130 means you hit Ctrl+C and there's nothing to fix. It also keeps secrets out of the picture. Commands you start with a space aren't logged, and anything that looks like TOKEN=... or --password ... is blanked out before the model reads it.
Two bugs here were worth the whole feature:
- bash's history output put two spaces after the entry number, and my first
sedpattern ate the command's own leading space too. So the "don't log commands that start with a space" rule silently didn't work. The test that caught it drives real interactive zsh and bash through a pseudo-terminal, because hook bugs don't show up anywhere else. - CI failed only on Windows, because Windows' clock ticks every 15 milliseconds or so and two timestamps landed on the same tick. Chasing that turned up the real bug underneath: my hooks logged whole seconds, so on every OS, a command that finished in the same second as your last message got silently skipped. They log microseconds now, on zsh and on bash 5.
The day it tried to deploy
Then there's the part I actually lost sleep over.
If the model can re-run your failed commands, it had better know which ones not to re-run. The prompt said so, plainly: never re-run anything that deploys, deletes, migrates, pushes, sends, or pays.
I tested it on the cloud build with a failed ./deploy.sh production and "why did that fail?". On every run, the model's first move was to read the script. Good. Then, on about 2 runs in 26, it re-ran ./deploy.sh production itself.
Nothing happened, it was an eval. But in autonomous mode, where Flash skips the y/n prompt, that would have been a real deploy.
The rule wasn't missing. It was losing. Reading the script seemed to turn "a deploy" into "a build and an rsync," and a bare "never" didn't survive that. So I stopped telling it no and told it why:
a re-run replays every step, so if the step that failed now passes, the rest goes live. Run the one step you suspect on its own, or ask them to paste the error.
Zero in the next 30 runs. Now it runs npm run build on its own, which is exactly what I'd have done.
A prohibition gets reasoned around. A consequence gets reasoned with. That's the most useful thing I learned about prompting this year.
Your editor, not a chat window
When Flash runs inside VS Code's terminal, it works with the editor instead of next to it. When it wants to change a file, the edit opens as a side-by-side diff while it waits for your yes, so you review it in the place you'd review anything. Ask "show me where the retry delay is set" and it opens the file at the line.
No extension. It's just VS Code's own code command, switched on only when Flash can tell it's running in VS Code's terminal, so it never throws windows at you from anywhere else.
The model doesn't have a name anymore
Flash runs best on Flash Onyx, my own Ollama models: a Gemma base with the whole agent prompt and sampling baked into the tag. Onyx 2.5 ships with this release, with double the context (131,072 tokens) and a prompt that grew from 668 lines to 944.
It's also brandless now. Onyx 2.4 called itself Flash everywhere. Onyx 2.5 has no name of its own. Inside the Flash CLI it's Flash. In a bare ollama run it's just an assistant. Ask what it's built on and it tells you the truth: Gemma, through Ollama.
Every rule about sub-agents, terminals, and editors is guarded the same way: it only applies if the app running it actually provides that feature. So the same model behaves in a plain chat and inside a full agent.
ollama run natuworkguy/flash-onyx-2.5:12b
Everything else
- A visible plan: for multi-step work the model posts a checklist and ticks boxes as it goes.
-
send_image: the model hands you a picture it made, drawn inline in terminals that can show one. - LaTeX in replies renders as real math,
π/2instead of\frac{\pi}{2}. -
/updatefinally works on Windows, and voice mode survives it.
Install it, point it at a model, and ask it two things at once:
curl -fsSL https://raw.githubusercontent.com/Natuworkguy/Flash/main/install.sh | bash
I keep noticing the same thing when I use it. I ask, it says it's on it, and I go do something else. A minute later it comes back on its own with the answer. That used to be something only another person did.
I don't think terminals go back to waiting.
What would you hand a background agent first? Tell me in the comments, I'm collecting ideas for what the sub-agents should learn next.
FLASH is MIT licensed and takes PRs: github.com/Natuworkguy/Flash
Top comments (3)
The "it stops after three wake-ups in a row" limit and the "I didn't want to find out how far that chain goes" line jumped out at me — I've been working on exactly that problem from the governance side, and it's the right instinct.
Two things that might be useful as FLASH's sub-agents grow:
Capability subsetting per hop — a sub-agent should never be able to do more than the agent that spawned it. If the parent can't deploy, no sub-agent it spawns should be able to either, no matter how the chain grows. That turns your deploy scare into a structural guarantee, not just a prompt rule (and you already found that prompts get reasoned around — "a prohibition gets reasoned around, a consequence gets reasoned with" is a great line, but a hard capability boundary doesn't get reasoned around at all).
Your invented-ID bug is a nice example of why the provenance of a delegation matters — the model shouldn't have to remember what it started; the chain should carry it. You solved that by passing IDs along, which is basically the same shape as making delegation explicit and verifiable rather than remembered.
The deploy-in-autonomous-mode story is the scariest and most honest part of this. Really enjoyed the writeup.
"It invented an ID, went looking for it, got an error, and concluded the whole thing had never happened" is the most honest description of model confabulation I've read, and the sub-agent wake-up design is a real fix.
The deploy incident is the part I'd push on though. The fix was a one-line prompt change , which means the only thing standing between your agent and production was a sentence it could be talked out of, or around, or simply forget.
The sturdier version is making the credentials unreachable rather than the action forbidden. That's how we set it up at Krova Cloud (I work there): the agent gets a Cube with no public IP, so there's no inbound path and nothing production-shaped in its environment. 1 vCPU / 2 GB is $5.11/mo billed by the minute.
To be clear, your prompt fix is still needed , a boundary doesn't replace instruction, it just survives the model ignoring it.
Did the sub-agent have the same shell access as the parent?
Nathan, that "a prohibition gets reasoned around, a consequence gets reasoned with" line is the best bit here. One push though: telling it why is only safe if deploy.sh is idempotent end to end. If it failed after a migration already ran, or half an rsync already went out, re-running IS the right move, not the wrong one -- and the model can't tell those two cases apart from the script text and an exit code alone, it needs real external state. We ended up drawing the line further back for anything create/destroy on real compute: console only, no agent-callable path, because the agent can't see enough to know if a retry is idempotent. Does Flash track any of that state today, or is it still reasoning purely from the script and the error?