diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index 40c791159ba..92ee3e8521b 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -109,6 +109,10 @@
+
+
+
+
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/ClawAgent.Console.csproj b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/ClawAgent.Console.csproj
new file mode 100644
index 00000000000..4174b7b3d6f
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/ClawAgent.Console.csproj
@@ -0,0 +1,25 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ $(NoWarn);OPENAI001;MAAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/Program.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/Program.cs
new file mode 100644
index 00000000000..4c0d4e346b9
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/Program.cs
@@ -0,0 +1,70 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Diagnostics.Metrics;
+using ClawAgent;
+using Harness.Shared.Console;
+using Harness.Shared.Console.OpenAI;
+using Harness.Shared.Console.ToolFormatters;
+using OpenTelemetry;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+
+const string ServiceName = "ClawAgent.Console";
+var otlpEndpoint = Environment.GetEnvironmentVariable("OTEL_EXPORTER_OTLP_ENDPOINT");
+var telemetryEnabled = !string.IsNullOrWhiteSpace(otlpEndpoint);
+
+// Export telemetry only when an OTLP endpoint is configured. We deliberately avoid the
+// console exporter: this is an interactive app whose UI is rendered by
+// HarnessConsole.RunAgentAsync, and streaming spans/metrics to stdout corrupts that UI.
+var resourceBuilder = ResourceBuilder.CreateDefault().AddService(ServiceName, serviceVersion: "1.0.0");
+using var tracerProvider = telemetryEnabled
+ ? Sdk.CreateTracerProviderBuilder()
+ .SetResourceBuilder(resourceBuilder)
+ .AddSource(ClawAgentFactory.OpenTelemetrySourceName)
+ .AddHttpClientInstrumentation()
+ .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint!))
+ .Build()
+ : null;
+
+using var meterProvider = telemetryEnabled
+ ? Sdk.CreateMeterProviderBuilder()
+ .SetResourceBuilder(resourceBuilder)
+ .AddMeter(ClawAgentFactory.OpenTelemetrySourceName)
+ .AddHttpClientInstrumentation()
+ .AddRuntimeInstrumentation()
+ .AddOtlpExporter(options => options.Endpoint = new Uri(otlpEndpoint!))
+ .Build()
+ : null;
+
+if (!telemetryEnabled)
+{
+ Console.WriteLine("Telemetry export is off. Set OTEL_EXPORTER_OTLP_ENDPOINT to send traces/metrics to an OTLP collector.");
+}
+
+using var meter = new Meter(ClawAgentFactory.OpenTelemetrySourceName);
+var sessionCounter = meter.CreateCounter("claw_console_sessions_total", description: "Interactive claw console sessions started.");
+sessionCounter.Add(1);
+
+await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions
+{
+ Log = Console.WriteLine,
+});
+
+await HarnessConsole.RunAgentAsync(
+ build.Agent,
+ userPrompt: "Ask me to value a stock, score your portfolio risk, research some tickers, or tidy your trade confirmations.",
+ new HarnessConsoleOptions
+ {
+ Observers =
+ [
+ new OpenAIResponsesWebSearchDisplayObserver(),
+ new OpenAIResponsesErrorObserver(),
+ .. HarnessConsoleOptions.BuildObserversWithPlanning(
+ build.Agent,
+ planModeName: "plan",
+ executionModeName: "execute",
+ toolFormatters: ToolCallFormatter.BuildDefaultToolFormatters()),
+ ],
+ CommandHandlers = HarnessConsoleOptions.BuildDefaultCommandHandlers(build.Agent),
+ });
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/README.md b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/README.md
new file mode 100644
index 00000000000..6029ba48dd5
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console/README.md
@@ -0,0 +1,12 @@
+# ClawAgent.Console
+
+Interactive local host for the production-ready claw. It uses the shared `ClawAgentFactory` and the Step 03 console experience (`HarnessConsole.RunAgentAsync`) with planning observers and OpenAI Responses display helpers.
+
+## Run
+
+```bash
+cd dotnet
+dotnet run --project samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Console
+```
+
+Set `OTEL_EXPORTER_OTLP_ENDPOINT` to send traces/metrics to an OTLP collector (for example a local Aspire dashboard). When it is not set, telemetry is not exported — there is no console exporter, because streaming spans and metrics to stdout would corrupt the interactive UI rendered by `HarnessConsole.RunAgentAsync`.
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/ClawAgent.Evals.csproj b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/ClawAgent.Evals.csproj
new file mode 100644
index 00000000000..98062801e81
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/ClawAgent.Evals.csproj
@@ -0,0 +1,16 @@
+
+
+
+ Exe
+ net10.0
+ enable
+ enable
+ $(NoWarn);OPENAI001;MAAI001
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/Program.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/Program.cs
new file mode 100644
index 00000000000..956486d2031
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/Program.cs
@@ -0,0 +1,92 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Text.RegularExpressions;
+using Azure.AI.Projects;
+using Azure.Identity;
+using ClawAgent;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI.Evaluation;
+using FoundryEvals = Microsoft.Agents.AI.Foundry.FoundryEvals;
+
+string[] queries =
+[
+ "What's the capital of France?",
+ "Value MSFT for me.",
+ "How risky is my portfolio?",
+];
+
+await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions
+{
+ // Evals run only the trusted skill scripts bundled with this sample. Auto-approve those scripts
+ // so evaluation receives the completed answer instead of an approval request.
+ AdditionalToolAutoApprovalRules = [AgentSkillsProvider.AllToolsAutoApprovalRule],
+ Log = Console.WriteLine,
+});
+
+Regex digitRegex = new(@"\d");
+LocalEvaluator localEvaluator = new(
+ FunctionEvaluator.Create("off_topic_refusal_or_finance_steer", item =>
+ {
+ if (!item.Query.Contains("capital of France", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ return item.Response.Contains("finance", StringComparison.OrdinalIgnoreCase)
+ || item.Response.Contains("invest", StringComparison.OrdinalIgnoreCase)
+ || item.Response.Contains("portfolio", StringComparison.OrdinalIgnoreCase)
+ || item.Response.Contains("outside", StringComparison.OrdinalIgnoreCase)
+ || item.Response.Contains("can't", StringComparison.OrdinalIgnoreCase)
+ || item.Response.Contains("cannot", StringComparison.OrdinalIgnoreCase);
+ }),
+ FunctionEvaluator.Create("numeric_valuation", item =>
+ !item.Query.Contains("Value MSFT", StringComparison.OrdinalIgnoreCase)
+ || digitRegex.IsMatch(item.Response)),
+ FunctionEvaluator.Create("portfolio_risk_runs", item =>
+ !item.Query.Contains("portfolio", StringComparison.OrdinalIgnoreCase)
+ || !string.IsNullOrWhiteSpace(item.Response)));
+
+AgentEvaluationResults localResults = await build.Agent.EvaluateAsync(queries, localEvaluator, evalName: "ClawLocalFinanceEvals");
+PrintResults("Local finance evals", localResults, queries);
+
+string? endpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT");
+if (!string.IsNullOrWhiteSpace(endpoint))
+{
+ string deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4";
+ AIProjectClient projectClient = new(new Uri(endpoint), new DefaultAzureCredential());
+ FoundryEvals foundryEvals = new(projectClient, deploymentName, FoundryEvals.Relevance, FoundryEvals.Coherence);
+ AgentEvaluationResults foundryResults = await build.Agent.EvaluateAsync(queries, foundryEvals, evalName: "ClawFoundryQualityEvals");
+ PrintResults("Foundry quality evals", foundryResults, queries);
+}
+else
+{
+ Console.WriteLine("Skipping Foundry quality evals. Set FOUNDRY_PROJECT_ENDPOINT to enable them.");
+}
+
+static void PrintResults(string title, AgentEvaluationResults results, string[] queries)
+{
+ Console.WriteLine($"=== {title} ===");
+ Console.WriteLine($"Provider: {results.ProviderName}");
+ Console.WriteLine($"Passed: {results.Passed}/{results.Total}");
+ if (results.ReportUrl is not null)
+ {
+ Console.WriteLine($"Report: {results.ReportUrl}");
+ }
+
+ Console.WriteLine();
+
+ for (int i = 0; i < results.Items.Count; i++)
+ {
+ Console.WriteLine($"Query: {(i < queries.Length ? queries[i] : "N/A")}");
+ Console.WriteLine($"Response: {(results.InputItems?[i].Response is { } response ? response[..Math.Min(80, response.Length)] : "N/A")}...");
+ foreach (var metric in results.Items[i].Metrics)
+ {
+ string value = metric.Value is NumericMetric numericMetric && numericMetric.Value.HasValue
+ ? numericMetric.Value.Value.ToString("F1")
+ : metric.Value.Interpretation?.Failed == true ? "FAIL" : "PASS";
+ Console.WriteLine($" {metric.Key}: {value}");
+ }
+
+ Console.WriteLine();
+ }
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/README.md b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/README.md
new file mode 100644
index 00000000000..848d26c6070
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals/README.md
@@ -0,0 +1,16 @@
+# ClawAgent.Evals
+
+Evaluation host for the production-ready claw.
+
+It builds the shared agent with `ClawAgentFactory`, runs local finance checks with `LocalEvaluator` and `FunctionEvaluator.Create(...)`, and prints `Passed`/`Total`. When `FOUNDRY_PROJECT_ENDPOINT` is available, it also runs Foundry quality evals (`FoundryEvals.Relevance` and `FoundryEvals.Coherence`).
+
+The eval host auto-approves only Agent Skills tools so the trusted scripts bundled with this sample
+can produce complete answers. Trades, shell commands, file writes, and unrelated tools remain subject
+to their normal approval behavior.
+
+## Run
+
+```bash
+cd dotnet
+dotnet run --project samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Evals
+```
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.agentignore b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.agentignore
new file mode 100644
index 00000000000..4e8de03ee83
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.agentignore
@@ -0,0 +1,40 @@
+# Files excluded from agent code deployment packaging.
+# Uses .gitignore syntax.
+# Note: only the root .agentignore is read; subdirectory files are not supported.
+#
+# To include a file that is excluded by default, use negation: !filename
+
+# azd tooling files
+agent.yaml
+agent.manifest.yaml
+azure.yaml
+.agentignore
+
+# Security / secrets
+.env
+.env.*
+.azure/
+.git/
+
+# Python
+__pycache__/
+.venv/
+venv/
+*.pyc
+*.pyo
+.mypy_cache/
+.pytest_cache/
+
+# .NET
+bin/
+obj/
+*.user
+*.suo
+.vs/
+
+# Node
+node_modules/
+
+# Docker (not used in code deploy)
+Dockerfile
+.dockerignore
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.dockerignore b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.dockerignore
new file mode 100644
index 00000000000..7c571658e8a
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.dockerignore
@@ -0,0 +1,10 @@
+.env
+bin/
+obj/
+.vs/
+.vscode/
+*.user
+.azure/
+.checkpoints/
+agent-file-memory/
+*.log
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.gitignore b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.gitignore
new file mode 100644
index 00000000000..5fe72b02045
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/.gitignore
@@ -0,0 +1,2 @@
+.azure
+azure.yaml
\ No newline at end of file
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/ClawAgent.Hosted.csproj b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/ClawAgent.Hosted.csproj
new file mode 100644
index 00000000000..3786e46bbb8
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/ClawAgent.Hosted.csproj
@@ -0,0 +1,23 @@
+
+
+
+ net10.0
+ enable
+ enable
+ false
+ $(NoWarn);OPENAI001;MAAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/Dockerfile b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/Dockerfile
new file mode 100644
index 00000000000..4f9da5f96a0
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/Dockerfile
@@ -0,0 +1,34 @@
+# Dockerfile for the ClawAgent.Hosted Foundry Hosted Agent, built from the agent-framework repo source.
+#
+# This project uses ProjectReference to local repo sources (ClawAgent, Microsoft.Agents.AI.Foundry,
+# Microsoft.Agents.AI.Foundry.Hosting, Microsoft.Agents.AI.LocalCodeAct) and to the repo's Central
+# Package Management, so a standard in-container `dotnet restore`/`publish` cannot resolve everything
+# from this folder alone. Instead, PRE-PUBLISH the app on your machine (inside the full repo, where the
+# references and package versions resolve) and COPY the output into the image:
+#
+# # 1. Build and publish separately, targeting the container runtime (glibc x64):
+# dotnet publish -c Release -f net10.0 -r linux-x64 --self-contained false -o out
+#
+# # 2. Then build the image from the pre-published output:
+# docker build -t personal-finance-claw .
+#
+# # 3. (Optional) run it locally:
+# docker run --rm -p 8088:8088 --env-file .env personal-finance-claw
+#
+# `azd deploy` performs step 2 for you (building remotely in Azure Container Registry) — but you must
+# still run step 1 (publish to ./out) first. See README.md.
+FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS final
+WORKDIR /app
+
+# LocalCodeAct spawns Python to run and validate model-generated code, so the image needs python3.
+RUN apt-get update \
+ && apt-get install -y --no-install-recommends python3 \
+ && rm -rf /var/lib/apt/lists/*
+
+COPY out/ .
+EXPOSE 8088
+ENV ASPNETCORE_URLS=http://+:8088
+ENV LOCAL_CODEACT_PYTHON=python3
+# The container is non-interactive: never block on a console prompt for a missing setting.
+ENV AF_DEMO_NONINTERACTIVE=1
+ENTRYPOINT ["dotnet", "ClawAgent.Hosted.dll"]
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/Program.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/Program.cs
new file mode 100644
index 00000000000..c4266c9d714
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/Program.cs
@@ -0,0 +1,91 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// Hosts the claw as a Foundry Hosted Agent (Responses API).
+//
+// Observability requires no extra wiring here: AddFoundryResponses automatically wraps the agent
+// with OpenTelemetryAgent, and the Foundry hosting runtime (Azure.AI.AgentServer.Core's
+// AddAgentHostTelemetry) registers the OTLP exporter pipeline. In the hosted environment Foundry
+// injects APPLICATIONINSIGHTS_CONNECTION_STRING automatically, so traces, metrics and logs flow to
+// Application Insights with no exporter configuration. To capture prompt/response content in traces,
+// set OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true (off by default).
+//
+// File access and shell are DISABLED on the hosted agent. Granting the model arbitrary read/write
+// access to the container filesystem, or letting it run shell commands, is a serious security risk in
+// a shared hosted environment (data exfiltration, tampering, persistence) — and the local
+// confirmations vault the shell operates on does not exist here. If you genuinely need file access
+// when hosted, supply an external AgentFileStore (for example, one backed by Azure Blob Storage) via
+// ClawAgentFactoryOptions.FileStore instead of using the container disk.
+//
+// CodeAct uses LocalCodeAct here, NOT the Hyperlight provider the local hosts use. Hyperlight runs
+// guest code in a VM-isolated micro-sandbox that needs a hypervisor (KVM) and FUSE — neither of which
+// an unprivileged Foundry hosted container exposes (attempting it fails at startup while configuring
+// `fuse`, so the app never reports ready). LocalCodeAct instead runs the generated Python in a child
+// process and relies on the hosted container itself as the isolation boundary, which is exactly the
+// pattern the canonical Hosted-LocalCodeAct sample uses. SECURITY: LocalCodeAct is not itself a
+// sandbox — only deploy it to an externally sandboxed environment such as a Foundry hosted-agent
+// container.
+
+using Azure.Core;
+using Azure.Identity;
+using ClawAgent;
+using DotNetEnv;
+using Hosted_Shared_Contributor_Setup;
+using Microsoft.Agents.AI.Foundry.Hosting;
+using Microsoft.Agents.AI.LocalCodeAct;
+
+Env.TraversePath().Load();
+
+var builder = WebApplication.CreateBuilder(args);
+var httpContextAccessor = new HttpContextAccessor();
+builder.Services.AddSingleton(httpContextAccessor);
+
+var projectEndpoint = Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
+ ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4";
+var pythonExecutable = Environment.GetEnvironmentVariable("LOCAL_CODEACT_PYTHON") ?? "python3";
+var purviewClientAppId = Environment.GetEnvironmentVariable("PURVIEW_CLIENT_APP_ID");
+
+// WARNING: DefaultAzureCredential is convenient for development but requires careful consideration in
+// production. Prefer a specific credential (e.g. ManagedIdentityCredential) when hosted. Here we chain
+// a temporary dev token (for local Docker debugging) ahead of DefaultAzureCredential (for local
+// dotnet run / managed identity when hosted).
+TokenCredential credential = new ChainedTokenCredential(
+ new DevTemporaryTokenCredential(),
+ new DefaultAzureCredential());
+
+await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions
+{
+ ProjectEndpoint = projectEndpoint,
+ DeploymentName = deploymentName,
+ Credential = credential,
+ AgentDescription = "A production-ready personal finance claw with skills, CodeAct, background agents, telemetry, and optional Purview governance.",
+ PurviewCredential = string.IsNullOrWhiteSpace(purviewClientAppId) ? null : credential,
+ FoundryCallIdProvider = () =>
+ {
+ HttpContext? context = httpContextAccessor.HttpContext;
+ return context is null ? null : context.Request.Headers["x-agent-foundry-call-id"].ToString();
+ },
+
+ // Disable filesystem and shell access on the hosted container (see risk note above).
+ EnableFileAccess = false,
+ EnableShell = false,
+
+ // Use LocalCodeAct instead of the default Hyperlight provider: the hosted container has no
+ // hypervisor/FUSE for Hyperlight, and acts as the sandbox for the child Python process itself.
+ CodeActProvider = new LocalCodeActProvider(pythonExecutable),
+
+ Log = Console.WriteLine,
+});
+
+// AddFoundryResponses wires up the Responses API host for the agent and auto-applies OpenTelemetry.
+builder.Services.AddFoundryResponses(build.Agent);
+
+var app = builder.Build();
+
+// Map the hosted-agent endpoint that live Foundry calls.
+app.MapFoundryResponses();
+
+// Contributor-only: map the per-agent OpenAI route shape for local debugging. Not used in production.
+app.MapDevTemporaryLocalAgentEndpoint();
+
+app.Run();
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/README.md b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/README.md
new file mode 100644
index 00000000000..a1ba6809da7
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/README.md
@@ -0,0 +1,186 @@
+# ClawAgent.Hosted
+
+ASP.NET host that serves the shared claw through the Foundry Responses hosting APIs.
+
+The host is deliberately thin:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+// Wires up the Responses API host for the agent and auto-applies OpenTelemetry.
+builder.Services.AddFoundryResponses(build.Agent);
+
+var app = builder.Build();
+
+// The endpoint that live Foundry calls.
+app.MapFoundryResponses();
+
+// Contributor-only: local REPL route shape. Not used in production.
+app.MapDevTemporaryLocalAgentEndpoint();
+
+app.Run();
+```
+
+## Observability comes for free
+
+No exporter wiring is required. `AddFoundryResponses` automatically wraps the agent with
+`OpenTelemetryAgent`, and the Foundry hosting runtime (`Azure.AI.AgentServer.Core`'s
+`AddAgentHostTelemetry`) registers the OTLP exporter pipeline. When hosted, Foundry injects
+`APPLICATIONINSIGHTS_CONNECTION_STRING` automatically, so traces, metrics, and logs flow to
+Application Insights with no configuration.
+
+To capture prompt and response content in traces (off by default), set:
+
+```bash
+OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=true
+```
+
+## File and shell access are disabled here
+
+The hosted build turns **file access and shell off**:
+
+```csharp
+await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions
+{
+ // ...
+ EnableFileAccess = false,
+ EnableShell = false,
+});
+```
+
+Why: in a shared, hosted container, giving the model arbitrary read/write access to the filesystem, or
+letting it run shell commands, is a serious security risk — data exfiltration, tampering, and
+persistence — even behind a deny-list. The local confirmations vault the shell operates on doesn't
+exist in the hosted environment anyway. If you enable either capability on a hosted container, treat it
+as a production security decision and scope it tightly.
+
+If you genuinely need file access when hosted, prefer supplying an **external `AgentFileStore`** (for
+example, one backed by Azure Blob Storage) rather than the container disk:
+
+```csharp
+await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions
+{
+ // ...
+ EnableFileAccess = true,
+ FileStore = new MyBlobAgentFileStore(blobContainerClient),
+});
+```
+
+## CodeAct runs on LocalCodeAct here, not Hyperlight
+
+The local hosts give the model a **Hyperlight**-backed CodeAct sandbox, which runs guest code in a
+VM-isolated micro-sandbox. That needs a hypervisor (KVM) and FUSE — neither of which an unprivileged
+Foundry hosted container exposes — so the Hyperlight provider can't initialize its sandbox when
+hosted, and the agent never becomes ready.
+
+The hosted build instead supplies a **`LocalCodeActProvider`**, which runs the generated Python in a
+child process and relies on the hosted container itself as the isolation boundary:
+
+```csharp
+await using ClawAgentBuild build = await ClawAgentFactory.CreateAsync(new ClawAgentFactoryOptions
+{
+ // ...
+ EnableFileAccess = false,
+ EnableShell = false,
+
+ // Hyperlight needs a hypervisor + FUSE the hosted container lacks; LocalCodeAct relies on the
+ // container as the sandbox. Override the interpreter with LOCAL_CODEACT_PYTHON if needed.
+ CodeActProvider = new LocalCodeActProvider(
+ Environment.GetEnvironmentVariable("LOCAL_CODEACT_PYTHON") ?? "python3"),
+});
+```
+
+> **Security:** `LocalCodeAct` is not itself a sandbox — it executes model-generated Python in a child
+> process. Only deploy it to an externally sandboxed environment such as a Foundry hosted-agent
+> container. To turn CodeAct off entirely instead, set `EnableCodeAct = false`.
+
+## Run locally
+
+```bash
+cd dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted
+dotnet run
+```
+
+## Deploy to Foundry (container path)
+
+This project deploys as a **container image** (not Foundry's source-code/zip path).
+
+The project uses `ProjectReference` to sibling and
+framework sources (`ClawAgent`, `Microsoft.Agents.AI.Foundry`, `.Foundry.Hosting`,
+`.LocalCodeAct`) and the repo's Central Package Management (`dotnet/Directory.Packages.props`).
+
+Because the ProjectReferences point outside this folder, a standard in-container `dotnet publish`
+can't resolve them. So the flow is **two explicit steps**: publish locally first, then build/deploy
+the image (this is what [`Dockerfile`](./Dockerfile) expects — it just
+`COPY`s the pre-published `out/`).
+
+**1. (First time only) initialize azd in container mode** (writes a `docker`-based `azure.yaml`):
+
+```bash
+azd ai agent init -m agent.manifest.yaml --deploy-mode container
+```
+
+`azd init` provisions (or reuses) a **container registry** and records it in the
+`AZURE_CONTAINER_REGISTRY_ENDPOINT` environment variable, so you don't need to configure a registry
+manually — azd pushes the built image there using this project's [`Dockerfile`](./Dockerfile)
+automatically.
+
+By default azd builds the image **remotely in Azure Container Registry**, so you don't need local
+Docker. Set `remoteBuild: false` under the `docker:` options in `azure.yaml` to build locally
+(requires Docker Desktop).
+
+**2. Build and publish the app separately** (on your machine, inside the full repo, so the
+ProjectReferences and package versions resolve). Target the container runtime (glibc x64):
+
+```bash
+cd dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted
+dotnet publish -c Release -f net10.0 -r linux-x64 --self-contained false -o out
+```
+
+This produces `out/ClawAgent.Hosted.dll` and its dependencies. `out/` is what
+`Dockerfile` copies — you must run this step **before** every image build/deploy.
+
+**3. Grant the Foundry workspace identity `AcrPull` on the registry.** azd pushes the image, but the
+hosted agent runtime pulls it using the Foundry **project's** system-assigned managed identity. That
+identity needs `AcrPull` on your registry, or the deploy fails with *"Container registry
+authentication failed … verify the workspace managed identity has AcrPull permissions"*:
+
+```bash
+# Get the project's system-assigned managed identity principal id:
+PRINCIPAL_ID=$(az resource show \
+ --ids "/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects/" \
+ --query identity.principalId -o tsv)
+
+# Grant AcrPull on the registry:
+az role assignment create \
+ --role AcrPull \
+ --assignee-principal-type ServicePrincipal \
+ --assignee-object-id "$PRINCIPAL_ID" \
+ --scope /subscriptions//resourceGroups//providers/Microsoft.ContainerRegistry/registries/
+```
+
+> RBAC changes can take a minute or two to propagate before the deploy can pull the image.
+
+**4. Deploy:**
+
+```bash
+azd up # first deploy: provisions resources, builds the image, creates the agent version
+# or, once provisioned (remember to re-run step 2 first so out/ is fresh):
+azd deploy
+```
+
+**Test the image locally first (optional but recommended):**
+
+```bash
+# after step 2:
+docker build -t personal-finance-claw .
+docker run --rm -p 8088:8088 --env-file .env personal-finance-claw
+# in another shell — should return HTTP 200:
+curl -i http://localhost:8088/readiness
+```
+
+> **Non-interactive note:** the sample helpers prompt on the console for missing settings, which would
+> block a non-interactive container. The image sets `AF_DEMO_NONINTERACTIVE=1` (and `az`-style hosts
+> have redirected stdin) so startup never blocks. Provide real values via the `env:` map in
+> `azure.yaml` or the container's environment. See the
+> [container deployment guide](https://learn.microsoft.com/azure/foundry/agents/how-to/deploy-hosted-agent).
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/agent.manifest.yaml b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/agent.manifest.yaml
new file mode 100644
index 00000000000..7a29f536f9e
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/agent.manifest.yaml
@@ -0,0 +1,38 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/AgentManifest.yaml
+name: personal-finance-claw
+displayName: "Personal Finance Claw"
+
+description: >
+ A production-ready personal finance claw hosted as a Foundry Hosted Agent with
+ Agent Framework harness capabilities, observability, optional Purview governance,
+ local finance skills, CodeAct, and background agents.
+
+metadata:
+ tags:
+ - AI Agent Hosting
+ - Azure AI AgentServer
+ - Responses Protocol
+ - Agent Framework
+ - Observability
+ - Purview
+ - Evaluation
+
+template:
+ name: personal-finance-claw
+ kind: hosted
+ protocols:
+ - protocol: responses
+ version: 2.0.0
+ resources:
+ cpu: "1.0"
+ memory: 2Gi
+ environment_variables:
+ - name: AZURE_AI_MODEL_DEPLOYMENT_NAME
+ value: "{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}"
+ - name: TOOLBOX_MCP_SERVER_URL
+ value: "{{TOOLBOX_MCP_SERVER_URL}}"
+ - name: PURVIEW_CLIENT_APP_ID
+ value: "{{PURVIEW_CLIENT_APP_ID}}"
+parameters:
+ properties: []
+resources: []
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/agent.yaml b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/agent.yaml
new file mode 100644
index 00000000000..bf2171dd1ad
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent.Hosted/agent.yaml
@@ -0,0 +1,16 @@
+# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml
+kind: hosted
+name: personal-finance-claw
+protocols:
+ - protocol: responses
+ version: 2.0.0
+resources:
+ cpu: "1.0"
+ memory: 2Gi
+environment_variables:
+ - name: AZURE_AI_MODEL_DEPLOYMENT_NAME
+ value: ${AZURE_AI_MODEL_DEPLOYMENT_NAME}
+ - name: TOOLBOX_MCP_SERVER_URL
+ value: ${TOOLBOX_MCP_SERVER_URL}
+ - name: PURVIEW_CLIENT_APP_ID
+ value: ${PURVIEW_CLIENT_APP_ID}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgent.csproj b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgent.csproj
new file mode 100644
index 00000000000..caeca12129f
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgent.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net10.0
+ enable
+ enable
+ $(NoWarn);OPENAI001;MAAI001
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentBuild.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentBuild.cs
new file mode 100644
index 00000000000..87a0b6beff0
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentBuild.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Agents.AI;
+
+namespace ClawAgent;
+
+///
+/// Contains the built production-ready claw agent and resources that must live as long as the agent.
+///
+public sealed class ClawAgentBuild : IAsyncDisposable
+{
+ private readonly List _disposables;
+ private readonly List _asyncDisposables;
+ private bool _disposed;
+
+ internal ClawAgentBuild(
+ AIAgent agent,
+ bool foundrySkillsEnabled,
+ bool purviewEnabled,
+ IEnumerable disposables,
+ IEnumerable asyncDisposables)
+ {
+ this.Agent = agent;
+ this.FoundrySkillsEnabled = foundrySkillsEnabled;
+ this.PurviewEnabled = purviewEnabled;
+ this._disposables = [.. disposables];
+ this._asyncDisposables = [.. asyncDisposables];
+ }
+
+ ///
+ /// Gets the fully configured claw agent.
+ ///
+ public AIAgent Agent { get; }
+
+ ///
+ /// Gets a value indicating whether Foundry Toolbox MCP skills were enabled.
+ ///
+ public bool FoundrySkillsEnabled { get; }
+
+ ///
+ /// Gets a value indicating whether Purview governance was enabled.
+ ///
+ public bool PurviewEnabled { get; }
+
+ ///
+ public async ValueTask DisposeAsync()
+ {
+ if (this._disposed)
+ {
+ return;
+ }
+
+ this._disposed = true;
+ foreach (IAsyncDisposable disposable in this._asyncDisposables)
+ {
+ await disposable.DisposeAsync().ConfigureAwait(false);
+ }
+
+ foreach (IDisposable disposable in this._disposables)
+ {
+ disposable.Dispose();
+ }
+ }
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentFactory.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentFactory.cs
new file mode 100644
index 00000000000..69e766e14a8
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentFactory.cs
@@ -0,0 +1,289 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ClientModel.Primitives;
+using Azure.AI.Projects;
+using Azure.Core;
+using Azure.Identity;
+using HyperlightSandbox.Guest.Python;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Hyperlight;
+using Microsoft.Agents.AI.Purview;
+using Microsoft.Agents.AI.Tools.Shell;
+using Microsoft.Extensions.AI;
+
+namespace ClawAgent;
+
+///
+/// Builds the shared production-ready claw agent used by all hosts.
+///
+public static class ClawAgentFactory
+{
+ ///
+ /// The OpenTelemetry source and meter name used by the claw harness agent.
+ ///
+ public const string OpenTelemetrySourceName = "BuildYourOwnClaw.ProductionReady.Claw";
+
+ private const string DefaultDeploymentName = "gpt-5.4";
+
+ private const string Instructions =
+ """
+ ## Personal Finance Assistant Instructions
+
+ You are a personal finance and investing assistant. You help the user understand their
+ portfolio and watchlist, value individual stocks, gauge portfolio risk, research the market,
+ and keep their records tidy.
+
+ ### Working style
+
+ - The user's holdings live in a file called portfolio.csv. Read it with the file_access tools
+ before answering questions about their portfolio, and never modify it unless asked.
+ - You have skills for valuation and risk-scoring. When a question matches a skill, load it and
+ follow its instructions (read its references, run its scripts) rather than guessing.
+ - When asked to research several tickers, delegate each one to the background research agent so
+ they run concurrently, then summarize the findings together.
+ - The user's trade confirmations accumulate in the working/confirmations folder. When asked to
+ tidy or reorganize them, use the run_shell tool: inspect the folder first, then move files into
+ a year/month layout and rename them to YYYY-MM-DD_TICKER_BUY|SELL.txt. Explain your plan before
+ running commands that change anything.
+ - To buy or sell, use the place_trade tool. This takes a real action, so the user will be asked
+ to approve it before it runs — explain what you are about to do first.
+
+ ### Important
+
+ You provide information and analysis only — you are not a licensed financial advisor and you
+ must not present your output as personalized investment advice. Remind the user to do their own
+ research before making decisions.
+ """;
+
+ ///
+ /// Creates the full claw agent and returns it with the resources that must be disposed by the host.
+ ///
+ /// Optional host-specific build settings.
+ /// Cancellation token.
+ /// The built agent and disposable resources.
+ public static async Task CreateAsync(ClawAgentFactoryOptions? options = null, CancellationToken cancellationToken = default)
+ {
+ options ??= new ClawAgentFactoryOptions();
+ Action log = options.Log ?? Console.WriteLine;
+
+ string endpoint = options.ProjectEndpoint
+ ?? Environment.GetEnvironmentVariable("FOUNDRY_PROJECT_ENDPOINT")
+ ?? throw new InvalidOperationException("FOUNDRY_PROJECT_ENDPOINT is not set.");
+ string deploymentName = options.DeploymentName
+ ?? Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME")
+ ?? DefaultDeploymentName;
+
+ string workingDir = options.WorkingDirectory ?? Path.Combine(AppContext.BaseDirectory, "working");
+ string vaultDir = Path.Combine(workingDir, "confirmations");
+ string skillsDir = options.SkillsDirectory ?? Path.Combine(AppContext.BaseDirectory, "skills");
+
+ TokenCredential credential = options.Credential ?? new DefaultAzureCredential();
+ AIProjectClient projectClient = new(
+ new Uri(endpoint),
+ credential,
+ new AIProjectClientOptions { RetryPolicy = new ClientRetryPolicy(3) });
+
+ IChatClient chatClient = projectClient
+ .GetProjectOpenAIClient()
+ .GetResponsesClient()
+ .AsIChatClient(deploymentName);
+
+ bool purviewEnabled = false;
+ string? purviewClientAppId = Environment.GetEnvironmentVariable("PURVIEW_CLIENT_APP_ID");
+ TokenCredential? purviewCredential = options.PurviewCredential;
+ if (purviewCredential is null && !string.IsNullOrWhiteSpace(purviewClientAppId))
+ {
+ purviewCredential = new InteractiveBrowserCredential(
+ new InteractiveBrowserCredentialOptions { ClientId = purviewClientAppId });
+ }
+
+ if (purviewCredential is not null)
+ {
+ chatClient = chatClient
+ .AsBuilder()
+ .WithPurview(purviewCredential, new PurviewSettings("Claw"))
+ .Build();
+ purviewEnabled = true;
+ log(options.PurviewCredential is not null
+ ? "Purview enabled (host-provided credential). "
+ : "Purview enabled (interactive browser credential). ");
+ }
+ else
+ {
+ log("Purview disabled. Set PURVIEW_CLIENT_APP_ID to enable governance checks.");
+ }
+
+ var skillsBuilder = new AgentSkillsProviderBuilder()
+ .UseFileSkills([skillsDir], scriptRunner: new SubprocessScriptRunner().RunAsync);
+
+ HttpClient? toolboxHttpClient = null;
+ ModelContextProtocol.Client.McpClient? toolboxMcpClient = null;
+ string? toolboxUrl = Environment.GetEnvironmentVariable("TOOLBOX_MCP_SERVER_URL");
+ bool foundrySkillsEnabled = false;
+ if (!string.IsNullOrWhiteSpace(toolboxUrl))
+ {
+ (toolboxMcpClient, toolboxHttpClient) = await FoundrySkills.ConnectAsync(
+ toolboxUrl,
+ credential,
+ options.FoundryCallIdProvider,
+ cancellationToken).ConfigureAwait(false);
+ skillsBuilder.UseMcpSkills(toolboxMcpClient);
+ foundrySkillsEnabled = true;
+ log("Foundry skills enabled (Toolbox MCP). ");
+ }
+ else
+ {
+ log("Foundry skills disabled. Set TOOLBOX_MCP_SERVER_URL to enable them.");
+ }
+
+ skillsBuilder.UseOptions((options) =>
+ {
+ options.DisableLoadSkillApproval = true;
+ options.DisableReadSkillResourceApproval = true;
+ });
+
+ AgentSkillsProvider skillsProvider = skillsBuilder.Build();
+ AIAgent researchAgent = ResearchAgent.Create(chatClient);
+
+ // Shell access is a powerful capability. It is confined to the local vault directory with a
+ // deny-list here, but on shared/hosted deployments it is disabled entirely (see hosted host).
+ LocalShellExecutor? shell = null;
+ if (options.EnableShell)
+ {
+ shell = new LocalShellExecutor(new LocalShellExecutorOptions
+ {
+ WorkingDirectory = vaultDir,
+ ConfineWorkingDirectory = true,
+ Policy = new ShellPolicy(denyList:
+ [
+ @"\brm\s+-rf\b",
+ @"\bsudo\b",
+ @":\(\)\s*\{",
+ @"\bmkfs\b",
+ @">\s*/dev/sd",
+ ]),
+ Timeout = TimeSpan.FromSeconds(15),
+ });
+ log("Shell enabled (confined to the confirmations vault). ");
+ }
+ else
+ {
+ log("Shell disabled. ");
+ }
+
+ // File access is enabled by default via a filesystem-backed store. Hosts may disable it or
+ // supply an external store (for example, backed by blob storage) instead of the container disk.
+ AgentFileStore? fileStore = null;
+ if (options.EnableFileAccess)
+ {
+ fileStore = options.FileStore ?? new FileSystemAgentFileStore(workingDir);
+ log(options.FileStore is not null
+ ? "File access enabled (custom AgentFileStore). "
+ : "File access enabled (local filesystem). ");
+ }
+ else
+ {
+ log("File access disabled. ");
+ }
+
+ // CodeAct gives the model a sandboxed code interpreter. By default we use the Hyperlight
+ // provider, which runs guest code in a VM-isolated micro-sandbox — great for local hosts, but
+ // it needs a hypervisor (KVM) and FUSE, which an unprivileged Foundry hosted container does not
+ // expose. Hosts running in such an environment supply their own provider via
+ // options.CodeActProvider (for example a LocalCodeActProvider that relies on the container
+ // itself as the isolation boundary) or disable CodeAct entirely with EnableCodeAct = false.
+ AIContextProvider? codeAct = null;
+ if (options.EnableCodeAct)
+ {
+ codeAct = options.CodeActProvider
+ ?? new HyperlightCodeActProvider(HyperlightCodeActProviderOptions.CreateForWasm(PythonGuestModule.GetModulePath()));
+ log(options.CodeActProvider is not null
+ ? "CodeAct enabled (custom provider). "
+ : "CodeAct enabled (Hyperlight VM-isolated sandbox). ");
+ }
+ else
+ {
+ log("CodeAct disabled. ");
+ }
+
+ List contextProviders = [skillsProvider];
+ if (codeAct is not null)
+ {
+ contextProviders.Add(codeAct);
+ }
+
+ List tools =
+ [
+ StockTools.CreateGetStockPriceTool(),
+ TradingTools.CreatePlaceTradeTool(),
+ ];
+
+ // Shell support is composed explicitly: the context provider tells the model about the
+ // environment, while the approval-gated function exposes command execution.
+ if (shell is not null)
+ {
+ contextProviders.Add(new ShellEnvironmentProvider(shell));
+ tools.Add(shell.AsAIFunction(requireApproval: true));
+ }
+
+ List>> autoApprovalRules =
+ [
+ FileAccessProvider.ReadOnlyToolsAutoApprovalRule,
+ ];
+ if (options.AdditionalToolAutoApprovalRules is not null)
+ {
+ autoApprovalRules.AddRange(options.AdditionalToolAutoApprovalRules);
+ }
+
+ AIAgent agent = chatClient.AsHarnessAgent(new HarnessAgentOptions
+ {
+ Name = options.AgentName,
+ Description = options.AgentDescription,
+ FileAccessStore = fileStore,
+ DisableAgentSkillsProvider = true,
+ BackgroundAgents = [researchAgent],
+ OpenTelemetrySourceName = OpenTelemetrySourceName,
+ ToolApprovalAgentOptions = new ToolApprovalAgentOptions
+ {
+ AutoApprovalRules = autoApprovalRules,
+ },
+ AgentModeProviderOptions = new AgentModeProviderOptions { DefaultMode = "execute" },
+ AIContextProviders = contextProviders,
+ ChatOptions = new ChatOptions
+ {
+ Instructions = Instructions,
+ Tools = tools,
+ Reasoning = new() { Effort = ReasoningEffort.Medium },
+ },
+ });
+
+ List disposables = [];
+ if (codeAct is IDisposable disposableCodeAct)
+ {
+ disposables.Add(disposableCodeAct);
+ }
+
+ if (toolboxHttpClient is not null)
+ {
+ disposables.Add(toolboxHttpClient);
+ }
+
+ if (chatClient is IDisposable disposableChatClient)
+ {
+ disposables.Add(disposableChatClient);
+ }
+
+ List asyncDisposables = [];
+ if (shell is not null)
+ {
+ asyncDisposables.Add(shell);
+ }
+
+ if (toolboxMcpClient is not null)
+ {
+ asyncDisposables.Add(toolboxMcpClient);
+ }
+
+ return new ClawAgentBuild(agent, foundrySkillsEnabled, purviewEnabled, disposables, asyncDisposables);
+ }
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentFactoryOptions.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentFactoryOptions.cs
new file mode 100644
index 00000000000..39a6eebb7c4
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ClawAgentFactoryOptions.cs
@@ -0,0 +1,133 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Azure.Core;
+using Microsoft.Agents.AI;
+
+namespace ClawAgent;
+
+///
+/// Options for building the production-ready claw agent.
+///
+public sealed class ClawAgentFactoryOptions
+{
+ ///
+ /// Gets or sets the Foundry project endpoint. Defaults to FOUNDRY_PROJECT_ENDPOINT.
+ ///
+ public string? ProjectEndpoint { get; set; }
+
+ ///
+ /// Gets or sets the Foundry model deployment name. Defaults to AZURE_AI_MODEL_DEPLOYMENT_NAME or gpt-5.4.
+ ///
+ public string? DeploymentName { get; set; }
+
+ ///
+ /// Gets or sets the token credential used for Foundry. Defaults to .
+ ///
+ public TokenCredential? Credential { get; set; }
+
+ ///
+ /// Gets or sets the token credential used for Purview. When provided, Purview is enabled with
+ /// this credential. Otherwise, PURVIEW_CLIENT_APP_ID enables local browser authentication.
+ ///
+ public TokenCredential? PurviewCredential { get; set; }
+
+ ///
+ /// Gets or sets an optional provider for the current Foundry hosted call ID. When supplied, the
+ /// call ID is forwarded to the Toolbox MCP endpoint as x-agent-foundry-call-id.
+ ///
+ public Func? FoundryCallIdProvider { get; set; }
+
+ ///
+ /// Gets or sets the agent name exposed to hosting and telemetry.
+ ///
+ public string AgentName { get; set; } = "personal-finance-claw";
+
+ ///
+ /// Gets or sets the agent description exposed to hosting and telemetry.
+ ///
+ public string AgentDescription { get; set; } = "A production-ready personal finance claw with skills, shell, CodeAct, background agents, telemetry, and optional Purview governance.";
+
+ ///
+ /// Gets or sets the working directory containing portfolio data and trade confirmations.
+ ///
+ public string? WorkingDirectory { get; set; }
+
+ ///
+ /// Gets or sets the directory containing file-based skills.
+ ///
+ public string? SkillsDirectory { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the agent can read and write files on the host.
+ ///
+ ///
+ /// Enabled by default for local hosts. Disable it on shared/hosted deployments where giving the
+ /// model arbitrary read/write access to the container filesystem is a data-exfiltration and
+ /// tampering risk. When you still need file access in a hosted environment, prefer supplying an
+ /// external (for example, a blob-storage-backed store) rather than the
+ /// container disk.
+ ///
+ public bool EnableFileAccess { get; set; } = true;
+
+ ///
+ /// Gets or sets an optional custom used for file access.
+ ///
+ ///
+ /// When (and is ), a
+ /// rooted at is used. Supply
+ /// your own store to keep files off the local disk — for example, a store backed by Azure Blob
+ /// Storage — which is the recommended approach for hosted deployments. Ignored when
+ /// is .
+ ///
+ public AgentFileStore? FileStore { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the agent can run shell commands on the host.
+ ///
+ ///
+ /// Enabled by default for local hosts. Disable it on shared/hosted deployments: arbitrary command
+ /// execution inside the hosted container is a serious security risk (data exfiltration, persistence,
+ /// tampering) even with a deny-list, and the local vault it operates on does not exist in the
+ /// hosted environment.
+ ///
+ public bool EnableShell { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether the agent exposes a CodeAct code interpreter.
+ ///
+ ///
+ /// Enabled by default. When and is
+ /// , a Hyperlight-backed, VM-isolated provider is used — suitable for local
+ /// hosts with a hypervisor (KVM) and FUSE. Foundry hosted containers do not expose those, so a
+ /// hosted host should either supply a that relies on the container as
+ /// the sandbox (for example a LocalCodeActProvider) or set this to .
+ ///
+ public bool EnableCodeAct { get; set; } = true;
+
+ ///
+ /// Gets or sets an optional CodeAct context provider used when is
+ /// .
+ ///
+ ///
+ /// When the factory creates a Hyperlight-backed provider. Supply your own
+ /// (for example a LocalCodeActProvider) to run in an environment without a hypervisor, such
+ /// as a Foundry hosted container. Ignored when is
+ /// . If the provider implements it is
+ /// disposed by the returned .
+ ///
+ public AIContextProvider? CodeActProvider { get; set; }
+
+ ///
+ /// Gets or sets additional tool auto-approval rules for this host.
+ ///
+ ///
+ /// The factory always includes the read-only file-access rule. Use additional rules only in
+ /// trusted hosts, such as an evaluation runner that executes bundled skill scripts.
+ ///
+ public IEnumerable>>? AdditionalToolAutoApprovalRules { get; set; }
+
+ ///
+ /// Gets or sets the optional log callback used for setup notes.
+ ///
+ public Action? Log { get; set; }
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/FoundrySkills.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/FoundrySkills.cs
new file mode 100644
index 00000000000..b1748c10a39
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/FoundrySkills.cs
@@ -0,0 +1,75 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Net.Http.Headers;
+using Azure.Core;
+using ModelContextProtocol.Client;
+
+namespace ClawAgent;
+
+///
+/// Helpers for wiring centrally-managed Foundry skills into the claw via a Foundry Toolbox MCP endpoint.
+///
+internal static class FoundrySkills
+{
+ ///
+ /// Connects to a Foundry Toolbox MCP endpoint and returns a connected MCP client.
+ ///
+ public static async Task<(McpClient McpClient, HttpClient HttpClient)> ConnectAsync(
+ string toolboxMcpServerUrl,
+ TokenCredential credential,
+ Func? foundryCallIdProvider = null,
+ CancellationToken cancellationToken = default)
+ {
+ var httpClient = new HttpClient(new BearerTokenHandler(credential, "https://ai.azure.com/.default", foundryCallIdProvider)
+ {
+ InnerHandler = new HttpClientHandler(),
+ });
+
+ try
+ {
+ McpClient mcpClient = await McpClient.CreateAsync(
+ new HttpClientTransport(
+ new HttpClientTransportOptions
+ {
+ Endpoint = new Uri(toolboxMcpServerUrl),
+ Name = "foundry_toolbox",
+ TransportMode = HttpTransportMode.StreamableHttp,
+ AdditionalHeaders = new Dictionary
+ {
+ ["Foundry-Features"] = "Toolboxes=V1Preview",
+ },
+ },
+ httpClient),
+ cancellationToken: cancellationToken).ConfigureAwait(false);
+
+ return (mcpClient, httpClient);
+ }
+ catch
+ {
+ httpClient.Dispose();
+ throw;
+ }
+ }
+
+ private sealed class BearerTokenHandler(
+ TokenCredential credential,
+ string scope,
+ Func? foundryCallIdProvider) : DelegatingHandler
+ {
+ private readonly TokenRequestContext _tokenContext = new([scope]);
+
+ protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
+ {
+ AccessToken token = await credential.GetTokenAsync(this._tokenContext, cancellationToken).ConfigureAwait(false);
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token);
+
+ string? callId = foundryCallIdProvider?.Invoke();
+ if (!string.IsNullOrWhiteSpace(callId) && !request.Headers.Contains("x-agent-foundry-call-id"))
+ {
+ request.Headers.TryAddWithoutValidation("x-agent-foundry-call-id", callId);
+ }
+
+ return await base.SendAsync(request, cancellationToken).ConfigureAwait(false);
+ }
+ }
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/README.md b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/README.md
new file mode 100644
index 00000000000..3a04dec0291
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/README.md
@@ -0,0 +1,7 @@
+# ClawAgent
+
+Shared class library for the production-ready personal finance claw.
+
+`ClawAgentFactory.CreateAsync(...)` returns a `ClawAgentBuild` containing the fully configured `AIAgent` plus disposable resources. It preserves the Step 03 capabilities: Foundry Responses `IChatClient`, local file skills, optional Foundry Toolbox MCP skills, background research agent, confined `LocalShellExecutor`, Hyperlight CodeAct, file access, approvals, agent modes, stock tools, and trading tools.
+
+Purview is opt-in via `PURVIEW_CLIENT_APP_ID`; when unset, the chat client is not wrapped. Telemetry is always enabled through `HarnessAgentOptions.OpenTelemetrySourceName`.
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ResearchAgent.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ResearchAgent.cs
new file mode 100644
index 00000000000..d76d98b39fb
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/ResearchAgent.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.AI;
+
+namespace ClawAgent;
+
+///
+/// Builds the background research agent that the main claw fans work out to.
+///
+internal static class ResearchAgent
+{
+ ///
+ /// Creates a web-search-only background agent for delegated ticker research.
+ ///
+ public static AIAgent Create(IChatClient chatClient) =>
+ chatClient.AsAIAgent(
+ instructions:
+ "You research a single stock ticker. Use the web search tool to find the most " +
+ "recent, relevant news and commentary, then return a short, factual summary " +
+ "(3-4 bullet points) with no preamble.",
+ name: "TickerResearchAgent",
+ description: "Searches the web for recent news and commentary about a single stock ticker.",
+ tools: [new HostedWebSearchTool()]);
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/StockTools.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/StockTools.cs
new file mode 100644
index 00000000000..1404e4ef267
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/StockTools.cs
@@ -0,0 +1,54 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.ComponentModel;
+using Microsoft.Extensions.AI;
+
+namespace ClawAgent;
+
+///
+/// A custom function tool that gives the claw access to illustrative stock prices.
+///
+internal static class StockTools
+{
+ ///
+ /// A delayed, illustrative stock quote, including trailing earnings-per-share.
+ ///
+ public sealed record StockQuote(string Symbol, decimal Price, decimal TrailingEps, string Currency, DateTimeOffset AsOf);
+
+ private static readonly Dictionary s_priceBook = new(StringComparer.OrdinalIgnoreCase)
+ {
+ ["MSFT"] = (462.97m, 11.80m),
+ ["AAPL"] = (229.35m, 6.13m),
+ ["GOOGL"] = (178.12m, 7.54m),
+ ["AMZN"] = (201.45m, 4.18m),
+ ["NVDA"] = (134.81m, 2.95m),
+ ["SPY"] = (612.40m, 23.10m),
+ };
+
+ ///
+ /// Gets the latest delayed, illustrative stock price and trailing EPS for a ticker symbol.
+ ///
+ [Description("Gets the latest (delayed, illustrative) stock price and trailing earnings per share for a ticker symbol.")]
+ public static StockQuote GetStockPrice(
+ [Description("The stock ticker symbol, e.g. MSFT or AAPL.")] string symbol)
+ {
+ if (!s_priceBook.TryGetValue(symbol, out var data))
+ {
+ var seed = 0;
+ foreach (var ch in symbol.ToUpperInvariant())
+ {
+ seed = (seed * 31 + ch) % 1_000_000;
+ }
+
+ var price = 50m + seed % 45000 / 100m;
+ data = (price, Math.Round(price / 20m, 2));
+ }
+
+ return new StockQuote(symbol.ToUpperInvariant(), data.Price, data.Eps, "USD", DateTimeOffset.UtcNow);
+ }
+
+ ///
+ /// Creates the AI function wrapper used to expose the stock price tool to the agent.
+ ///
+ public static AIFunction CreateGetStockPriceTool() => AIFunctionFactory.Create(GetStockPrice, "get_stock_price");
+}
diff --git a/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/SubprocessScriptRunner.cs b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/SubprocessScriptRunner.cs
new file mode 100644
index 00000000000..b892b634b6a
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/BuildYourOwnClaw/Claw_Step04_ProductionReady/ClawAgent/SubprocessScriptRunner.cs
@@ -0,0 +1,179 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+using System.Diagnostics;
+using System.Text.Json;
+using Microsoft.Agents.AI;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace ClawAgent;
+
+///
+/// Executes file-based skill scripts as local subprocesses.
+///
+internal sealed class SubprocessScriptRunner
+{
+ private static readonly TimeSpan s_scriptTimeout = TimeSpan.FromSeconds(30);
+ private readonly ILogger _logger;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SubprocessScriptRunner(ILoggerFactory? loggerFactory = null)
+ {
+ this._logger = (loggerFactory ?? NullLoggerFactory.Instance).CreateLogger();
+ }
+
+ ///
+ /// Runs a skill script as a local subprocess.
+ ///
+ public async Task