.NET: expose pending tool approval requests from a restored AgentSession - #7867
Draft
Atharva Vichare (atty57) wants to merge 1 commit into
Draft
.NET: expose pending tool approval requests from a restored AgentSession#7867Atharva Vichare (atty57) wants to merge 1 commit into
Atharva Vichare (atty57) wants to merge 1 commit into
Conversation
Hosts with durable sessions had no supported way to discover that a conversation is paused on a human-in-the-loop tool approval. The state was already persisted by ApprovalResponseBindingChatClient under the internal `_pendingApprovalRequests` state bag key, so the only working approach was to parse that private field out of serialized session metadata. Adds AgentSession.TryGetPendingToolApprovalRequests(), which reads the same recorded state through a supported API, so a host can restore its approval UI after a reload and submit the approval by request id. Addresses option 2 of microsoft#7862.
Atharva Vichare (atty57)
deployed
to
github-app-auth
August 25, 2026 18:23 — with
GitHub Actions
Active
Atharva Vichare (atty57)
deployed
to
github-app-auth
August 25, 2026 18:23 — with
GitHub Actions
Active
Atharva Vichare (atty57)
deployed
to
github-app-auth
August 25, 2026 18:24 — with
GitHub Actions
Active
Atharva Vichare (atty57)
deployed
to
github-app-auth
August 25, 2026 18:24 — with
GitHub Actions
Active
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a public API for recovering pending tool approvals from durable .NET agent sessions.
Changes:
- Adds
TryGetPendingToolApprovalRequests. - Tests persistence round-trips, consumed approvals, and empty state.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
ToolApprovalAgentSessionExtensions.cs |
Exposes pending approvals from session state. |
ApprovalResponseBindingChatClientTests.cs |
Tests approval recovery behavior. |
💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.
| AgentJsonUtilities.DefaultOptions) | ||
| && pending is { Count: > 0 }) | ||
| { | ||
| requests = pending; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation & Context
A host application with durable sessions has no supported way to discover that a conversation is paused on a human-in-the-loop tool approval.
ApprovalResponseBindingChatClientalready records every model-originatedToolApprovalRequestContentinto the session state bag under the internal_pendingApprovalRequestskey, and consumes the entry once a matchingToolApprovalResponseContentis bound. That state is correct and durable — it just isn't reachable through a public API.So when a user closes or refreshes a conversation that stopped at an approval request and later reopens it, the host can only recover the pending approval by parsing that private state bag field out of serialized session metadata. Field names and shapes there are not a public contract, which makes the workaround fragile. Hosts also end up maintaining a duplicate mirror of the same state purely to have a stable request id to submit against.
Without that recovery the host renders message history only, misses the pending approval, and can let the user send a new message after an unresolved assistant tool call — producing a sequence strict chat-completions providers reject with
An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'.Description & Review Guide
What are the major changes?
Adds
ToolApprovalAgentSessionExtensions.TryGetPendingToolApprovalRequests, an extension onAgentSessioninMicrosoft.Agents.AI:It reads the same state
ApprovalResponseBindingChatClientwrites, returningfalsewhen there is no pending approval. It follows the shape of the existingAgentSessionExtensions.TryGetInMemoryChatHistory. Three unit tests are added toApprovalResponseBindingChatClientTests, the primary one covering a state bag serialize/deserialize round trip so the reload scenario from the issue is exercised end to end.What is the impact of these changes?
Purely additive and read-only. No behavioral change to approval binding, recording, or consumption, and no change to what gets persisted. A host can now, after
DeserializeSessionAsync, detect the paused approval, restore its approval UI, and submit by request id — removing the need for a host-side mirror of the pending approval list.This addresses option 2 of the three remedies proposed in the issue. Option 1 (persisting
ToolApprovalRequestContentinto durable chat history rather than a bareFunctionCallContent) changes what goes into message history and affects replay across providers, so it is intentionally left out of this PR as a design decision for maintainers. I'm happy to follow up on it separately if you'd like it.What do you want reviewers to focus on?
Whether the API name, placement, and return shape are what you'd want long term — in particular whether this belongs as an
AgentSessionextension inMicrosoft.Agents.AIor somewhere more discoverable, and whether it should also expose a way to correlate a pending request back to theFunctionCallContentalready present in message history.Related Issue
Fixes #7862
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.