Commit graph

15 commits

Author SHA1 Message Date
Rod Boev
4a7b851c8c fix(#6481): materialize pending turns without borrowed display indexes 2026-07-29 18:36:24 +00:00
Rod Boev
9b0506d31d fix(#6481): keep display ownership out of context indexes 2026-07-29 18:36:24 +00:00
Rod Boev
525edef5f7 Fix silent-result ownership for repeated prompts 2026-07-29 18:36:24 +00:00
Rod Boev
66438f07bc Settle verification turns from exact turn ownership 2026-07-29 18:36:24 +00:00
Rod Boev
920b48d709 fix(#6481): preserve repeated pending turns through verification cleanup 2026-07-29 18:36:24 +00:00
Rod Boev
5e447683f8 fix(#6481): keep verification cleanup from duplicating the current turn 2026-07-29 18:36:24 +00:00
Rod Boev
ad3770fd6e fix(#6481): restore marker-only verification cleanup 2026-07-29 18:36:24 +00:00
Rod Boev
663aec682e fix(#6481): prevent verification follow-up context contamination 2026-07-29 18:36:24 +00:00
chsong
139c76535b fix tool-limit final answer classification 2026-07-15 19:38:25 +00:00
b3nw
34fcc3a387 fix: drop messages-None writeback guard, reframe for invariant lineage
Per Greptile's review on #5717: the previous
`if result.get('messages') is not None` guard created an asymmetric
condition where the silent-failure classifier could miss the injected
fallback when the agent result had `messages: None` (which never
actually happens in practice since `finalize_turn` always returns a
list, but the asymmetry read as code smell). Drop the guard; the
writeback is now unconditional on `isinstance(result, dict)`.

Per the maintainer's review on #5717: there may be a
`finalize_turn` invariant on the agent branch this WebUI ships
against that already appends the fallback as an assistant row, in
which case the live bug is gone and this PR becomes a regression
guard. Reframe the test docstrings as dual-purpose pins of the user-
visible closure-text contract so they read correctly under both
interpretations.

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-07-07 00:04:27 +00:00
b3nw
02692cd64a fix: surface agent's graceful summary on iteration-limit exhaustion (closes #5494)
The WebUI already surfaces the model-generated summary when the agent's
tool-iteration budget is exhausted. The remaining gap: when the agent
hits the limit but its summary call yields no usable assistant content
(common with reasoning-only responses), the user sees a bare
`tool_limit_reached` error instead of any closure text.

`hermes-agent.handle_max_iterations()` always returns a non-empty
`final_response` (either the summary or a graceful fallback), and
`AIAgent` exposes that string on the result dict. The WebUI, by
contrast, only reads `result['messages']`, so an empty summary
left no final assistant answer and routed to the error path.

Inject `result['final_response']` as a final assistant turn when
`max_iterations_reached` fires and no usable answer exists in
`messages`. The existing `_mark_latest_assistant_tool_limit_status`
flow then attaches the status card and the user sees the closure
text instead of the bare error. Hermes-agent parity preserved (it's
literally the same string); the bare-error path stays for the case
where the agent died entirely (no fallback either).

Tests cover: fallback-injection surfaces closure text + status card,
no double-injection when a real summary already exists, the existing
'died entirely' apperror path is unchanged, and three unit cases for
the helper's skip conditions.

Co-authored-by: b3nw <b3nw@users.noreply.github.com>
2026-07-07 00:02:27 +00:00
Frank Song
02e72163a6 Fix tool-limit terminal failure annotation 2026-06-12 10:25:18 +08:00
Frank Song
136568fe81 fix(streaming): tighten tool limit terminal guards 2026-06-12 10:03:31 +08:00
Frank Song
08ba5a6676 Fix tool-limit terminal detection 2026-06-12 10:03:31 +08:00
Frank Song
ec23dac922 Handle tool limit terminal state 2026-06-12 10:03:31 +08:00