Skip to content

fix(chat): surface the fallback when a turn ends on a tool-call step - #55

Merged
BatLeDev merged 1 commit into
mainfrom
fix-empty-turn-after-tool-step
Sep 1, 2026
Merged

fix(chat): surface the fallback when a turn ends on a tool-call step#55
BatLeDev merged 1 commit into
mainfrom
fix-empty-turn-after-tool-step

Conversation

@BatLeDev

@BatLeDev BatLeDev commented Sep 1, 2026

Copy link
Copy Markdown
Member

A turn can end with no answer, no error and no fallback. Observed once in three during a traced audit of the assistant on dataset metadata: the lead announced a delegation, launched two sub-agents, both finished cleanly server-side (finishReason: stop on each), and the lead never issued another request. The chips settled, nothing was appended, the input became available again. From the user's side the assistant simply stopped mid-thought — and in that particular turn, 2 columns out of 22 had been configured and nothing else.

Why the existing guard missed it

use-agent-chat.ts already protects against silent drops:

if (!mainScope.producedText) { /* push the fallback bubble */ }

producedText latches on the first text delta of the turn and never resets. The model almost always announces what it is about to do in the very step that calls the sub-agents — here, « Je délègue l'analyse approfondie. » — so by the end of the turn producedText is true and the guard stays quiet. A turn that spoke once and then died is indistinguishable from a turn that answered.

The fix

Track whether the step that just finished issued a tool call, and fall back on that too:

if (!mainScope.producedText || mainScope.lastStepHadTool) {  }

A turn whose last step called a tool is a turn the model meant to continue: it had a result to read and said nothing. That is true whatever it said earlier, which is what the text-based signal could not express.

It also covers, explicitly, a case the original comment already named but could not catch when the turn had spoken: stopWhen: stepCountIs(10) reached on a tool call.

lastStepHadTool is set at finish-step from the existing stepHadTool, which was already tracked for the activity label — no new stream bookkeeping.

Testing

5 new unit tests in tests/features/empty-turn/, pinning the distinction the fix rests on — in particular that text and the sub-agent calls landing in one step still counts as a turn that never came back, which is the exact shape of the observed failure and the case my first attempt (a text-based signal) got wrong. 339 unit tests pass; lint and check-types clean.

What is not verified

The e2e suite has not been run — it needs the dev stack up. Two things a reviewer may want to exercise before merging:

  • chat-silent-drop and chat-subagent, for regressions on the paths that already surface a fallback;
  • the behaviour change this introduces: a turn that produced text and exhausted its 10-step budget on a tool call now gets a fallback bubble appended after its partial answer, where previously it got none. That is arguably the point — the turn was truncated — but it is a visible change and no existing test covers a main-agent step-cap turn that produced text.

There is a mock seam for this shape already (loop forever, used by chat-subagent for the sub-agent close-out path), so an e2e for the main-agent case would be cheap to add if you want it in this PR.

The empty-turn guard keyed on `producedText`, which latches on the first
word of a turn and never resets. A lead agent that announces a delegation
and calls its sub-agents in the same step therefore looks, at the end of
the turn, exactly like one that answered: the guard stays quiet and the
user is left with settled chips, no answer and no error.

Observed on a dataset-metadata turn: two sub-agents finished cleanly
server-side (both `finishReason: stop`), the lead never issued another
request, and the chat simply became available again — one turn in three
during that campaign.

Track instead whether the step that just finished issued a tool call. A
turn whose last step called a tool is one the model meant to continue: it
had a result to read and said nothing. That also covers the case the
original comment already named, the step limit being reached on a tool
call.
@github-actions github-actions Bot added the fix label Sep 1, 2026
@BatLeDev
BatLeDev merged commit 9333b7b into main Sep 1, 2026
4 checks passed
@BatLeDev
BatLeDev deleted the fix-empty-turn-after-tool-step branch September 1, 2026 09:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant