[agentserver] Support container protocol v2.0.0 (per-request call ID)#47584
[agentserver] Support container protocol v2.0.0 (per-request call ID)#47584vangarp wants to merge 11 commits into
Conversation
Implements container spec v2.0.0 across the agentserver packages: - Replace the `x-agent-user-isolation-key` / `x-agent-chat-isolation-key` headers with `x-agent-user-id` (global per-user key) and `x-agent-foundry-call-id` (opaque per-request call ID). - Add `FOUNDRY_AGENT_ID` env var support (`AgentConfig.agent_guid`). - Add a request-scoped platform context in core (`RequestContext`, `get_request_context`/`set_request_context`/`reset_request_context`) so the per-request call ID / user ID can be forwarded on outbound Foundry 1P calls and read by handler/tool code. - invocations + responses read the new inbound headers, bind them to the request context, and forward them on outbound Foundry Storage calls. - Rename the public `IsolationContext` -> `PlatformContext` (`user_id_key` / `call_id`); provider methods take `context=` instead of `isolation=`; in-process partition enforcement is now keyed on the user ID. - ghcopilot: the toolbox/MCP bridge forwards the platform identity headers on outbound Foundry toolbox calls (initialize, tools/list, tools/call). Packages bumped: core 2.0.0b7, responses 1.0.0b8, invocations 1.0.0b6, ghcopilot 1.0.0b3. The optimization package is unaffected (startup-time resolver, no inbound call ID). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
responses and invocations import RequestContext / get_request_context from azure-ai-agentserver-core, which were introduced in core 2.0.0b7. The previous floor (>=2.0.0b4) let isolated build/doc venvs resolve an older published core lacking those symbols, breaking the Sphinx import. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Per review: 1P services accept/trust only x-agent-foundry-call-id on inbound. x-agent-user-id is not accepted/trusted by 1P, so it must not be forwarded outbound — it remains an inbound-only header used for container-side per-user state partitioning. - RequestContext.platform_headers() now emits only x-agent-foundry-call-id. - Foundry Storage provider forwards only the call ID. - Storage logging policy logs only call-id presence. - ghcopilot toolbox bridge forwards only the call ID (via platform_headers()). - Updated tests, CHANGELOGs. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…-> FoundryAgentRequestContext - ghcopilot: echo the per-request x-agent-foundry-call-id on outbound toolbox tools/call. Because the Copilot engine dispatches tools on a separate reader task where the request context var is empty, the per-turn call id is captured in the request task and resolved out-of-band keyed by the Copilot session_id, then stamped as both the HTTP header and params._meta. Copilot sessions are bound to a single user (x-agent-user-id) and never reused across users. - Rename the ambient request context type RequestContext -> FoundryAgentRequestContext (get_request_context() unchanged) to avoid the generic name and to match the .NET type; Azure.RequestContext exists in Azure.Core on the .NET side. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ix spelling - Add toolbox-centric "multi-user session" examples to the core, responses, invocations, and ghcopilot READMEs: forward the per-request x-agent-foundry-call-id on outbound toolbox calls so a tool server resolves the caller per request on a shared session (x-agent-user-id is never echoed). - Document request.state.user_id / request.state.call_id in invocations. - Fix cspell errors: rename test session ids "sess-*" -> "session-*". Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
….0b7 floor) These packages now require azure-ai-agentserver-core>=2.0.0b7 for the new FoundryAgentRequestContext / PlatformContext APIs. Until core 2.0.0b7 is published, the minimum-dependency check cannot resolve a satisfying core version, so disable it here (matching the azure-ai-agentserver-ghcopilot sibling precedent). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR implements the foundry container protocol v2.0.0 (per-request call ID) across the four sdk/agentserver packages, deprecating v1.0.0. The wire contract changes from isolation-key headers (x-agent-user-isolation-key / x-agent-chat-isolation-key) to a global x-agent-user-id plus a new opaque per-request x-agent-foundry-call-id, adds a FOUNDRY_AGENT_ID env var, and introduces a request-scoped platform context that is forwarded on outbound calls to Foundry 1P services (Storage, Toolbox/MCP).
Changes:
- Core: renames platform-header constants (
USER_ID,FOUNDRY_CALL_ID), addsAgentConfig.agent_guid/resolve_agent_guid(), and a new request-scoped context (FoundryAgentRequestContextwithget/set/reset_request_context). - Responses: renames the public
IsolationContext→PlatformContext, switches provider protocol methods fromisolation=tocontext=, keys in-process partition enforcement on the user ID, and forwards the call ID outbound (user ID is not forwarded to 1P). - Invocations + ghcopilot: bind inbound headers to the request context (invocations re-establishes it for streaming via
_wrap_streaming_response) and forward the platform identity headers on outbound toolbox calls; dependency floors and versions bumped, CHANGELOGs/tests updated.
The rename is applied cleanly (no leftover references), version bumps and CHANGELOG/Breaking-Changes entries are consistent, and the preview status is correctly reflected. However, I found a critical correctness gap: the responses host resets the request context in handle_create's finally before the StreamingResponse body iterates, so get_request_context() is empty inside streaming handlers — which silently disables call-ID forwarding (and the §8.4 owner check) for streaming turns, the very behavior this PR adds. Invocations avoids this via _wrap_streaming_response, but responses has no equivalent.
Reviewed changes
Copilot reviewed 49 out of 49 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
azure-ai-agentserver-responses/.../hosting/_endpoint_handler.py |
Binds platform context but resets it in finally before streaming body iterates (root cause of the streaming gap). |
azure-ai-agentserver-ghcopilot/.../_copilot_adapter.py |
Reads get_request_context().call_id/.user_id inside an async-generator handler → empty under streaming, so call ID is never bound and owner never recorded. |
azure-ai-agentserver-invocations/.../_invocation.py |
Correctly re-establishes platform context for the streaming body in _wrap_streaming_response (reference pattern). |
azure-ai-agentserver-responses/.../hosting/_orchestrator.py |
Mechanical isolation=→context= and user_id_key=ctx.user_id renames; invokes handler lazily during stream iteration. |
azure-ai-agentserver-ghcopilot/.../_toolbox.py |
Session-keyed call-id forwarding on initialize/tools/list/tools/call; correct in isolation but depends on the call ID being bound. |
azure-ai-agentserver-core/.../_request_context.py |
New FoundryAgentRequestContext + get/set/reset helpers (forwards only call_id outbound). |
azure-ai-agentserver-core/.../_platform_headers.py |
Renamed header constants (USER_ID, FOUNDRY_CALL_ID); removed isolation-key constants. |
azure-ai-agentserver-core/.../_config.py |
Adds agent_guid (keyword-only) and resolve_agent_guid() from FOUNDRY_AGENT_ID. |
.../tests/unit/test_platform_headers.py |
Updates constant assertions to the new header names. |
.../tests/unit/test_foundry_storage_provider.py |
Asserts user_id is not forwarded to 1P and call_id is; adds call-id-alone test. |
.../tests/unit/test_foundry_logging_policy.py |
Presence-logging assertions renamed to has_call_id. |
.../tests/unit/test_response_context_input_items.py, test_in_memory_provider_crud.py |
IsolationContext→PlatformContext, isolation=→context= renames. |
.../tests/contract/test_user_isolation_enforcement.py |
Renamed contract test; enforcement now keyed on x-agent-user-id. |
CHANGELOG.md / _version.py (4 packages) |
Version bumps and Breaking-Changes entries for the header/type renames. |
For streaming requests the registered handler runs lazily while Starlette iterates the StreamingResponse body, which happens after handle_create has returned and its finally block already called reset_request_context(). As a result get_request_context() returned the empty context inside any streaming handler, so the per-request x-agent-foundry-call-id / x-agent-user-id could not be forwarded on raw outbound 1P calls (protocol 2.0.0). This also broke the ghcopilot adapter, whose create handler is a streaming async generator that reads get_request_context().call_id / .user_id. Re-establish the platform context inside the streaming body iterator (for both background and non-background streams), mirroring the invocations package's _wrap_streaming_response. Added a regression test that fails without the fix. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot review and resolve any outdated comments |
…P, not user ID Co-authored-by: vangarp <28958073+vangarp@users.noreply.github.com>
Reviewed all three open comments:
|
|
@copilot apply fixes for the findings |
…rom core in _toolbox Co-authored-by: vangarp <28958073+vangarp@users.noreply.github.com>
Both findings fixed:
|
Co-authored-by: vangarp <28958073+vangarp@users.noreply.github.com>
Summary
Implements container spec v2.0.0 (per-request call ID delivery, deprecating v1.0.0) across the
sdk/agentserverpackages, per the foundry container spec.Wire-contract changes
x-agent-user-isolation-key→x-agent-user-id(global, cross-agent per-user partition key)x-agent-chat-isolation-key→ removed (conversation scoping uses the existingconversation_id)x-agent-foundry-call-id— opaque per-request call ID (protocol2.0.0)FOUNDRY_AGENT_IDenv var (stable agent GUID)x-agent-user-id+x-agent-foundry-call-idon outbound calls to Foundry 1P services (Storage, Toolbox/MCP).Changes by package
azure-ai-agentserver-core→ 2.0.0b7_platform_headers:USER_ID(x-agent-user-id),FOUNDRY_CALL_ID(x-agent-foundry-call-id); removed the isolation-key constants.AgentConfig.agent_guid+resolve_agent_guid()fromFOUNDRY_AGENT_ID.RequestContext,get_request_context(),set_request_context(),reset_request_context().azure-ai-agentserver-invocations→ 1.0.0b6request.state.user_id/request.state.call_id.azure-ai-agentserver-responses→ 1.0.0b8IsolationContext→PlatformContext(user_id_key/call_id);ResponseContext.isolation→platform_context.context=(wasisolation=); in-process partition enforcement now keyed on the user ID.azure-ai-agentserver-ghcopilot→ 1.0.0b3initialize,tools/list,tools/call).Not affected
azure-ai-agentserver-optimization— startup-time config resolver against a job-scoped endpoint; no inbound call ID to forward.Tests
test_chat_isolation_enforcement.py→test_user_isolation_enforcement.py; added new tests forRequestContext,agent_guid, and the toolbox header forwarding.Note
These packages are in preview; the header/type renames are noted under Breaking Changes in the CHANGELOGs since the isolation-key surface shipped in prior betas.
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com