Repository navigation
.NET: Add a do-not-retry reason when samples reject tool approvals - #10095
Conversation
There was a problem hiding this comment.
🟢 Approval recommended
No active review issues block the sample updates that add explicit no-retry guidance for user-denied tool calls.
No open issues 🎉
What changed in this PR
Updates .NET samples to distinguish explicit user rejection from an unanswered approval request. Rejections now tell the model not to retry the tool call, addressing repeated approval prompts described in #8503 without changing library-level automatic rejection behavior.
Changes:
- Add a consistent “The user rejected this tool call. Do not retry it.” reason to user-denied function-tool approvals.
- Preserve approval behavior and service-managed hosted MCP responses.
- Update the AGUI README to explain the rejection reason.
| File | Description |
|---|---|
dotnet/samples/05-end-to-end/M365Agent/AFAgentApplication.cs |
Adds the rejection reason to user-submitted approval decisions. |
dotnet/samples/04-hosting/FoundryHostedAgents/responses/Using-Samples/Hosted-Toolbox-AuthPaths-Client/Program.cs |
Adds the reason for function tools; preserves hosted MCP handling. |
dotnet/samples/02-agents/Harness/Harness_Shared_Console/Observers/ToolApprovalObserver.cs |
Standardizes the “Deny” response reason. |
dotnet/samples/02-agents/AGUI/Step04_HumanInLoop/Client/Program.cs |
Adds the reason to interactive client rejections. |
dotnet/samples/02-agents/AGUI/README.md |
Documents the client’s explicit no-retry reason. |
dotnet/samples/02-agents/AgentSkills/Agent_Step07_SkillsAutoApproval/Program.cs |
Adds the reason when manual approval is denied. |
dotnet/samples/02-agents/Agents/Agent_Step11_Middleware/Program.cs |
Adds the reason to middleware-generated rejections. |
dotnet/samples/02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals/Program.cs |
Demonstrates explicit rejection guidance in the introductory sample. |
dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step12_Middleware/Program.cs |
Adds the reason to Foundry middleware rejections. |
dotnet/samples/02-agents/AgentProviders/foundry/Agent_Step04_UsingFunctionToolsWithApprovals/Program.cs |
Adds the reason to Foundry function-tool rejections. |
Review effort: Balanced
There was a problem hiding this comment.
MAF Automated Review — Iteration 1
Result: No findings
Scope: full PR (1 commit(s)): 04947ac3f6d9
Model: gpt-5.6-sol
Overview
This sample-only change consistently supplies explicit do-not-retry guidance when users reject Agent Framework function-tool calls while preserving approval behavior and the separate hosted MCP wire path. Existing framework binding logic preserves the reason, rejects forged responses, and consumes matched approval state. No Critical, High, or Medium defect was established.
Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
No publishable findings remained after source verification for this scope.
Motivation & Context
When a user rejects a tool call, the model sees a function result that says the call was rejected. Without a reason, some newer models read this as a temporary failure and ask for approval of the same call again (#8503).
Microsoft.Extensions.AI 10.11.0 tried to fix this by making the default rejection text say the call "must not be retried". That broke Agent Framework's automatic rejection of unanswered approval requests, because the model was then told never to retry a call that nobody actually rejected (#9656). That change has been reverted upstream (dotnet/extensions#7914). The guidance now is that the caller should say in the rejection reason what the model should do, because only the caller knows whether a retry makes sense.
Our .NET samples rejected tool calls without a reason, so they showed the pattern that leads to repeated approval prompts. This PR updates them to follow the new guidance.
Description & Review Guide
ToolApprovalResponseContentnow adds a reason when the answer is no:CreateResponse(approved: false, reason: "The user rejected this tool call. Do not retry it."). Approvals are unchanged. Updated samples:02-agents/Agents/Agent_Step01_UsingFunctionToolsWithApprovals02-agents/Agents/Agent_Step11_Middleware02-agents/AgentProviders/foundry/Agent_Step04_UsingFunctionToolsWithApprovals02-agents/AgentProviders/foundry/Agent_Step12_Middleware02-agents/AgentSkills/Agent_Step07_SkillsAutoApproval02-agents/AGUI/Step04_HumanInLoop/Client(and its README)05-end-to-end/M365Agent04-hosting/.../Hosted-Toolbox-AuthPaths-Client(function-tool path only)02-agents/Harness/Harness_Shared_Console(the "Deny" reason now uses the same wording instead of "User denied")GroupChatToolApprovalis also unchanged because it only ever approves.Related Issue
Fixes #9656
Fixes #8503
#9678 is also open for #9656. It changes the library so that automatic rejections of unanswered requests use different wording. This PR takes a different approach: the default rejection text stays neutral (now that the upstream change is reverted), and the samples show callers how to say "do not retry" in the rejection reason.
AI Assistance
AI assistance details: Sample code updates, README wording, and this PR description.
Contribution Checklist
breaking changelabel (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and title prefix in sync automatically.