Skip to content

Python: Harden AG-UI approval lifecycle and resume semantics - #7594

Open
Evan Mattson (moonbox3) wants to merge 14 commits into
microsoft:mainfrom
moonbox3:ag-ui-approval-gated-tool-calls
Open

Python: Harden AG-UI approval lifecycle and resume semantics#7594
Evan Mattson (moonbox3) wants to merge 14 commits into
microsoft:mainfrom
moonbox3:ag-ui-approval-gated-tool-calls

Conversation

@moonbox3

@moonbox3 Evan Mattson (moonbox3) commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Motivation & Context

AG-UI approval-gated tool calls need occurrence-scoped authority across interruption, resume, execution, retry, and snapshot restoration. The previous runner-owned paths could leave approvals claimed or executing when tools disappeared or streams ended unexpectedly, confuse repeated call identifiers, and diverge from current AG-UI resume semantics.

This change centralizes approval lifecycle ownership so each pending call has one authoritative transition path while retaining the existing MAF endpoint and response compatibility.

Description & Review Guide

  • What are the major changes?
    • Introduces a typed, occurrence-aware approval lifecycle covering registration, claims, execution ownership, recovery, terminal outcomes, replay deduplication, and bounded retention.
    • Routes local, hosted, and deferred approvals through explicit transition owners and safely recovers unavailable or interrupted executions.
    • Reconciles stored interrupt snapshots with lifecycle state and preserves multi-call batch continuity without duplicating side effects.
    • Aligns standalone and workflow-contained agent approvals with current AG-UI semantics: both emit canonical tool-approval interrupts and accept standard approved with full-replacement editedArgs.
    • Preserves legacy MAF accepted and direct partial edits, keeps hosted approvals decision-only, and clears workflow-agent pending state when an approval is cancelled.
    • Keeps the existing MAF accepted alias and direct partial argument edits for compatibility; no exported API surface changes.
    • Extends endpoint, lifecycle, state, replay, and workflow regression coverage and updates the function-calling scenario specification.
  • What is the impact of these changes?
    • Approval retries and reconnects are deterministic and fail closed when execution outcome is uncertain.
    • Local tools are not executed without lifecycle authority, and disappearing executors no longer strand claims indefinitely.
    • Existing MAF clients continue to work while standards-conforming AG-UI clients can use canonical approval payloads.
  • What do you want reviewers to focus on?
    • Lifecycle transition ownership and recovery behavior around stream cancellation or unavailable executors.
    • Occurrence identity and replay behavior for batches containing duplicate call IDs.
    • Backward compatibility of the additive AG-UI approval schema and cancellation behavior.

Related Issue

N/A — approval lifecycle hardening identified through local adversarial review.

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 title prefix in sync automatically.

Key decisions:
- Add an internal typed approval lifecycle with pending, claimed, executing, and settled states.
- Keep authorization separate from execution; only LocalPendingToolTransitionOwner invokes approved local calls.
- Register server-owned occurrences before canonical ResumeDecision claims and retain one replayable result under the original call identity.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_approval_state.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_approval_result_event.py
- packages/ag-ui/tests/ag_ui/test_agent_wrapper_comprehensive.py

Verification:
- 952 AG-UI tests passed.
- Focused lifecycle/public tracer passed with warnings treated as errors.
- Ruff format/check and AG-UI Pyright passed.
- git diff --check passed.

Notes for next iteration:
- The function-calling-loop scenario mapping is inaccessible under the organization content-exclusion policy and could not be updated.
- The workspace Poe package fan-out is blocked by the pre-existing missing packages/durabletask/pyproject.toml; equivalent package-local checks were run.
Key decisions:
- Give each local approval a scoped logical occurrence identity and share one occurrence across trusted thread aliases.
- Validate complete Resume Decision batches before applying claims, then account for accepted, rejected, and cancelled occurrences independently.
- Preserve sibling authority and original result identity across failures, mixed decisions, and reused raw call IDs.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_approval_state.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_agent_wrapper_comprehensive.py

Verification:
- 959 AG-UI tests passed with 90% lifecycle branch coverage.
- 30 focused lifecycle/public tracer tests passed with warnings treated as errors.
- Ruff format/check and AG-UI Pyright passed.
- git diff --check passed.

Notes for next iteration:
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
- Workspace typing fan-out remains blocked by the pre-existing missing packages/durabletask/pyproject.toml; package-local Pyright passed, while package-local MyPy retains three unrelated baseline errors.
Key decisions:
- Retain terminal decisions and outcomes by scoped occurrence so identical accepted and rejected retries reproject results without granting execution authority again.
- Reject conflicting names, arguments, decisions, wrong-scope lookups, and expired authority before an execution intent can reach the local transition owner.
- Keep protocol normalization in the runner while using server-owned lifecycle context to canonicalize retries and preserve existing AG-UI wire aliases.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_agent_wrapper_comprehensive.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- 965 AG-UI tests passed with 90% approval lifecycle coverage.
- 18 focused lifecycle, hostile-resume, wrong-thread, and endpoint retry tests passed with runtime and deprecation warnings treated as errors.
- Ruff format/check and AG-UI package-local Pyright passed.
- git diff --check passed.

Notes for next iteration:
- Terminal retention is process-local and unbounded until the later bounded-retention issue adds its explicit policy.
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
Key decisions:
- Carry explicit local, hosted, deferred in-run, or unavailable ownership on every approval occurrence and authorized intent.
- Keep lifecycle authorization separate from execution; local calls execute only through the local adapter while hosted and setup-injected decisions forward through owner-specific adapters.
- Leave declaration-only calls pending when no transition owner can act, and settle forwarded outcomes against the original occurrence without local fallback.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_approval_state.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- 967 AG-UI tests passed with 92% package coverage and 89% approval lifecycle coverage.
- 94 focused lifecycle, hosted, deferred-owner, hostile-resume, and approval tests passed.
- Ruff format/check and package-local Pyright passed.
- git diff --check passed.

Notes for next iteration:
- Executing-without-outcome recovery remains for the indeterminate execution-window issue.
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
- Workspace Poe fan-out remains blocked by the pre-existing missing packages/durabletask/pyproject.toml; equivalent package-local checks passed.
Key decisions:
- Distinguish reserved claims from execution windows that may have started an external side effect.
- Recover non-idempotent execution failures as indeterminate and reject identical retries without another invocation.
- Permit claim release only under an explicit safe policy and execution retry only with a predeclared idempotency key shared by local and forwarded owners.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- 972 AG-UI tests passed with 92% line coverage and 89% package branch coverage.
- 23 focused lifecycle, duplicate-resume, hosted-owner, and public settlement-window tests passed.
- Package-local Ruff and Pyright passed; git diff --check passed.

Notes for next iteration:
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
- Workspace Poe fan-out remains blocked by the pre-existing missing packages/durabletask/pyproject.toml; equivalent package-local checks passed.
Key decisions:
- Keep Approval State authoritative and emit typed snapshot reconciliation keyed by logical occurrence identity.
- Retire settled, rejected, cancelled, expired, indeterminate, and missing controls while preserving nonterminal authority.
- Reconcile stale snapshots before hydration or resume, and retain lifecycle deduplication when snapshot saves fail.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/agent_framework_ag_ui/_snapshot_session.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py

Verification:
- 975 AG-UI tests passed with 92% package coverage and 89% approval lifecycle coverage.
- Package-local Ruff and Pyright passed; git diff --check passed.

Notes for next iteration:
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
- Workspace Poe fan-out remains blocked by the pre-existing missing packages/durabletask/pyproject.toml; equivalent package-local checks passed.
Key decisions:
- Protect pending, claimed, executing, and indeterminate occurrences from eviction while retaining terminal outcomes for a configurable 15-minute process-local deduplication window.
- Serialize complete approval batches by logical occurrence locks so aliases share atomic decisions and independent batches can progress concurrently.
- Fail capacity, claim, and settlement conflicts explicitly, and emit redacted structured lifecycle telemetry without tool names, arguments, or approval payloads.
- Remove legacy LRU eviction paths so active Approval State and middleware state are never silently discarded.

Files changed:
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_approval_state.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_approval_state.py

Verification:
- 982 AG-UI tests passed with 92% package coverage and 91% approval lifecycle coverage.
- 34 focused lifecycle and storage tests passed with RuntimeWarning and DeprecationWarning treated as errors.
- Package-local Ruff and Pyright passed; git diff --check passed.

Notes for next iteration:
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
- Workspace Poe fan-out remains blocked by the pre-existing missing packages/durabletask/pyproject.toml; equivalent package-local checks passed.
Key decisions:
- Make ApprovalLifecycle the sole owner of trusted aliases, occurrence metadata, authority transitions, and retained outcomes.
- Remove the parallel mutable pending-approval registry and route local, hosted, deferred, cancellation, replay, and snapshot reconciliation through lifecycle occurrences.
- Encapsulate middleware Approval State behind copy-isolated store methods while keeping AG-UI protocol normalization and event projection in the runner.

Files changed:
- packages/ag-ui/AGENTS.md
- packages/ag-ui/agent_framework_ag_ui/_agent.py
- packages/ag-ui/agent_framework_ag_ui/_agent_run.py
- packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
- packages/ag-ui/agent_framework_ag_ui/_approval_state.py
- packages/ag-ui/tests/ag_ui/test_agent_wrapper_comprehensive.py
- packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
- packages/ag-ui/tests/ag_ui/test_approval_result_event.py
- packages/ag-ui/tests/ag_ui/test_approval_state.py
- packages/ag-ui/tests/ag_ui/test_endpoint.py
- packages/ag-ui/tests/ag_ui/test_run.py

Verification:
- 964 package-local AG-UI tests passed with 92% coverage and 90% approval lifecycle coverage.
- 85 warning-strict focused approval tests passed.
- Package-local Ruff and Pyright passed; git diff --check passed.

Notes for next iteration:
- The function-calling-loop scenario mapping remains inaccessible under the organization content-exclusion policy.
- Workspace Poe fan-out remains blocked by the pre-existing missing packages/durabletask/pyproject.toml; equivalent package-local checks passed.
Copilot AI balanced review requested due to automatic review settings August 10, 2026 10:05
@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 Aug 10, 2026
@github-actions

github-actions Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Python Test Coverage

Python Test Coverage Report •
FileStmtsMissCoverMissing
packages/ag-ui/agent_framework_ag_ui
   _agent.py46295%73, 129
   _agent_run.py139914589%158, 218, 237, 284, 302, 318–319, 441, 494–495, 502, 611, 615, 617, 634, 661–662, 716, 745, 759, 763, 767, 770, 775, 780, 789, 792, 799–805, 816, 830, 837, 847, 862, 865, 897, 932, 956, 968–972, 1018, 1033, 1036, 1038, 1064, 1066, 1068, 1071–1075, 1099, 1130, 1166, 1221–1222, 1264, 1278, 1323–1329, 1338, 1346, 1374, 1396, 1400, 1403, 1405, 1465–1466, 1472–1478, 1575, 1579–1580, 1583, 1588–1589, 1592, 1598–1599, 1603–1605, 1623–1625, 1650–1651, 1674–1675, 1688, 1730–1732, 1743–1745, 1747, 1761–1763, 1891, 1899, 1929, 1933, 1978, 2180, 2332, 2334, 2341, 2357, 2416, 2433, 2502, 2645, 2683, 2691, 2693, 2696, 2702, 2757, 2760, 2770–2771, 2778, 2825
   _approval_lifecycle.py5585091%39, 51, 70, 249, 251, 253, 255, 285, 319, 321, 326, 334, 339, 349–355, 472, 475, 491, 519, 528, 538, 562, 636, 647, 650, 681, 686, 793, 802, 804, 818, 822, 838, 842, 854, 863, 888, 890, 894, 909, 928, 930, 1001–1003
   _approval_state.py450100% 
   _run_common.py6223993%100, 175–176, 178, 180, 183, 189, 191, 205, 212, 236–237, 250, 269, 292–294, 305, 349, 361, 369, 372, 565, 845–846, 1170–1171, 1176, 1178–1180, 1189, 1197, 1209, 1211–1214, 1277
   _snapshot_session.py77198%40
   _workflow_run.py7749288%93, 107, 109, 111, 114, 180–181, 185, 286–289, 333, 344, 349, 380, 416–419, 447, 452, 468, 470, 475–479, 489, 496, 508, 513, 520, 553, 563, 574, 579, 582, 604, 628, 631, 636, 639, 656–658, 663, 665, 671, 675, 677, 694, 700–701, 711, 717, 721–722, 746–747, 785, 793, 862, 882, 898, 913, 997, 1019–1021, 1056–1070, 1102–1103, 1137–1138, 1213, 1275
TOTAL46591433990% 

Python Unit Test Overview

Tests Skipped Failures Errors Time
9395 36 💤 0 ❌ 0 🔥 2m 36s ⏱️

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.

Pull request overview

Centralizes AG-UI approval authority in an occurrence-aware lifecycle while updating resume, cancellation, replay, and snapshot behavior.

Changes:

  • Adds lifecycle ownership, retention, recovery, and replay deduplication.
  • Supports canonical approved/editedArgs payloads and normal cancellation.
  • Expands regression coverage and documentation.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
python/packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py Adds approval lifecycle state machine.
python/packages/ag-ui/agent_framework_ag_ui/_approval_state.py Integrates lifecycle-backed state storage.
python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Routes approval execution and replay through lifecycle.
python/packages/ag-ui/agent_framework_ag_ui/_agent.py Removes the legacy approval registry.
python/packages/ag-ui/agent_framework_ag_ui/_run_common.py Extends approval response schemas.
python/packages/ag-ui/agent_framework_ag_ui/_workflow_run.py Makes workflow cancellation terminal and normal.
python/packages/ag-ui/agent_framework_ag_ui/_snapshot_session.py Reconciles in-memory snapshot interrupts.
python/packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py Adds lifecycle behavior and concurrency tests.
python/packages/ag-ui/tests/ag_ui/test_approval_state.py Tests bounded state behavior.
python/packages/ag-ui/tests/ag_ui/test_approval_result_event.py Refactors result projection tests.
python/packages/ag-ui/tests/ag_ui/test_endpoint.py Expands endpoint lifecycle regressions.
python/packages/ag-ui/tests/ag_ui/test_run.py Updates runner tests for lifecycle state.
python/packages/ag-ui/tests/ag_ui/test_agent_wrapper_comprehensive.py Updates wrapper approval assertions.
python/packages/ag-ui/README.md Documents canonical approval payloads.
python/packages/ag-ui/AGENTS.md Records lifecycle ownership conventions.
docs/specs/004-python-function-calling-loop.md Updates function-loop approval semantics.
Suppressed comments (1)

python/packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py:405

  • This second iteration has the same unlocked-read race: concurrent registration/purge can mutate _occurrences while the set comprehension runs, causing a runtime error or an inconsistent pending-ID snapshot. Keep the purge and iteration under _index_lock.
        self._purge_expired_terminal()
        return {
            occurrence.identity.interrupt_id
            for occurrence in self._occurrences.values()
            if thread_id in occurrence.thread_ids and occurrence.status is ApprovalStatus.PENDING
        }

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

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread docs/specs/004-python-function-calling-loop.md Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_approval_state.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py Outdated

@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.

Agent Framework Review — Iteration 1

Completed passes: 5 | Result: Findings reported

Scope: full PR (9 commit(s)): 06daf9586c71, 26ef56d71571, f24f57a45253, 5c21854ec475, de6fce99d656, d7fcd066ca41, 42b3d05b47ef, 31c483e65fc6, 99feeaf529c0

Review passes

  • Correctness (gpt-5.6-sol) — The unavailable-local-tool recovery path can strand sibling hosted or deferred approvals in the claimed state, breaking safe retry and batch continuity. Two medium-severity correctness issues were found: reserved approval fields can be overwritten by colliding tool argument names, and lifecycle dictionary iteration is vulnerable to concurrent mutation.
  • Security Reliability (claude-opus-4.8) — No issues found in this pass.
  • Test Coverage (gpt-5.6-sol) — Two regressions remain uncovered: duplicate call IDs within a single approval batch can cause incorrect settlement, and tool arguments named approved can corrupt the approval response schema.
  • Failure Modes (claude-opus-4.8) — No issues found in this pass.
  • Design Approach (claude-opus-4.8) — No issues found in this pass.

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py
Comment thread python/packages/ag-ui/tests/ag_ui/test_approval_lifecycle.py
@moonbox3
Evan Mattson (moonbox3) marked this pull request as ready for review August 12, 2026 02:21

@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.

Automated Code Review

Reviewers: 1

✗ Deep Review

Deep review found 3 verified inline finding(s).


Automated review by moonbox3's agents

Comment thread python/packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py Outdated
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_approval_lifecycle.py
Comment thread python/packages/ag-ui/agent_framework_ag_ui/_agent_run.py
…d-tool-calls

# Conflicts:
#	python/packages/ag-ui/tests/ag_ui/test_endpoint.py
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.

4 participants