// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Linq;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Agents.AI.Hyperlight.Internal;
using Microsoft.Extensions.AI;
namespace Microsoft.Agents.AI.Hyperlight;
///
/// Standalone execute_code backed by a
/// Hyperlight sandbox. Use this for manual/static wiring when an
/// lifecycle is not needed — for example
/// when the tool registry and capability configuration are fixed for the
/// lifetime of the agent.
///
///
/// Unlike , this type does not hook
/// into the pipeline. It captures a single
/// snapshot of the provided
/// at construction time and reuses it for the lifetime of the instance.
/// The instance can be passed directly anywhere an
/// is accepted; when the configuration requires approval (per
/// or because a
/// configured tool is itself an ),
/// the instance surfaces an via
/// , which is how the rest of
/// the framework discovers approval requirements.
///
public sealed class HyperlightExecuteCodeFunction : AIFunction, IDisposable
{
private const string ExecuteCodeName = "execute_code";
private static readonly JsonElement s_schema = JsonDocument.Parse(
"""
{
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "Code to execute using the provider's configured backend/runtime behavior."
}
},
"required": ["code"]
}
""").RootElement;
private readonly SandboxExecutor _executor;
private readonly SandboxExecutor.RunSnapshot _snapshot;
private readonly string _description;
private readonly bool _approvalRequired;
private ApprovalRequiredAIFunction? _approvalProxy;
private bool _disposed;
///
/// Initializes a new instance of the class.
///
///
/// Optional configuration options. When the defaults of
/// are used.
///
public HyperlightExecuteCodeFunction(HyperlightCodeActProviderOptions? options = null)
{
var effective = options ?? new HyperlightCodeActProviderOptions();
this._executor = new SandboxExecutor(effective);
var tools = (effective.Tools?.Where(t => t is not null) ?? []).ToList();
var fileMounts = (effective.FileMounts?.Where(m => m is not null) ?? []).ToList();
var allowedDomains = (effective.AllowedDomains?.Where(d => d is not null) ?? []).ToList();
this._snapshot = new SandboxExecutor.RunSnapshot(tools, fileMounts, allowedDomains, effective.HostInputDirectory);
this._description = InstructionBuilder.BuildExecuteCodeDescription(
this._snapshot.Tools,
this._snapshot.FileMounts,
this._snapshot.AllowedDomains,
hasHostInputDirectory: !string.IsNullOrEmpty(this._snapshot.HostInputDirectory));
this._approvalRequired = HyperlightCodeActProvider.ComputeApprovalRequired(effective.ApprovalMode, this._snapshot.Tools);
}
///
public override string Name => ExecuteCodeName;
///
public override string Description => this._description;
///
public override JsonElement JsonSchema => s_schema;
///
/// Builds a CodeAct instruction string describing the available tools and capabilities.
///
///
/// When , the instructions assume tools are only accessible
/// through CodeAct (via call_tool). When , the instructions
/// are abbreviated for cases where the same tools are already visible to the model as
/// direct agent tools.
///
public string BuildInstructions(bool toolsVisibleToModel = false)
{
this.ThrowIfDisposed();
return InstructionBuilder.BuildContextInstructions(toolsVisibleToModel);
}
///
public override object? GetService(Type serviceType, object? serviceKey = null)
{
if (serviceKey is null
&& this._approvalRequired
&& serviceType == typeof(ApprovalRequiredAIFunction))
{
return this._approvalProxy ??= new ApprovalRequiredAIFunction(this);
}
return base.GetService(serviceType, serviceKey);
}
///
protected override async ValueTask