recordToolOutcome updates the failure streak from one finished tool result. A success (or a failure of a different target) resets the streak; a same- target failure extends it. lastToolKey was stamped in dispatchNextTool for the call this result belongs to.
(name, content string)
| 1144 | } |
| 1145 | } |
| 1146 | return chmctx.Message{}, false |
| 1147 | } |
| 1148 | |
| 1149 | // newestAssistantEmpty reports whether the turn's final assistant message |
| 1150 | // carried neither text nor a structured tool call. A clean finish always has a |
| 1151 | // summary and a continuing turn always has a tool call, so an empty newest |
| 1152 | // assistant message is always an anomaly: the model stopped mid-task, or its |
| 1153 | // call streamed into the reasoning channel and was dropped before reaching us. |
| 1154 | func newestAssistantEmpty(history []chmctx.Message) bool { |
| 1155 | msg, ok := newestAssistant(history) |
| 1156 | return ok && strings.TrimSpace(msg.Content) == "" && len(msg.ToolCalls) == 0 |
| 1157 | } |
| 1158 | |
| 1159 | // newestAssistantUnverified reports whether the turn's final assistant message |
| 1160 | // already carries an "unverified" marker, the honest self-assessment the finish |
| 1161 | // nudge exists to elicit. Case-insensitive: the model writes "unverified" / |
| 1162 | // "Unverified" interchangeably. Used to suppress the finish nudge on a finish |
| 1163 | // that already named what it couldn't prove (see maybeVerifyNudge). |
| 1164 | func newestAssistantUnverified(history []chmctx.Message) bool { |