Universal.Operative.Sdk 1.3.0

dotnet add package Universal.Operative.Sdk --version 1.3.0
                    
NuGet\Install-Package Universal.Operative.Sdk -Version 1.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Universal.Operative.Sdk" Version="1.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Universal.Operative.Sdk" Version="1.3.0" />
                    
Directory.Packages.props
<PackageReference Include="Universal.Operative.Sdk" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Universal.Operative.Sdk --version 1.3.0
                    
#r "nuget: Universal.Operative.Sdk, 1.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Universal.Operative.Sdk@1.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Universal.Operative.Sdk&version=1.3.0
                    
Install as a Cake Addin
#tool nuget:?package=Universal.Operative.Sdk&version=1.3.0
                    
Install as a Cake Tool

About

Universal.Operative.Sdk is a provider-neutral harness for running agentic LLM turns: stream a model response, dispatch any tool calls it produces, feed the results back, and loop until the model stops. It defines the domain model (Conversation, Message, content Blocks), the extension seams (IModel, ITool, ITransformer), and a ready-to-use Operative implementation — all independent of any specific provider. Pair it with Universal.Operative.Sdk.Anthropic or Universal.Operative.Sdk.OpenAI for a concrete IModel.

How to Use

Installation

dotnet add package Universal.Operative.Sdk

Quick start

using Universal.Operative.Sdk;

IModel model = /* an IModel, e.g. from Universal.Operative.Sdk.Anthropic or .OpenAI */;

IOperative operative = OperativeBuilder.FromDefaults(model)
    .AddBaselineTools()   // Read, Write, Edit, Glob, Grep, a shell, TodoWrite, NotebookEdit
    .Build();

var conversation = new Conversation
{
    System = new List<Block> { new TextBlock("You are a helpful assistant.") },
    Messages = { new Message { Role = MessageRoles.User, Content = { new TextBlock("List the files in this repo.") } } },
};

await foreach (OperativeEvent e in operative.ExecuteAsync(conversation))
{
    switch (e)
    {
        case ModelStreamEvent s: /* incremental provider events, for live UI */ break;
        case AssistantMessageEvent a: Console.WriteLine(a.Message.Content); break;
        case ToolCallStartedEvent t: Console.WriteLine($"Running {t.Call.Name}..."); break;
        case TurnCompletedEvent done: Console.WriteLine($"Done: {done.Reason}"); break;
    }
}
// conversation.Messages now holds the full transcript, including tool calls and results.

OperativeBuilder.FromDefaults wires a sensible pre-transformer stack (boundary-aware history windowing, tool-result budgeting, history snipping, auto-compaction) so long-running sessions don't blow the model's context window. Use new OperativeBuilder() instead if you want to opt into transformers individually.

Core concepts

  • Conversation / Message / Block — the provider-neutral transcript. Message.Content is a list of Blocks (TextBlock, ToolCallBlock, ToolResultBlock, …); round-tripping a Conversation through JSON is enough to resume it later.
  • IModel — the seam to a concrete provider. Implement StreamAsync to translate a Conversation into a provider request and its response stream into StreamEvents; CompleteAsync is provided for free as a fold over the stream.
  • ITool / Tool<TArgs> — a callable the model can invoke. Subclass Tool<TArgs> with a typed argument record and a hand-authored JSON-Schema Definition; the base class handles (de)serialization and turns malformed arguments into a model-readable error instead of an exception. See Tools/BaselineTools.cs and Tools/FileSystem/GlobTool.cs for worked examples.
  • ITransformer — rewrites the conversation (or shared IContext) around a model call — compaction, tool-result pruning, history snipping. Operative runs four stages: BeforeTurnTransformers once before the turn's first iteration, BeforeIterationTransformers before each model call, AfterIterationTransformers after the assistant message lands but before tools run, and AfterTurnTransformers once after the loop ends. (PreTransformers/PostTransformers still work as obsolete aliases for the before/after-iteration stages.)
  • Operative / IOperative — drives the loop described above. Every step is a protected virtual method, so subclass to override one piece (tool-result formatting, dispatch policy, timeouts) without reimplementing the whole turn.
  • OperativeEvent — the single observable channel for a turn: model stream events, the settled assistant message, tool start/complete/error events, the synthesized tool-result message, transformer applications, and a terminal TurnCompletedEvent.

Writing a custom tool

using System.Text.Json;
using System.Text.Json.Serialization;
using Universal.Operative.Sdk.Tools;

public sealed class WeatherTool : Tool<WeatherTool.Args>
{
    public override string Name => "get_weather";
    public override bool IsConcurrencySafe => true; // read-only calls may run in parallel with others

    public override ToolDefinition Definition { get; } = new()
    {
        Name = "get_weather",
        Description = "Get the current weather for a city.",
        Parameters = JsonDocument.Parse("""
            { "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
            """).RootElement.Clone(),
    };

    public sealed record Args
    {
        [JsonPropertyName("city")] public string City { get; init; } = string.Empty;
    }

    protected override async Task<ToolResult> ExecuteAsync(Args args, CancellationToken cancellationToken)
        => ToolResult.Json(new { city = args.City, tempF = 72 });
}

Register it with builder.AddTool(new WeatherTool()) and the harness auto-publishes its Definition on outbound messages and dispatches calls to it by name.

License

Free for noncommercial use under the Universal.Operative Noncommercial License. Source is closed; commercial use requires a separate license from Andrew Ong.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Universal.Operative.Sdk:

Package Downloads
Universal.Operative.Sdk.Anthropic

Anthropic extensions for the Universal.Operative.Sdk.

Universal.Operative.Sdk.OpenAI

OpenAI extensions for Universal.Operative.Sdk.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.3.0 96 7/6/2026
1.2.0 92 7/6/2026
1.1.0 95 7/4/2026
1.0.3 102 7/4/2026
1.0.2 98 7/4/2026
1.0.1 108 7/4/2026
1.0.0 123 7/3/2026

Added SystemPromptTransformer for refreshing the system prompt from a caller-supplied source (with a SystemPromptTransformer.FromFile convenience factory). Added OperativeBuilder.ReplaceTransformer/RemoveTransformer(s)/AddTransformerBefore/AddTransformerAfter for editing the transformer pipeline by type instead of hand-rolling find-remove-add against the raw lists. Added FileSystemConversationPersistenceTransformer (wired via OperativeBuilder.WithFileSystemConversationPersistence) for durable, rotating, size-capped conversation snapshots to local disk, with LoadLatestConversationAsync for restoring the latest one at host startup.