fix(subagents): recover a best-effort answer when a worker loops to its step cap#50
Merged
Conversation
…ts step cap When a sub-agent exhausts its stepCountIs(10) budget while still calling tools (finishReason 'tool-calls'), the orchestrator previously discarded the run and reported a bare truncation notice — a result the worker often already had in hand was lost. Now that finish reason triggers ONE final close-out turn with no tools (SUBAGENT_CLOSEOUT_PROMPT via generateText): the model cannot loop, so it must synthesize a best-effort answer from its own transcript. That answer is carried as the trailing message content (flagged stepLimitReached) and surfaced to the lead via subAgentModelOutput prefixed with SUBAGENT_PARTIAL_PREFIX so it is treated as possibly incomplete. Only when the close-out call itself fails does it fall back to the standalone step-limit notice. - mock-model: add "loop forever" / close-out seams and salt tool-call ids per step so a multi-step loop gets distinct ids. - docs/architecture/sub-agents.md: document the step-budget close-out path. - unit + e2e coverage for the recovered-answer and empty-fallback cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When a sub-agent exhausts its
stepCountIs(10)budget while still calling tools(
finishReason: 'tool-calls'), the orchestrator used to discard the run and report a baretruncation notice — losing a result the worker often already had in hand.
Now that finish reason triggers one final close-out turn with no tools
(
SUBAGENT_CLOSEOUT_PROMPTviagenerateText): the model can't loop, so it synthesizes abest-effort answer from its own transcript. That answer reaches the lead via
subAgentModelOutput, prefixed withSUBAGENT_PARTIAL_PREFIXso it's treated as possiblyincomplete. If the close-out call itself fails, it falls back to the standalone step-limit notice.
loop forever/ close-out seams + per-step tool-call-id salting so amulti-step loop gets distinct ids.
Why: a weak worker that keeps calling tools after it already has the answer would hit the
step cap and have its work reported as a failure; this preserves the result, flagged partial.
Heads-up: adds one bounded
generateTextcall on the step-cap path, and changes what thelead receives on that path (a partial answer instead of a fixed notice). Unrelated: the
regenerated
put-req/.typeartifacts drop a stale moderator-description sentence to match thecommitted schema source.