* .NET: Add Hosted-AgentSkills sample for Foundry Skills integration Add a new hosted agent sample that demonstrates how to load behavioral guidelines from Foundry Skills at startup using AgentSkillsProvider and the progressive disclosure pattern (advertise -> load on demand). The sample: - Downloads SKILL.md files from Foundry via ProjectAgentSkills SDK - Extracts ZIP archives with zip-slip protection - Wires skills into AgentSkillsProvider as an AIContextProvider - Hosts the agent via the Responses protocol Ships two Contoso Outdoors skills matching the Python sample (PR #5822): - support-style: tone, formatting, signature guidelines - escalation-policy: when and how to escalate tickets Includes convenience provisioning gated behind PROVISION_SAMPLE_SKILLS env var, clearly documented as NOT a production pattern. Closes #5776 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Add unit tests and integration test for Hosted-AgentSkills Unit tests (14 tests, all passing): - ZIP extraction with zip-slip guard (valid archive, traversal attack, sibling-prefix attack, directory entries) - Skill name validation (rejects dots, separators, traversal patterns) - AgentSkillsProvider with downloaded skills (advertises both skills, load_skill returns canary tokens, unknown skill returns error) Container integration test: - New 'agent-skills' scenario in the test container that creates Contoso Outdoors skills on disk and wires AgentSkillsProvider - AgentSkillsHostedAgentFixture + 4 integration tests verifying: - Routine questions load support-style skill (STYLE-CANARY-3318) - Escalation triggers load escalation-policy (ESC-CANARY-7742) - Skills are advertised in system prompt - load_skill tool is invoked via FunctionCallContent Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Add smoke test, bootstrap, and docs for agent-skills integration - Add scripts/smoke.ps1 for local Docker smoke testing: builds the contributor image, runs the container, verifies both skills are loaded via canary tokens (STYLE-CANARY-3318, ESC-CANARY-7742) - Add 'agent-skills' to the bootstrap script scenario list - Add agent-skills row to the integration test README scenarios table - Exclude HostedAgentSkillsPatternTests from net472 (uses net8.0+ APIs) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Update commented-out package versions to latest across all hosted samples Update the end-user PackageReference versions (in the commented-out sections) from 1.0.0 to the current latest NuGet versions: - Microsoft.Agents.AI: 1.6.1 - Microsoft.Agents.AI.Foundry: 1.6.1-preview.260514.1 - Microsoft.Agents.AI.Foundry.Hosting: 1.6.1-preview.260514.1 - Microsoft.Agents.AI.Hosting: 1.6.1-preview.260514.1 - Microsoft.Agents.AI.OpenAI: 1.6.1 - Microsoft.Agents.AI.Workflows: 1.6.1 Also adds explicit versions to Hosted-Workflow-Handoff which had bare PackageReference entries without Version attributes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Fix broken markdown links in Hosted-AgentSkills README Remove references to non-existent ../../README.md. Replace with inline instructions matching other hosted samples that don't have a parent README. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * .NET: Use OS-appropriate string comparison in zip-slip guard Use Ordinal on Unix (case-sensitive FS) and OrdinalIgnoreCase on Windows to prevent case-based path bypass on Linux containers. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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 |