* .NET: Add Hosted-MemoryAgent sample with isolation key plumbing (#5692) Adds HostedSessionContext + HostedSessionIsolationKeyProvider in Microsoft.Agents.AI.Foundry.Hosting so AIContextProviders (notably FoundryMemoryProvider) can scope per user via the platform's x-agent-user-isolation-key / x-agent-chat-isolation-key headers. - New types: HostedSessionContext (sealed), HostedSessionContextExtensions (public Get, internal Set), abstract HostedSessionIsolationKeyProvider (async), internal PlatformHostedSessionIsolationKeyProvider mapping ResponseContext.Isolation. - AgentFrameworkResponseHandler now resolves the provider, tags fresh sessions, and validates resumed sessions against the live request (strict 403 'Hosted session identity context mismatch' on any mismatch; 500 on null keys). - New shared sample project Hosted_Shared_Contributor_Setup hosts DevTemporaryTokenCredential and DevTemporaryLocalSessionIsolationKeyProvider plus AddDevTemporaryLocalContributorSetup. All 9 existing responses samples migrated to consume it so local runs keep working under the strict isolation contract. - New Hosted-MemoryAgent sample: travel assistant wired through FoundryMemoryProvider with stateInitializer reading session.GetHostedContext().UserId. Includes Dockerfile, smoke.ps1, agent.yaml/manifest. - New IT scenario 'memory' in Foundry.Hosting.IntegrationTests + MemoryHostedAgentFixture + MemoryHostedAgentTests. Verified end to end against the tao Foundry project. - ADR 0026 captures the design tree. * Address PR review feedback - Dockerfile: add header noting it targets NuGet builds; contributors must use Dockerfile.contributor for ProjectReference source builds. - PlatformHostedSessionIsolationKeyProvider: doc said 'returns context with empty values'; corrected to 'returns null' which the handler treats as 500. - FakeHostedSessionIsolationKeyProvider: doc clarifies that null configurations are allowed for testing the handler error path. - HostedSessionContextExtensions.SetHostedContext: enforce write-once with InvalidOperationException; doc + xml exception updated. - AgentFrameworkResponseHandler: cache PlatformHostedSessionIsolationKeyProvider as static readonly to avoid per-request allocation. - MemoryHostedAgentTests: tighten waits from 20s to 5s (FoundryMemoryProvider defaults UpdateDelay=0; ingestion ~3s). - Sample Program.cs imports reordered to satisfy IDE0005. * Add HostedFoundryMemoryProviderScopes built-in helpers (#5692) Addresses review feedback from @lokitoth on Hosted-MemoryAgent/Program.cs:54. - New HostedFoundryMemoryProviderScopes static class with PerUser, PerChat, PerUserAndChat factories returning Func<AgentSession?, FoundryMemoryProvider.State>. - All helpers throw InvalidOperationException when GetHostedContext() is null, with a message pointing at writing a custom stateInitializer for non-hosted scenarios. - New HostedFoundryMemoryScope enum and AddHostedFoundryMemoryProvider DI extension (two overloads: explicit AIProjectClient and DI-resolved). Singleton lifetime. Default scope = PerUser. - Hosted-MemoryAgent sample and the memory IT scenario container both swap their inline lambdas for HostedFoundryMemoryProviderScopes.PerUser(). - 14 new unit tests (241/241 hosting unit tests pass). * Replace HostedFoundryMemoryScope enum with Func<...> parameter (#5692) Address PR review feedback from @westey-m: enums are a breaking-change hazard when extended, and the enum was redundant with the existing HostedFoundryMemoryProviderScopes static class. - Delete HostedFoundryMemoryScope.cs. - AddHostedFoundryMemoryProvider DI extensions now take Func<AgentSession?, FoundryMemoryProvider.State>? stateInitializer = null. When null, default to HostedFoundryMemoryProviderScopes.PerUser(). - Callers pick a built-in helper (PerUser/PerChat/PerUserAndChat) or pass a custom delegate. New built-ins are a single static method addition with zero impact on existing callers. - Tests updated; 244/244 hosting unit tests pass. * Fix isolation context resume for externally-created conversations (#5692) Branch on the session's existing hosted-context (not on conversation_id presence) so a conversation provisioned externally (e.g. via conversations.CreateProjectConversationAsync) is treated as fresh on first hosted-agent request and stamped, rather than rejected with 403 hosted_session_identity_mismatch. Strict equality is preserved on real resume of an already-stamped session. Also tighten dotnet/global.json to version 10.0.204 + rollForward latestPatch so local builds match the CI Docker image SDK and avoid 10.0.300 dotnet format stripping required usings. * Revert global.json SDK pin to upstream (#5692) The 10.0.204 + latestPatch pin from the previous commit broke the dotnet-format CI job (hostfxr_resolve_sdk2 could not find a compatible SDK in the mcr.microsoft.com/dotnet/sdk:10.0 image). Restore upstream 10.0.200 + minor; local Release builds with SDK 10.0.300 should set GITHUB_ACTIONS=true to bypass the auto-format-on-build target.
Hosted-MemoryAgent
A hosted Foundry agent that uses FoundryMemoryProvider to remember user-private details across requests and across sessions, scoped per end user via the Foundry platform's isolation keys. The agent plays a friendly travel assistant: tell it about your trip, ask follow-up questions in a new session, and it recalls what it learned about you.
This sample exists to demonstrate two things together:
- How to host an agent that consumes a
Microsoft.Extensions.AI.AIContextProvider(specificallyFoundryMemoryProvider) under the Foundry Responses hosting layer. - How the new
HostedSessionContextflows from theFoundryplatform isolation headers (x-agent-user-isolation-key,x-agent-chat-isolation-key) through theHostedSessionIsolationKeyProviderinto the provider'sstateInitializer, so memories are partitioned per user automatically.
Prerequisites
- .NET 10 SDK
- An Azure AI Foundry project with at least one chat model deployment and one embedding model deployment
- Azure CLI logged in (
az login)
Configuration
Copy the template and fill in your values:
cp .env.example .env
Required:
AZURE_AI_PROJECT_ENDPOINT=https://<account>.services.ai.azure.com/api/projects/<project>
AZURE_AI_MODEL_DEPLOYMENT_NAME=gpt-4o
AZURE_AI_EMBEDDING_DEPLOYMENT_NAME=text-embedding-ada-002
AZURE_AI_MEMORY_STORE_ID=hosted-memory-sample
AGENT_NAME=hosted-memory-agent
ASPNETCORE_URLS=http://+:8088
ASPNETCORE_ENVIRONMENT=Development
For local container runs only (the platform supplies these in production):
HOSTED_USER_ISOLATION_KEY=alice
HOSTED_CHAT_ISOLATION_KEY=alice-chat-1
.envis gitignored. The.env.exampletemplate is checked in as a reference.
How memory scoping works
| Layer | Source of the user identity |
|---|---|
| Inbound request | The Foundry platform sets x-agent-user-isolation-key and x-agent-chat-isolation-key headers on every request. |
| Hosting layer | AgentFrameworkResponseHandler resolves a HostedSessionIsolationKeyProvider from DI and calls GetKeysAsync(context, request, ct). The default implementation reads context.Isolation.UserIsolationKey and context.Isolation.ChatIsolationKey. |
| Session | The handler stores the resolved values on the session as a HostedSessionContext on the first request, and validates the values on every subsequent request that resumes the same conversation (mismatch returns 403). |
| Memory provider | The sample's stateInitializer reads session.GetHostedContext().UserId and uses it as the FoundryMemoryProviderScope. Memories are partitioned per user. |
When running outside the Foundry platform the headers are absent. The sample registers
DevTemporaryLocalSessionIsolationKeyProvider (via AddDevTemporaryLocalContributorSetup) which
falls back to the HOSTED_USER_ISOLATION_KEY and HOSTED_CHAT_ISOLATION_KEY environment variables,
defaulting to a single local-dev-* bucket when neither is set.
Production warning. Never register
DevTemporaryLocalSessionIsolationKeyProviderin production. The Foundry platform sets the isolation keys for every inbound request, and client-supplied environment variables can be forged.
Running directly (contributors)
This project uses ProjectReference to build against the local Agent Framework source.
cd dotnet/samples/04-hosting/FoundryHostedAgents/responses/Hosted-MemoryAgent
dotnet run
The agent starts on http://localhost:8088.
Test it
curl -X POST http://localhost:8088/responses \
-H "Content-Type: application/json" \
-d '{"input": "Hi! My name is Taylor and I am planning a hiking trip to Patagonia in November.", "model": "hosted-memory-agent"}'
Wait a few seconds for memory extraction, then ask a follow-up using the response id from the
previous call as previous_response_id:
curl -X POST http://localhost:8088/responses \
-H "Content-Type: application/json" \
-d '{"input": "What do you already know about my upcoming trip?", "previous_response_id": "<id>", "model": "hosted-memory-agent"}'
Running with Docker
Since this project uses ProjectReference, the standard Dockerfile cannot resolve dependencies
outside this folder. Use Dockerfile.contributor which takes a pre-published output.
1. Publish for the container runtime (Linux Alpine)
dotnet publish -c Debug -f net10.0 -r linux-musl-x64 --self-contained false -o out
2. Build the Docker image
docker build -f Dockerfile.contributor -t hosted-memory-agent .
3. Run the container
export AZURE_BEARER_TOKEN=$(az account get-access-token --resource https://ai.azure.com --query accessToken -o tsv)
docker run --rm -p 8088:8088 \
-e AGENT_NAME=hosted-memory-agent \
-e AZURE_BEARER_TOKEN=$AZURE_BEARER_TOKEN \
-e HOSTED_USER_ISOLATION_KEY=alice \
-e HOSTED_CHAT_ISOLATION_KEY=alice-chat-1 \
--env-file .env \
hosted-memory-agent
4. Smoke test the running container
A scripted smoke test that exercises memory recall and per-user isolation across two simulated
users is provided at scripts/smoke.ps1. From the sample folder:
pwsh ./scripts/smoke.ps1
The script publishes the project, builds the image, runs the container with two distinct
HOSTED_USER_ISOLATION_KEY values, drives a multi-turn conversation per user, asserts that each
user only sees their own memories, and exits non-zero on failure.
NuGet package users
If you are consuming the Agent Framework as a NuGet package (not building from source), use the
standard Dockerfile instead of Dockerfile.contributor. See the commented section in
HostedMemoryAgent.csproj for the PackageReference alternative.
How it differs from sibling samples
| Hosted-ChatClientAgent | Hosted-MemoryAgent | |
|---|---|---|
| Agent definition | Inline (AsAIAgent(model, instructions)) |
Inline, plus AIContextProviders = [memoryProvider] |
| State | None beyond the conversation history | Per-user memories persisted in Foundry Memory |
| Identity | Not used | Required: HostedSessionContext.UserId flows into the memory scope |
| Local dev | AddDevTemporaryLocalContributorSetup() keeps requests succeeding when isolation headers are absent |
Same; additionally honours HOSTED_USER_ISOLATION_KEY to simulate distinct users |