Skip to content

Python: bound stateless pause batches to user turns - #8449

Merged
Eduard van Valkenburg (eavanvalkenburg) merged 10 commits into
microsoft:mainfrom
eavanvalkenburg:approval-resume-stack
Sep 18, 2026
Merged

Eduard van Valkenburg (eavanvalkenburg) merged 10 commits into
microsoft:mainfrom
eavanvalkenburg:approval-resume-stack

Conversation

@eavanvalkenburg

@eavanvalkenburg Eduard van Valkenburg (eavanvalkenburg) commented Sep 17, 2026

Copy link
Copy Markdown
Member

Motivation & Context

Stateless mixed-pause discovery currently infers request batches from flattened message history. A standalone approval request that is abandoned before an unrelated user turn can therefore be combined with a later standalone Host-owned request. Supplying the Host result then spuriously requires an approval response for the abandoned request and raises before the provider is called.

Separating those batches also requires occurrence-safe response ownership: when standalone approval and Host requests reuse a call_id, a Host result must not consume a valid approval decision or satisfy an older mixed batch, exact Host occurrence identity must survive newer call-ID-only candidates, and a completed mixed batch must not reinterpret the local terminal result as another Host response.

This follow-up preserves fail-closed behavior for genuine mixed approval/Host batches while keeping standalone pauses from separate turns independent.

Description & Review Guide

  • What are the major changes? Treat a user message that does not answer the currently inferred stateless pause batch as a batch boundary; assign each stateless response in one linear pass to its nearest compatible request batch while prioritizing exact Host occurrence identity; match occurrence-identified Host results before assigning id-less results to unique unanswered occurrences; accept equivalent id-less Host duplicates only with authoritative session-backed provenance, while stateless ambiguous extras cannot restore approval authority; derive same-batch Host ownership from actual matched occurrences rather than result-message counts; retain Host-result identities for pending approval discovery while exposing only response-position-relevant exclusions to normalization; prevent approval wrappers from binding Host-owned calls and close excluded Host results against Host occurrences only; add regressions for abandoned approvals, response ordering, duplicate and ambiguous results with reused call IDs, completed approvals and mixed batches, historical/exact Host calls, valid normalized call/result pairing, and linear scan behavior; and clarify the function-calling loop specification and scenario mapping.
  • What is the impact of these changes? Later Host-owned continuations no longer fail because of an unrelated abandoned approval. Reused call IDs remain occurrence-scoped and response-order independent; exact Host results cannot be stolen by newer call-ID-only approvals; identified results reserve their occurrences before id-less matching; session-backed equivalent duplicates remain inert, while stateless ambiguous duplicates cannot replay approval authority; conflicting duplicates fail closed; historical completed Host calls cannot capture a later approval; and completed mixed approvals cannot re-execute on later turns. Caller-controlled standalone history no longer causes quadratic response-tail rescans. Genuine mixed batches still require every approval and Host response, retain model order, cannot be hidden by newer standalone requests, and remain inert after completion.
  • What do you want reviewers to focus on? The user-turn boundary and linear response ownership in _stateless_mixed_pause_batch_status, exact-before-id-less matching and provenance-gated duplicate handling in _match_mixed_pause_responses, and Host-only occurrence closure in _replace_approval_contents_with_results.

Related Issue

Fixes #8448

Partially addresses #8436 by fixing occurrence-identified/id-less Host assignment and preserving stateless conflict rejection. The provider-invalidation outbox recovery scenario remains a separate state-lifecycle change.

No other open pull request references either issue.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR is linked to an issue and there is no other open PR for this issue (see Related Issue above).
  • This is not a breaking change. If it is a breaking change, add the breaking change label (or add "[BREAKING]" to the title prefix, before or after any language prefix) — a workflow keeps the label and the title prefix in sync automatically.

Copilot AI balanced review requested due to automatic review settings September 17, 2026 08:54
@agent-framework-automation agent-framework-automation Bot added documentation Usage: [Issues, PRs], Target: documentation in the code base and learn docs python Usage: [Issues, PRs], Target: Python labels Sep 17, 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.

🔵 Needs a closer look

Later reused Host call IDs can satisfy an older mixed batch incorrectly, and significant AG-UI API changes are undocumented in the PR scope.

Pull request overview

Fixes stateless pause batching across user turns and extends approval-resume context handling.

Changes:

  • Adds user-turn boundaries while preserving unresolved mixed-batch detection.
  • Refactors pause-response correlation and streaming order handling.
  • Adds session-authority and AG-UI invocation-context support.
File summaries
File Description
python/packages/core/agent_framework/_tools.py Refactors mixed-pause discovery and session authority.
python/packages/core/agent_framework/_agents.py Marks framework-created sessions as non-authoritative.
python/packages/core/agent_framework/_harness/_tool_approval.py Preserves streamed mixed-pause ordering.
python/packages/core/tests/core/test_function_invocation_logic.py Adds stateless mixed-batch regressions.
python/packages/core/tests/core/test_harness_tool_approval.py Tests session authority and streaming order.
python/packages/ag-ui/agent_framework_ag_ui/_agent.py Exposes invocation kwargs through the wrapper.
python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Propagates and merges invocation kwargs.
python/packages/ag-ui/tests/ag_ui/test_approval_result_event.py Tests invocation-argument precedence.
python/packages/ag-ui/tests/ag_ui/test_agent_wrapper_comprehensive.py Tests context preservation across resume.
python/packages/ag-ui/AGENTS.md Documents AG-UI invocation context behavior.
docs/specs/004-python-function-calling-loop.md Updates mixed-pause invariants and test mapping.
Review details

Suppressed comments (1)

python/packages/core/agent_framework/_tools.py:3212

  • This unbounded response scan can bind a later occurrence's result to an older mixed batch. If an incomplete mixed batch has Host call ID x, and a later standalone Host request reuses x and returns a result without Content.id (a supported compatibility form), _match_mixed_pause_responses sees only the old batch's item and treats the later result as its missing response. The old approval can then execute even though that result belongs to the newer request. Account for intervening request occurrences—either exclude responses owned by later batches or treat call-ID-only matching as ambiguous when a later request reuses the ID.
        responses = [
            content
            for content in flattened_contents[batch_end + 1 :]
            if content.type in {"function_approval_response", "function_result"}
  • Files reviewed: 3/3 changed files
  • Comments generated: 0
  • Review effort level: Balanced (auto)

Note

Copilot is running an experiment and ran this review at Balanced.


💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 1

Result: Findings reported
Scope: full PR (1 commit(s)): 8cb5920f98cc
Model: gpt-5.6-sol-fast

Overview

The change introduces an explicit unrelated-user-turn boundary while retaining reverse discovery and fail-closed handling for genuine mixed approval/Host batches. The added tests cover distinct-call-ID separation and preserve non-user split-output behavior. One occurrence-correlation gap remains: separated standalone pauses that reuse a call_id can become response-order dependent and silently suppress a valid approval.

Reviewed the supplied pull-request change set across correctness, security/reliability, architecture, and failure behavior.
1 verified finding remained after source verification (1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_tools.py

Comment thread python/packages/core/agent_framework/_tools.py

@github-actions github-actions Bot 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.

MAF Automated Review — Iteration 2

Result: Findings reported
Scope: 3 net-new commit(s): a3fc29dbd63a, 57b204e22e48, 68c4c159952a
Model: gpt-5.6-sol-fast

Overview

The change adds user-turn boundaries and newest-to-oldest ownership for stateless pause responses, with focused regressions for reused call IDs, incomplete mixed batches, and completed-batch replay. The Purview changes also broaden content evaluation and make identity, execution-mode, and cached blocking behavior fail closed across Python and .NET. Two residual issues remain in the stateless ownership pass: compatible but unassignable responses can be reinterpreted by older batches, and scanning every standalone batch makes caller-controlled history processing quadratic.

Reviewed the supplied incremental change set across correctness, security/reliability, architecture, and failure behavior.
2 verified findings remained after source verification (1 high, 1 medium) across 1 file. Details are attached to the affected lines below.

Affected areas: python/packages/core/agent_framework/_tools.py

Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py
Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py Outdated
Comment thread python/packages/core/agent_framework/_tools.py Outdated
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 18, 2026
# Conflicts:
#	docs/specs/004-python-function-calling-loop.md
Merged via the queue into microsoft:main with commit 381e8e4 Sep 18, 2026
43 checks passed
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 python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: Stateless mixed-pause discovery merges requests across user turns

4 participants