Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions python/packages/ag-ui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,12 @@ credential or tenant boundary. Production applications must authenticate and aut
and choose a Snapshot Scope that represents the app's real access boundary, such as an authenticated user, tenant,
or workspace. Do not rely on untrusted client-provided fields by themselves to choose that boundary.

Tool approval resumes are validated against server-owned Approval State. The default Approval State store is
process-local and bounded, and stores only approval-specific state needed to validate and continue pending approvals.
It is not an authentication, tenant authorization, or distributed durability mechanism; production applications remain
responsible for endpoint authentication, tenant authorization, and deployment/storage architecture that matches their
availability and worker topology requirements.

Stored snapshots are untrusted application data with confidentiality impact. They may contain sensitive user text,
model output, tool results, function arguments, UI payloads, Shared State, and interrupt data. The built-in
`InMemoryAGUIThreadSnapshotStore` is in-memory only, process-local, bounded, latest-only, and not durable production
Expand Down
22 changes: 13 additions & 9 deletions python/packages/ag-ui/agent_framework_ag_ui/_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

"""AgentFrameworkAgent wrapper for AG-UI protocol."""

from collections import OrderedDict
from collections.abc import AsyncGenerator
from typing import Any, cast

from ag_ui.core import BaseEvent
from agent_framework import SupportsAgentRun

from ._agent_run import PendingApprovalEntry, PendingApprovalKey, run_agent_stream
from ._approval_state import InMemoryAGUIApprovalStateStore
from ._snapshots import AGUIThreadSnapshotStore


Expand Down Expand Up @@ -111,13 +111,13 @@ def __init__(
snapshot_store=snapshot_store,
)

# Server-side registry of pending approval requests.
# Keys are (thread_id, request_id), values are the function name.
# Populated when approval requests are emitted; consumed when responses arrive.
# Prevents bypass, function name spoofing, and replay attacks.
# Bounded to prevent unbounded growth from abandoned approval requests.
self._pending_approvals: OrderedDict[PendingApprovalKey, PendingApprovalEntry] = OrderedDict()
self._pending_approvals_max_size: int = 10_000
# Server-side Approval State. Populated when approval requests are emitted
# and consumed when resume decisions arrive.
self._approval_state_store = InMemoryAGUIApprovalStateStore()
self._pending_approvals = cast(
dict[PendingApprovalKey, PendingApprovalEntry],
self._approval_state_store.pending_approvals,
)

@property
def snapshot_store(self) -> AGUIThreadSnapshotStore | None:
Expand All @@ -137,6 +137,10 @@ async def run(
AG-UI events
"""
async for event in run_agent_stream(
input_data, self.agent, self.config, pending_approvals=self._pending_approvals
input_data,
self.agent,
self.config,
pending_approvals=self._pending_approvals,
approval_state_store=self._approval_state_store,
):
yield event
Loading
Loading