A sub-agent in Atlarix is a helper the main agent starts for one part of a task. Several can run at once, each with its own context and tools, and any code change a sub-agent makes comes back as a proposal you accept before it is written. From 15.12.0 to 15.13.3, every sub-agent stopped after its first step. This post explains how Atlarix runs sub-agents, how one option that was no longer passed caused the stop, and how 15.14.0 fixed it.
What a sub-agent is in Atlarix
Some tasks split into parts that don't depend on each other. "How do auth, billing and notifications interact?" is three searches. The main agent can start one sub-agent per part and keep working while they run.
Atlarix has two built-in kinds:
- explore searches your code and the web and reads files. It is read-only.
- build drafts a scoped edit. It never writes to disk.
Each sub-agent shows as its own card in the chat, with its thinking and tool calls in order. The main agent folds in each result as it arrives. A long one moves to the background, where the running-tasks chip lists it next to background commands. You can read its narration or stop it. You can also define your own agents in .atlarix/agents/. The sub-agents guide covers all of it.
Why build sub-agents return proposals
Parallel helpers that write straight to disk would be hard to follow and hard to undo. So a build sub-agent's edits are recorded as a proposal per file. The main agent reads each diff and decides. Accepting one writes the sub-agent's exact bytes, and a file that isn't on your allow-list still asks you first, the same as any other edit. A delegated change goes through the same approval as one the main agent made itself.
That design only works if the sub-agent gets far enough to make an edit. From 15.12.0 to 15.13.3, it didn't.
How a sub-agent's loop runs
Each sub-agent is one call to streamText from the AI SDK (Atlarix is on ai@7), with its own system prompt and tool set. A single call can run many steps. The model asks for a tool, the SDK runs it and sends the result back, and the model decides what to do next. That repeats until something tells it to stop.
The stopWhen option is that something. It takes a list of conditions, and the SDK checks them after each step. The main turn passes its own list. A sub-agent should keep going until the model stops asking for tools.
What changed in 15.12.0
15.12.0 reworked how a long turn compacts its context. Before, a turn ended its round at the compaction point, compacted, and started a new round. After the change, it compacts in place before each step, inside the SDK's prepareStep hook.
The sub-agent's old stopWhen had one job, which was to end the round at the compaction point. Under the new design that job was gone, so the condition was deleted. The main turn kept its stopWhen list. The sub-agent call was left with none.
In ai@7, a streamText call with no stopWhen gets a default of isStepCount(1) (also exported under its older name, stepCountIs(1)). One step. So every sub-agent made one tool call, usually a file read, and the call ended there.
Why it was hard to see
Nothing failed. The sub-agent's run ended normally and reported "completed". Its card showed a read, which looks like a sensible first move. A build sub-agent's proposal set was simply empty.
The task still got done, because the main agent saw a finished helper with little to show and did the work itself. The cost was time and tokens, and the parallel work never happened. It hit every model the same way.
We found it by watching a recording of a real session on 15.13.3. Each sub-agent did one read, about 3k tokens, and returned no proposal. Then the main agent repeated the job.
The fix is one line
The sub-agent call now passes stopWhen: [], an empty list. With no conditions, the round runs until the model stops calling tools, the same way the main turn ends. The sub-agent gets no step cap, so a long task is not cut short.
The line carries a comment so the next change to that call doesn't repeat the mistake. Leaving stopWhen out is not the same as passing an empty list, because the SDK fills in one step. A follow-up change also removed a continuation loop for compaction that nothing could reach any more.
Tests that drive the real loop
A test that mocks streamText would have passed through all of this. The new tests run the real sub-agent function through the real streamText, with a scripted model from the AI SDK's test helpers in place of a provider. The model answers with fixed steps:
- An explore sub-agent scripted read, read, answer must run both reads.
- A build sub-agent scripted read, edit, answer must record the edit as a proposal.
On the old code both tests failed, with only the first tool call run. With the fix they pass.
Checked live
In a live check of the fixed build, we gave the agent three jobs for three sub-agents. Each one did real work, between 3 and 8 steps of reading and then editing, instead of one read and a stop. Their proposals came back to the main agent and were applied.
What we take from it
A default you don't pass is still a choice, made by the library. When a change deletes an option, check what the library does without it. For an agent loop, the useful test counts steps on the real loop. A status of "completed" says the run ended. It doesn't say the work was done.
The fix shipped in Atlarix 15.14. Sub-agents now run until their part is done, in the desktop app, the CLI and the editor extension. The full list of changes is in the changelog.
FAQ
What is a sub-agent in Atlarix?
A helper the main agent starts for one part of a task. Several run in parallel. Explore sub-agents search and read without changing anything; build sub-agents draft edits that come back as proposals.
Do Atlarix sub-agents change my files directly?
No. A build sub-agent's edits are recorded as a proposal per file. The main agent reviews each diff, and a file that isn't on your allow-list still asks you before it is written.
Which Atlarix versions had the one-step sub-agent bug?
15.12.0 through 15.13.3. Each sub-agent stopped after its first tool call and reported completed. Atlarix 15.14.0 fixed it.
What does stopWhen do in the AI SDK?
It sets when a multi-step streamText or generateText call stops: the SDK checks its conditions after each step. In ai@7, leaving it out means a single step; an empty list means the call runs until the model stops calling tools.
Originally published at atlarix.dev.
Top comments (0)