diff --git a/dotnet/Directory.Packages.props b/dotnet/Directory.Packages.props
index 2121be9207..efa9a70227 100644
--- a/dotnet/Directory.Packages.props
+++ b/dotnet/Directory.Packages.props
@@ -112,6 +112,7 @@
+
diff --git a/dotnet/agent-framework-dotnet.slnx b/dotnet/agent-framework-dotnet.slnx
index d494768139..d694c10cbc 100644
--- a/dotnet/agent-framework-dotnet.slnx
+++ b/dotnet/agent-framework-dotnet.slnx
@@ -124,6 +124,7 @@
+
diff --git a/dotnet/samples/02-agents/Harness/ConsoleReactiveComponents/AnsiEscapes.cs b/dotnet/samples/02-agents/Harness/ConsoleReactiveComponents/AnsiEscapes.cs
index b13c2ddc82..cf916938e7 100644
--- a/dotnet/samples/02-agents/Harness/ConsoleReactiveComponents/AnsiEscapes.cs
+++ b/dotnet/samples/02-agents/Harness/ConsoleReactiveComponents/AnsiEscapes.cs
@@ -24,6 +24,11 @@ public static class AnsiEscapes
///
public static string MoveCursor(int row, int column) => $"\x1b[{row};{column}H";
+ ///
+ /// Erases the current line from the cursor position to the end of the line (EL 0).
+ ///
+ public static string EraseToEndOfLine => "\x1b[0K";
+
///
/// Erases the entire current line (EL 2).
///
diff --git a/dotnet/samples/02-agents/Harness/Harness_Shared_Console/Components/AgentStatus.cs b/dotnet/samples/02-agents/Harness/Harness_Shared_Console/Components/AgentStatus.cs
index f07035d27a..725e1ffaa6 100644
--- a/dotnet/samples/02-agents/Harness/Harness_Shared_Console/Components/AgentStatus.cs
+++ b/dotnet/samples/02-agents/Harness/Harness_Shared_Console/Components/AgentStatus.cs
@@ -35,6 +35,7 @@ public class AgentStatus : ConsoleReactiveComponent
/// Initializes a new instance of the class.
@@ -85,7 +86,12 @@ public override void RenderCore(AgentStatusProps props, AgentStatusState state)
}
System.Console.Write(AnsiEscapes.SaveCursor);
- System.Console.Write(AnsiEscapes.MoveAndEraseLine(this.Y));
+ System.Console.Write(AnsiEscapes.MoveCursor(this.Y, this.X));
+ if (props != this._previousProps)
+ {
+ System.Console.Write(AnsiEscapes.EraseToEndOfLine);
+ this._previousProps = props;
+ }
if (props.ShowSpinner)
{
diff --git a/dotnet/samples/02-agents/Harness/Harness_Shared_Console/HarnessConsole.cs b/dotnet/samples/02-agents/Harness/Harness_Shared_Console/HarnessConsole.cs
index 1f313d1008..f65d220882 100644
--- a/dotnet/samples/02-agents/Harness/Harness_Shared_Console/HarnessConsole.cs
+++ b/dotnet/samples/02-agents/Harness/Harness_Shared_Console/HarnessConsole.cs
@@ -1,5 +1,6 @@
// Copyright (c) Microsoft. All rights reserved.
+using System.Text;
using Harness.ConsoleReactiveComponents;
using Microsoft.Agents.AI;
@@ -24,6 +25,8 @@ public static async Task RunAgentAsync(AIAgent agent, string userPrompt, Harness
{
options ??= new();
+ System.Console.OutputEncoding = Encoding.UTF8;
+
// Null means use defaults; an explicit (possibly empty) list means use exactly what was provided.
var observers = options.Observers
?? HarnessConsoleOptions.BuildDefaultObservers();
@@ -63,6 +66,7 @@ public static async Task RunAgentAsync(AIAgent agent, string userPrompt, Harness
System.Console.ResetColor();
System.Console.Write(AnsiEscapes.ResetScrollRegion);
+ System.Console.Write(AnsiEscapes.EraseScrollbackBuffer);
System.Console.Write(AnsiEscapes.EraseEntireScreen);
System.Console.Write(AnsiEscapes.MoveCursor(1, 1));
System.Console.WriteLine("Goodbye!");
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Harness_Step04_CodeExecution.csproj b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Harness_Step04_CodeExecution.csproj
new file mode 100644
index 0000000000..729ba2dd88
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Harness_Step04_CodeExecution.csproj
@@ -0,0 +1,29 @@
+
+
+
+ Exe
+ net10.0
+
+ enable
+ enable
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs
new file mode 100644
index 0000000000..af53443c63
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/Program.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Microsoft. All rights reserved.
+
+// This sample demonstrates a HarnessAgent with ALL features enabled, plus:
+// - Hyperlight CodeAct (HyperlightCodeActProvider) for sandboxed Python code execution
+// - Skills (AgentSkillsProvider) discovering a local "regex-tester" skill
+//
+// The agent can plan tasks with todos, manage modes, store memories, read/write files,
+// search the web, approve sensitive tools, discover and use skills, and execute arbitrary
+// Python code in a Hyperlight sandbox — all pre-configured by the HarnessAgent.
+//
+// Try asking: "Help me write a regex that matches valid email addresses, then test it."
+//
+// Special commands:
+// /todos — Display the current todo list without invoking the agent.
+// /mode — Get or set the current agent mode.
+// /exit — End the session.
+
+#pragma warning disable OPENAI001 // Suppress experimental API warnings for Responses API usage.
+#pragma warning disable MAAI001 // Suppress experimental API warnings for Agents AI experiments.
+
+using System.ClientModel.Primitives;
+using Azure.AI.Projects;
+using Azure.Identity;
+using Harness.Shared.Console;
+using HyperlightSandbox.Guest.Python;
+using Microsoft.Agents.AI;
+using Microsoft.Agents.AI.Hyperlight;
+using Microsoft.Extensions.AI;
+
+var endpoint = Environment.GetEnvironmentVariable("AZURE_AI_PROJECT_ENDPOINT") ?? throw new InvalidOperationException("AZURE_AI_PROJECT_ENDPOINT is not set.");
+var deploymentName = Environment.GetEnvironmentVariable("AZURE_AI_MODEL_DEPLOYMENT_NAME") ?? "gpt-5.4";
+
+const int MaxContextWindowTokens = 1_050_000;
+const int MaxOutputTokens = 128_000;
+const string TracingSourceName = "Harness.CodeExecution";
+
+// Set up OpenTelemetry tracing that writes spans to a text file.
+using var tracerProvider = HarnessTracing.CreateFileTracerProvider(TracingSourceName);
+
+// Create the HyperlightCodeActProvider with the Python/Wasm backend.
+// The guest module path is resolved automatically from the Hyperlight.HyperlightSandbox.Guest.Python NuGet package.
+using var codeAct = new HyperlightCodeActProvider(
+ HyperlightCodeActProviderOptions.CreateForWasm(PythonGuestModule.GetModulePath()));
+
+var instructions =
+ """
+ ## Technical Assistant Instructions
+
+ You are a code-powered technical assistant. You can execute Python code in a sandboxed environment
+ to solve problems precisely rather than guessing. You also have access to skills that provide
+ structured workflows for specific technical tasks.
+
+ ### Code Execution
+
+ When a problem requires computation, validation, or testing:
+ - Write Python code and use `execute_code` to run it in the sandbox.
+ - Always verify results by running the code rather than reasoning about what would happen.
+ - If code fails, read the error message carefully, fix the issue, and retry.
+
+ ### Skills
+
+ You have access to discoverable skills. When a task matches a skill's description:
+ - Follow the skill's instructions carefully.
+ - Use the skill's reference materials for context.
+ - Combine the skill's workflow with code execution when appropriate.
+
+ ### Planning and Research
+
+ For complex tasks:
+ - Break the problem into steps using your todo list.
+ - Research background information using web search when needed.
+ - Save important findings to file memory for later reference.
+
+ ### Presenting Results
+
+ - Show your work: include the code you ran and its output.
+ - Explain what each part of your solution does.
+ - If applicable, save final results to file memory.
+ """;
+
+// Create the agent with ALL HarnessAgent features enabled plus Hyperlight CodeAct.
+// No Disable* flags are set — TodoProvider, AgentModeProvider, FileMemory, FileAccess,
+// ToolApproval, WebSearch, and AgentSkillsProvider are all active.
+AIAgent agent =
+ new AIProjectClient(
+ new Uri(endpoint),
+ new DefaultAzureCredential(),
+ new AIProjectClientOptions { RetryPolicy = new ClientRetryPolicy(3) })
+ .GetProjectOpenAIClient()
+ .GetResponsesClient()
+ .AsIChatClient(deploymentName)
+ .AsHarnessAgent(MaxContextWindowTokens, MaxOutputTokens, new HarnessAgentOptions
+ {
+ Name = "CodeExecutionAgent",
+ Description = "A technical assistant with sandboxed code execution and skill-based workflows.",
+ OpenTelemetrySourceName = TracingSourceName,
+ // Point the file memory at a local folder for persistent memory across sessions.
+ FileMemoryStore = new FileSystemAgentFileStore(Path.Combine(AppContext.BaseDirectory, "agent-files")),
+ // Add the HyperlightCodeActProvider so the agent can execute Python code in a sandbox.
+ AIContextProviders = [codeAct],
+ ChatOptions = new ChatOptions
+ {
+ Instructions = instructions,
+ MaxOutputTokens = MaxOutputTokens,
+ Reasoning = new() { Effort = ReasoningEffort.Medium },
+ },
+ });
+
+// Run the interactive console session using the shared HarnessConsole helper.
+await HarnessConsole.RunAgentAsync(
+ agent,
+ userPrompt: "Ask me a technical question, or try: \"Help me write a regex that matches valid email addresses.\"",
+ new HarnessConsoleOptions
+ {
+ Observers = HarnessConsoleOptions.BuildObserversWithPlanning(
+ agent,
+ planModeName: "plan",
+ executionModeName: "execute",
+ maxContextWindowTokens: MaxContextWindowTokens,
+ maxOutputTokens: MaxOutputTokens),
+ CommandHandlers = HarnessConsoleOptions.BuildDefaultCommandHandlers(agent),
+ });
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/README.md b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/README.md
new file mode 100644
index 0000000000..0d1b109bee
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/README.md
@@ -0,0 +1,51 @@
+# Harness Step 04 — Code Execution (Hyperlight + Skills)
+
+This sample demonstrates a HarnessAgent with **all features enabled**, plus:
+
+- **Hyperlight CodeAct** — sandboxed Python code execution via `execute_code` (requires KVM)
+- **Skills** — file-based skill discovery (a `regex-tester` skill is included)
+
+The agent can plan tasks, manage modes, store memories, read/write files, search the web, approve sensitive operations, discover and use skills, and execute arbitrary Python code — all pre-configured by the HarnessAgent.
+
+## Prerequisites
+
+- .NET 10 SDK
+- An Azure AI Foundry project endpoint
+- KVM-capable host (the Hyperlight sandbox runs code in micro-VMs)
+
+## Environment Variables
+
+| Variable | Description |
+|----------|-------------|
+| `AZURE_AI_PROJECT_ENDPOINT` | Your Azure AI Foundry project endpoint |
+| `AZURE_AI_MODEL_DEPLOYMENT_NAME` | Model deployment name (default: `gpt-5.4`) |
+
+## Running
+
+```bash
+dotnet run
+```
+
+## What to Try
+
+- **Regex testing**: "Help me write a regex that matches valid email addresses, then test it against some examples."
+- **Code execution**: "Calculate the first 20 prime numbers using the Sieve of Eratosthenes."
+- **Skill + code combo**: "I need a regex for ISO 8601 dates — test it thoroughly with edge cases."
+
+## Included Skill
+
+The `skills/regex-tester/` skill instructs the agent to validate regex patterns by executing Python test code in the Hyperlight sandbox. It includes a regex cheatsheet as reference material.
+
+## Features Enabled
+
+| Feature | Description |
+|---------|-------------|
+| TodoProvider | Task planning and tracking (`/todos` command) |
+| AgentModeProvider | Mode switching (`/mode` command) |
+| FileMemoryProvider | Persistent memory stored as files |
+| FileAccessProvider | Read/write files in a working directory |
+| ToolApproval | Don't-ask-again approval for sensitive tools |
+| WebSearch | Built-in hosted web search |
+| AgentSkillsProvider | Discovers and uses skills from the `skills/` folder |
+| HyperlightCodeActProvider | Sandboxed Python execution via `execute_code` |
+| OpenTelemetry | Trace logging to a text file |
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/skills/regex-tester/SKILL.md b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/skills/regex-tester/SKILL.md
new file mode 100644
index 0000000000..7d1c9c49e3
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/skills/regex-tester/SKILL.md
@@ -0,0 +1,36 @@
+---
+name: regex-tester
+description: Validate, test, and debug regular expressions by executing them against sample inputs. Use when asked to build, verify, or explain a regex pattern.
+---
+
+## Usage
+
+When the user asks you to create, validate, or debug a regular expression:
+
+1. **Understand the requirement** — clarify what the pattern should match and what it should reject.
+2. **Consult the cheatsheet** — review `references/regex-cheatsheet.md` for syntax reminders if needed.
+3. **Write and execute test code** — use the `execute_code` tool to run Python code that:
+ - Compiles the regex with `re.compile()`
+ - Tests it against a set of positive examples (should match) and negative examples (should not match)
+ - Extracts and displays any capturing groups
+ - Reports pass/fail for each test case
+4. **Iterate** — if any test fails, refine the pattern and re-run until all cases pass.
+5. **Present the result** — give the user the final pattern, explain what each part does, and show the test results.
+
+## Example Test Script
+
+```python
+import re
+
+pattern = re.compile(r'^[\w.+-]+@[\w-]+\.[\w.-]+$')
+
+positives = ["user@example.com", "first.last+tag@sub.domain.org"]
+negatives = ["@missing.com", "no-at-sign", "spaces in@address.com"]
+
+for s in positives:
+ assert pattern.match(s), f"FAIL: expected match for '{s}'"
+for s in negatives:
+ assert not pattern.match(s), f"FAIL: expected no match for '{s}'"
+
+print("All tests passed!")
+```
diff --git a/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/skills/regex-tester/references/regex-cheatsheet.md b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/skills/regex-tester/references/regex-cheatsheet.md
new file mode 100644
index 0000000000..342719673a
--- /dev/null
+++ b/dotnet/samples/02-agents/Harness/Harness_Step04_CodeExecution/skills/regex-tester/references/regex-cheatsheet.md
@@ -0,0 +1,97 @@
+# Regex Quick Reference (Python `re` module)
+
+## Character Classes
+
+| Pattern | Matches |
+|---------|---------|
+| `.` | Any character except newline |
+| `\d` | Digit `[0-9]` |
+| `\D` | Non-digit |
+| `\w` | Word character `[a-zA-Z0-9_]` |
+| `\W` | Non-word character |
+| `\s` | Whitespace `[ \t\n\r\f\v]` |
+| `\S` | Non-whitespace |
+| `[abc]` | Any of a, b, or c |
+| `[^abc]`| Any character except a, b, c |
+| `[a-z]` | Range: a through z |
+
+## Quantifiers
+
+| Pattern | Meaning |
+|---------|---------|
+| `*` | 0 or more (greedy) |
+| `+` | 1 or more (greedy) |
+| `?` | 0 or 1 (greedy) |
+| `{n}` | Exactly n |
+| `{n,}` | n or more |
+| `{n,m}` | Between n and m |
+| `*?`, `+?`, `??` | Non-greedy versions |
+
+## Anchors
+
+| Pattern | Meaning |
+|---------|---------|
+| `^` | Start of string (or line with `re.MULTILINE`) |
+| `$` | End of string (or line with `re.MULTILINE`) |
+| `\b` | Word boundary |
+| `\B` | Non-word boundary |
+
+## Groups and Backreferences
+
+| Pattern | Meaning |
+|---------|---------|
+| `(...)` | Capturing group |
+| `(?:...)`| Non-capturing group |
+| `(?P...)` | Named group |
+| `\1` | Backreference to group 1 |
+| `(?=...)` | Positive lookahead |
+| `(?!...)` | Negative lookahead |
+| `(?<=...)` | Positive lookbehind |
+| `(?\d{4})-(?P\d{2})-(?P\d{2})', "2025-01-15")
+m.group('year') # '2025'
+
+# Replace
+re.sub(r'\d+', 'X', "abc 123 def") # 'abc X def'
+
+# Split
+re.split(r',+', "a,b,,c") # ['a', 'b', 'c']
+
+# Compile for reuse
+pattern = re.compile(r'^\d{4}-\d{2}-\d{2}$')
+pattern.match("2025-01-15") # Match object
+```