When Should an Agent Loop Stop? A Deep Dive into DSH vs Pi
The article dissects where an AI agent loop can pause—model stream end, tool batch return, turn completion, driver activity, and long‑term goal—by examining DeepSeek Harness and Pi implementations, their code structures, stop‑reason handling, logging, and practical trade‑offs in production scenarios.
Why the stopping point matters
Integrating an agent into a ticket system lets it claim tasks, execute them, and report back. In a PR‑creation scenario the agent may create a PR, receive a 504 from the API, and be unsure whether to retry, risking duplicate PRs or silently missing failures. Even when the PR is created, CI may still be pending, and new user commands can arrive while the agent is idle.
Breaking “stop” into distinct layers
For a single PR task the program can pause at five logical layers:
Model flow end – the request received a termination signal (normal finish, token limit, error, or cancellation).
Tool batch return – all tool calls launched by the model have returned, but the model may still need to inspect the results.
Turn end – the turn is truly finished only when no pending tool results exist and next-step contains no new messages. Steering or tool‑added context can reopen the turn.
Driver activity end – the agent driver has no further turn to process and can return to idle.
Long‑term goal end – the persistent goal state reaches complete, blocked, or paused. An idle driver does not guarantee goal completion; the Goal Driver may later invoke followup() to wake the agent.
DeepSeek Harness (DSH) implementation
DSH’s driver loop is a simple while (await this.turn()) {} construct. The driver maintains three phases: idle, maintenance, and running. When entering running, the current turn, step, AbortController, and wakeRequested are attached. During maintenance the driver appears idle, but incoming messages are recorded as “needs wake‑up” and processed after maintenance finishes.
Cancellation is handled by writing the abort reason into the current activity and preventing new calls from being dispatched. Messages are routed to next-turn rather than being forced into the still‑closing turn.
When checking for idle, whenIdle() repeatedly compares activityDone to avoid false‑positive idle reports when a new driver has already started.
Turn‑level termination signals
Each turn records two fields: turnEnds (the reason the model stopped) and target (which determines whether to fetch next-turn or next-step). The step can end in three concrete results: completed – no tool calls or the tool batch explicitly requests closure. max‑tokens – the model hit its token limit. null – tools finished but the model must inspect the result once more.
In the PR example, after create_pr returns a URL, the CI status is still pending. The model receives null, knows it cannot finish the turn, and may either wait or query the build log.
while (await this.turn()) {
}Stopping is not a simple boolean
DSH fires agent/turn-stopping just before a turn ends. Plugins write a message to next-step instead of returning a boolean, allowing multiple plugins to contribute without overwriting each other. The core loop then reads the inbox again; if a message exists, a new step starts, otherwise the loop stops.
Tools can signal early closure with concludesTurn. DSH treats this as an OR condition – if any tool returns true, the batch is marked concluded, but pending messages still take precedence.
Pi implementation
Pi’s runLoop() keeps the long‑term goal outside the core loop. The inner loop focuses on tool callbacks and steering:
hasMoreToolCalls || pendingMessages.length > 0A typical iteration runs prepareNextTurn, processes pending steering, calls the model, executes tools, emits turn_end, and finally lets shouldStopAfterTurn decide whether to stop. When the inner queue is empty, Pi calls getFollowUpMessages(); if follow‑up messages exist, they start a new turn, otherwise the invocation ends with agent_end.
Pi exposes three entry points for extensions: getSteeringMessages(), getFollowUpMessages(), and shouldStopAfterTurn(). Extensions do not need to understand the persistent inbox, but they must handle merging follow‑up messages, state recovery after crashes, and decide whether the long‑term goal and approval logic should share the same termination semantics.
Key differences
Tool return handling : DSH uses an OR‑style concludesTurn flag, while Pi requires every finalized result to have terminate: true before the batch can end.
Stopping signal : DSH distributes the stop decision across model finish, tool results, inbox, lifecycle hooks, and a persistent state machine; Pi concentrates the decision in the current loop and a few host callbacks.
Process recovery : DSH writes interrupted and a paired tool/call / tool/result event when a crash occurs; Pi leaves recovery to the host.
Parallel execution ordering : DSH slots tool results and commits them in model order via commitReady(); Pi relies on Promise.all which preserves input array order.
Practical checklist
When diagnosing a stuck or duplicated PR, ask four questions that the article highlights:
After a tool returns, who decides whether the model should run again?
At which boundary does a new user input land, and can it be traced after cancellation?
When the agent is idle, who is authorized to wake it, and where are budget checks performed?
Can the logs distinguish which side‑effects have already happened after a timeout or crash?
For simple, single‑prompt tool calls Pi’s lightweight core is easier to maintain. For multi‑turn goals, approval workflows, high‑risk writes, and cross‑process recovery, DSH’s richer state handling provides clearer audit trails.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect
Professional architect sharing high‑quality architecture insights. Topics include high‑availability, high‑performance, high‑stability architectures, big data, machine learning, Java, system and distributed architecture, AI, and practical large‑scale architecture case studies. Open to ideas‑driven architects who enjoy sharing and learning.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
