Skip to content

.NET: collect partial native approval decisions durably behind an opt-in - #9791

Closed
Ran Shemtov (ranst91) wants to merge 2 commits into
microsoft:mainfrom
ranst91:fix/pni722-durable-partial-approvals
Closed

Ran Shemtov (ranst91) wants to merge 2 commits into
microsoft:mainfrom
ranst91:fix/pni722-durable-partial-approvals

Conversation

@ranst91

@ranst91 Ran Shemtov (ranst91) commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Problem and behavior

A native approval batch currently rejects every request omitted from the next run. That intentional default prevents applications from accepting decisions one at a time across session reloads.

Add ChatClientAgentOptions.EnablePartialApprovalResponses (default false). With this option, the native approval-binding layer saves original-bound decisions and completed sibling results in session state and returns unanswered requests without executing tools or calling the model. Once the batch has answers, the existing binding/invocation path resumes it. The first recorded answer wins; duplicate or conflicting answers cannot replace it. This keeps the current default policy unchanged.

Collection-only ordinary input is queued in native session state when the downstream service/history path has not received it. Synthetic waiting responses preserve the existing conversation identity.

The change includes serialized-state regression coverage and usage documentation, including the distinction between decided and executed, session serialization/concurrency requirements, and unchanged crash/side-effect transaction limitations. It does not change the AG-UI host or claim browser/Intelligence parity.

Validation

303 native approval tests pass, including 32 combinations of streaming/non-streaming, both history-persistence modes, partial approve/decline, late approve/decline, frontend/native batches, fresh-agent reloads, duplicate/conflicting answers, tampered caller call identities/arguments, canonical persisted results, and no re-execution of completed calls. Existing default-policy approval tests remain green. Four additional service-history cases verify that waiting responses retain ConversationId and that ordinary text accompanying a decision survives fresh-agent save/reload, reaches the resumed model once, and is not sent again after successful completion.

Local validation used SDK 10.0.201/runtime 10.0.5 and cache-only dependency overrides because this checkout requires unavailable SDK 10.0.401 and newer analyzer/test packages. MEAI stayed at 10.10.0 with Abstractions 10.10.1. Cached analyzers passed with incompatible existing-baseline PublicAPI diagnostics and an unrelated existing whitespace diagnostic suppressed. This is not a claim that the unmodified upstream toolchain passed; CI should run the repository-declared SDK/packages.

Portable deterministic reproduction: dotnet/tests/Microsoft.Agents.AI.UnitTests/ChatClient/ChatClientAgent_PartialApprovalsTests.cs. Run the native unit-test project with --filter-class '*Approval*'. Source behavior and usage are documented in docs/features/partial-tool-approvals.md.

Review follow-up

Commit aeadfd833 addresses missing ConversationId and lost ordinary input on collection-only runs. The mixed frontend/native provider-history finding is tracked separately and is not resolved by this follow-up. The PR remains a draft pending that composition and real-host/provider validation.

Copilot AI balanced review requested due to automatic review settings October 28, 2026 11:11
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net labels Oct 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Collection-only runs can lose ordinary input or throw due to a missing ConversationId, while resumed model input can contain orphan tool results that providers reject.

3 open issues
What changed in this PR

Adds opt-in durable collection of partial tool-approval decisions to .NET ChatClientAgent, while preserving the default policy of rejecting unanswered requests on the next run.

Changes:

  • Adds and wires EnablePartialApprovalResponses, including cloning, configuration validation, and public API declarations.
  • Stores decisions and completed sibling results in session state, returning unanswered requests until the batch is ready.
  • Adds serialization/reload regression coverage and documents persistence requirements and limitations.
File Description
dotnet/​tests/​Microsoft.Agents.AI.UnitTests/​ChatClient/​ChatClientAgent_PartialApprovalsTests.cs Tests partial decisions, reloads, replay, and option validation.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​netstandard2.0/​PublicAPI.Unshipped.txt Declares the option for .NET Standard 2.0.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net9.0/​PublicAPI.Unshipped.txt Declares the option for .NET 9.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net8.0/​PublicAPI.Unshipped.txt Declares the option for .NET 8.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net472/​PublicAPI.Unshipped.txt Declares the option for .NET Framework 4.7.2.
dotnet/​src/​Microsoft.Agents.AI/​PublicAPI/​net10.0/​PublicAPI.Unshipped.txt Declares the option for .NET 10.
dotnet/​src/​Microsoft.Agents.AI/​ChatClient/​ChatClientExtensions.cs Configures partial collection and rejects incompatible options.
dotnet/​src/​Microsoft.Agents.AI/​ChatClient/​ChatClientAgentOptions.cs Defines, documents, and clones the opt-in setting.
dotnet/​src/​Microsoft.Agents.AI/​ChatClient/​ApprovalResponseBindingChatClient.cs Collects durable decisions and prepares completed batches for resumption.
docs/​features/​partial-tool-approvals.md Documents usage, durability constraints, and regression coverage.

Review effort: Balanced

var collected = this.CollectPartialResponses(messages, session);
if (collected.Waiting is not null)
{
return new ChatResponse([new ChatMessage(ChatRole.Assistant, collected.Waiting)]);
var copy = message.Clone();
copy.Contents = message.Contents.Where(content => content switch
{
ToolApprovalRequestContent request => !settled.Contains(request.ToolCall.CallId),
Comment on lines +291 to +293
if (waiting.Count > 0)
{
return (input, waiting);
@ranst91
Ran Shemtov (ranst91) deleted the fix/pni722-durable-partial-approvals branch October 29, 2026 16:49

This branch was successfully deployed

1 active deployment
github-app-auth — aeadfd83 Deployed Oct 28, 2026 by ranst91 via add_label #26405
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs .NET Usage: [Issues, PRs], Target: .Net

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants