maybeRunawayNudge appends one soft system note when a turn crosses maxToolRounds tool calls without finishing. The runawayNudged latch fires it exactly once per turn: this is consulted only when the pending queue drains, but toolRounds increments per call, so a multi-tool-call round can jump the cou
()
| 1194 | // flows through runToolCall; none are special-cased. lastToolKey records this |
| 1195 | // call's target so the failure nudge can tell when the model keeps retrying the |
| 1196 | // same failing operation (see recordToolOutcome). |
| 1197 | func (m Model) dispatchNextTool() (tea.Model, tea.Cmd) { |
| 1198 | call := m.pending[0] |
| 1199 | m.pending = m.pending[1:] |
| 1200 | m.appendLine(styleDim.Render(tools.InlineStatus(call))) |
| 1201 | m.lastToolKey = toolTargetKey(call) |
| 1202 | m.toolRounds++ |
| 1203 | if call.Name != tools.ReadFileName { |
| 1204 | m.turnActed = true |
| 1205 | } |
| 1206 | m.phase = phaseRunning |
| 1207 | return m, runToolCall(m.turnCtx, call) |
| 1208 | } |
| 1209 | |
| 1210 | // nudgeOrigin prefixes every deterministic backstop note. A weak (30B) model |
| 1211 | // reads a bare mid-turn system message as an empty/absent user turn ("the user |