// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Agents.AI.Tools.Shell;
///
/// Pluggable backend that runs shell commands on behalf of a tool.
///
///
///
/// runs commands directly on the host (no
/// isolation; approval-in-the-loop is the security boundary).
/// runs them inside a container with resource
/// limits, network isolation, and a non-root user.
///
///
/// This is an abstract class rather than an interface so the surface can be
/// extended in future versions (e.g., adding new lifecycle hooks) without
/// breaking existing third-party implementations. Mirrors the Python
/// ShellExecutor Protocol in
/// agent_framework_tools.shell._executor_base.
///
///
/// Lifetime: is invoked at most once per
/// instance (idempotent); tears the executor down
/// at the end of its life. There is no public Shutdown step — disposal is the
/// teardown.
///
///
/// Concurrency and session ownership. A single executor instance is
/// intended to serve a single conversation / agent session — i.e., a single
/// user. Stateless mode is safe to share across concurrent callers (each
/// RunAsync spawns a fresh process or container, so there is no
/// shared mutable state). Persistent mode is not shareable: a
/// single long-lived shell process backs every call, it carries mutable
/// state (working directory, exported variables, history, in-flight
/// background jobs) that is visible to every subsequent command, and
/// concurrent commands would interleave on its stdin/stdout. The framework
/// does not isolate one caller's state from another's. Build one executor
/// per session, treat it as owned by that session for its lifetime, and
/// dispose it when the session ends. If you register an executor with a DI
/// container, use a per-request / per-conversation scope, not a singleton.
///
///
public abstract class ShellExecutor : IAsyncDisposable
{
///
/// Eagerly initialize the backend. Idempotent; subsequent calls are
/// no-ops once the executor is started. For stateless executors this is
/// typically a no-op (the default implementation returns
/// ).
///
/// Cancellation token.
public virtual Task InitializeAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
///
/// Run a single command and return its result. Implementations are
/// expected to apply the configured per-command timeout and surface it
/// via + ExitCode = 124.
///
/// The shell command to execute.
/// Cancellation token.
public abstract Task RunAsync(string command, CancellationToken cancellationToken = default);
///
public abstract ValueTask DisposeAsync();
}