Fix inverted tool approval badge in Duo chat
What does this MR do and why?
In Duo Agentic Chat, tool call status badges could show the opposite of the user's actual decision. When a tool call was denied and then another call of the same tool was approved, the denied card finalized as "Approved" and the approved card as "Cancelled".
The cause is in toolDenialTransformer. An approved request is followed by an
executed tool message sharing its tool_info.name; a denied request has none.
The transformer matched forward from each request, earliest first, so when a
denied request came before an approved one for the same tool, the denied (earlier)
request greedily claimed the later request's executed tool message — flipping both
badges.
This changes the matching to start from each executed tool and claim the nearest preceding unclaimed request of the same name. That request is the one that was approved; requests left without a tool are denied. The pairing is now correct regardless of the order the user approved and denied same-named tools in, and every existing case (approve-first, different tools, pending, multiple denials) still behaves the same.
References
Closes #619249
Related: !248785 (merged) (added MSW coverage for Duo chat tool calls and documented the
toolDenialTransformer Approved-vs-Cancelled behaviour this MR corrects).
Before / After
Deny the first tool approval prompt, then approve a second prompt for the same tool:
| Tool call | Before (badge) | After (badge) |
|---|---|---|
| First (denied) | Approved |
Cancelled |
| Second (approved) | Cancelled |
Approved |
How to set up and validate locally
yarn jest ee/spec/frontend/ai/duo_agentic_chat/transformers/tool_denial_transformer_spec.jsThe spec adds a case for the denied-first / approved-second same-tool sequence that reproduced the inverted badges.