Skip to content

[agentserver] Support container protocol v2.0.0 (per-request call ID)#47584

Open
vangarp wants to merge 11 commits into
mainfrom
vangarp/agentserver-core-2.0.0-support
Open

[agentserver] Support container protocol v2.0.0 (per-request call ID)#47584
vangarp wants to merge 11 commits into
mainfrom
vangarp/agentserver-core-2.0.0-support

Conversation

@vangarp

@vangarp vangarp commented Jun 20, 2026

Copy link
Copy Markdown
Member

Summary

Implements container spec v2.0.0 (per-request call ID delivery, deprecating v1.0.0) across the sdk/agentserver packages, per the foundry container spec.

Wire-contract changes

  • x-agent-user-isolation-keyx-agent-user-id (global, cross-agent per-user partition key)
  • x-agent-chat-isolation-keyremoved (conversation scoping uses the existing conversation_id)
  • New x-agent-foundry-call-id — opaque per-request call ID (protocol 2.0.0)
  • New FOUNDRY_AGENT_ID env var (stable agent GUID)
  • The container/SDK now forwards x-agent-user-id + x-agent-foundry-call-id on 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() from FOUNDRY_AGENT_ID.
  • New request-scoped platform context: RequestContext, get_request_context(), set_request_context(), reset_request_context().

azure-ai-agentserver-invocations → 1.0.0b6

  • Reads the new inbound headers, binds them to the request context (incl. streaming), exposes request.state.user_id / request.state.call_id.

azure-ai-agentserver-responses → 1.0.0b8

  • Public IsolationContextPlatformContext (user_id_key / call_id); ResponseContext.isolationplatform_context.
  • Provider protocol methods use context= (was isolation=); in-process partition enforcement now keyed on the user ID.
  • Foundry Storage provider forwards both headers outbound; presence-logging flags renamed.

azure-ai-agentserver-ghcopilot → 1.0.0b3

  • Toolbox/MCP bridge forwards the platform identity headers on outbound Foundry toolbox calls (initialize, tools/list, tools/call).
  • Bumped core/responses dependency floors.

Not affected

  • azure-ai-agentserver-optimization — startup-time config resolver against a job-scoped endpoint; no inbound call ID to forward.

Tests

  • Updated all affected unit/contract tests; renamed test_chat_isolation_enforcement.pytest_user_isolation_enforcement.py; added new tests for RequestContext, agent_guid, and the toolbox header forwarding.
  • Local runs: core 106, invocations 188, responses 917 (unit/contract/interop), ghcopilot toolbox 33 — all passing.

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

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>
@github-actions github-actions Bot added the Hosted Agents sdk/agentserver/* label Jun 20, 2026
vangarp and others added 5 commits June 20, 2026 12:40
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>
@vangarp vangarp marked this pull request as ready for review June 21, 2026 04:28
Copilot AI review requested due to automatic review settings June 21, 2026 04:28
….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>

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

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), adds AgentConfig.agent_guid / resolve_agent_guid(), and a new request-scoped context (FoundryAgentRequestContext with get/set/reset_request_context).
  • Responses: renames the public IsolationContextPlatformContext, switches provider protocol methods from isolation= to context=, 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 IsolationContextPlatformContext, 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 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

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

@vangarp

vangarp commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@copilot review and resolve any outdated comments

…P, not user ID

Co-authored-by: vangarp <28958073+vangarp@users.noreply.github.com>

Copilot AI commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

@copilot review and resolve any outdated comments

Reviewed all three open comments:

  1. _endpoint_handler.py:662-668 — Already resolved in commit ffa6e04: the _iter_with_context() async generator now re-establishes the platform context inside the StreamingResponse body iterator, mirroring what invocations does in _wrap_streaming_response.

  2. _copilot_adapter.py:424-428 — Also resolved by ffa6e04: since _handle_create runs inside _iter_with_context(), get_request_context() is valid when the generator body executes.

  3. _toolbox.py:224-226 — Fixed in this commit: clarified that only x-agent-foundry-call-id is forwarded to 1P services; x-agent-user-id is never forwarded (it is used only for container-side per-user partitioning).

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

Copilot reviewed 50 out of 50 changed files in this pull request and generated 2 comments.

Comment thread sdk/agentserver/azure-ai-agentserver-core/CHANGELOG.md Outdated
@vangarp

vangarp commented Jun 21, 2026

Copy link
Copy Markdown
Member Author

@copilot apply fixes for the findings

…rom core in _toolbox

Co-authored-by: vangarp <28958073+vangarp@users.noreply.github.com>

Copilot AI commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

@copilot apply fixes for the findings

Both findings fixed:

  1. CHANGELOG.md:9 — corrected RequestContext.platform_headers()FoundryAgentRequestContext.platform_headers().
  2. _toolbox.py:40 — removed the local _FOUNDRY_CALL_ID_HEADER = "x-agent-foundry-call-id" literal; added from azure.ai.agentserver.core._platform_headers import FOUNDRY_CALL_ID and updated the two usage sites accordingly.

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

Copilot reviewed 50 out of 50 changed files in this pull request and generated 1 comment.

Co-authored-by: vangarp <28958073+vangarp@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Hosted Agents sdk/agentserver/*

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants