Optimizely.Opal.AgentSdk 0.7.0

Prefix Reserved
dotnet add package Optimizely.Opal.AgentSdk --version 0.7.0
                    
NuGet\Install-Package Optimizely.Opal.AgentSdk -Version 0.7.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="Optimizely.Opal.AgentSdk" Version="0.7.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Optimizely.Opal.AgentSdk" Version="0.7.0" />
                    
Directory.Packages.props
<PackageReference Include="Optimizely.Opal.AgentSdk" />
                    
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 Optimizely.Opal.AgentSdk --version 0.7.0
                    
#r "nuget: Optimizely.Opal.AgentSdk, 0.7.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 Optimizely.Opal.AgentSdk@0.7.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=Optimizely.Opal.AgentSdk&version=0.7.0
                    
Install as a Cake Addin
#tool nuget:?package=Optimizely.Opal.AgentSdk&version=0.7.0
                    
Install as a Cake Tool

Optimizely.Opal.AgentSdk

Invoke Opal agents from .NET — run them one-shot or stream their output, hold multi-turn conversations, trigger workflows, browse and install agents from the Agent Directory, and manage Virtual Teammates.

Status: 0.7.0, not yet on NuGet. All eight namespaces are implemented: Agents (Specialized / Workflow / Catalog), Executions (including socket.io subscribe), Skills, Canvas, Memories, Pats, Directory, and Teammates.

Targets net8.0. On the same version line as the Python, TypeScript and Java Opal Agent SDKs.

Install

dotnet add package Optimizely.Opal.AgentSdk

Quickstart

using Optimizely.Opal.AgentSdk;
using Optimizely.Opal.AgentSdk.Auth;

// Instance and customer identity are read from the PAT's own claims —
// nothing else to configure.
await using var client = new OpalClient(new PatAuth(Environment.GetEnvironmentVariable("OPAL_PAT")!));

var result = await client.Agents.Specialized.RunAsync(
    "your-agent-id",
    new Dictionary<string, object?> { ["query"] = "Where is order #1234?" });

Console.WriteLine(result.OutputText);

OpalClient implements IAsyncDisposable, so await using closes the HTTP and socket.io transports for you.

Authentication

The SDK authenticates with a Personal Access Token. Create one in the Opal UI under Settings → Developer → Personal Access Tokens; the secret is shown once at creation, so copy it then.

var client = new OpalClient(new PatAuth(pat));

A PAT is scoped to one Opal instance, and optionally to specific product connections, at creation time. The SDK reads that scope from the token, so a client built from a PAT already knows which instance it is talking to.

Configuration

Pass an OpalConfig to override anything. OpalConfig.FromPat keeps the identity discovered from the token and lets you change the rest:

var config = OpalConfig.FromPat(pat, new OpalConfig
{
    BaseUrl = "https://opal.optimizely.com",
    Timeout = TimeSpan.FromSeconds(60),
    RetryMaxAttempts = 3,
});

await using var client = new OpalClient(new PatAuth(pat), config);
Property Default Purpose
BaseUrl OpalConfig.DefaultBaseUrl Opal API endpoint
WsUrl / WsPath derived from BaseUrl socket.io endpoint for streaming
InstanceId / CustomerId from the PAT override the token's scope
ProductInstances from the PAT product connections to act against
Timeout 30s per-request timeout
RetryMaxAttempts 3 retries on transient failures
VerifySsl true set false only for local development

Specialized agents

RunAsync aggregates the whole execution into one result:

var result = await client.Agents.Specialized.RunAsync("agent-id",
    new Dictionary<string, object?> { ["query"] = "Summarise last week's tickets" });

Console.WriteLine(result.OutputText);
Console.WriteLine(result.ExecutionId);

Stream yields events as they arrive, as an IAsyncEnumerable:

await foreach (var e in client.Agents.Specialized.Stream("agent-id", parameters))
{
    if (e.EventType == "response_chunk")
        Console.Write(e.ResponseChunkText());
}

Cancellation stops your side of the stream. It does not stop the agent — use CancelAsync for that.

Multi-turn chat

ChatStream returns a session that carries the conversation memory between turns, so you do not manage memory_id yourself:

var chat = client.Agents.Specialized.ChatStream("agent-id");

await foreach (var e in chat.Send("What changed in the Q3 report?"))
{
    if (e.EventType == "response_chunk") Console.Write(e.ResponseChunkText());
}

await foreach (var e in chat.Send("And who approved it?"))
{
    if (e.EventType == "response_chunk") Console.Write(e.ResponseChunkText());
}

Workflow agents

var execution = await client.Agents.Workflow.TriggerAsync("workflow-agent-id", "Run the weekly digest");

await foreach (var e in client.Executions.SubscribeAsync(execution.ExecutionId))
{
    Console.WriteLine(e.EventType);
}

Execution history

var page = await client.Executions.ListAsync();

await foreach (var summary in client.Executions.IterAsync())
{
    Console.WriteLine($"{summary.ExecutionId} {summary.Status}");
}

var log = await client.Executions.GetAsync(executionId);

Agent Directory

client.Agents is this instance's own catalog. client.Directory is the Agent Directory — the published, cross-region catalog you browse and install from.

var tags = await client.Directory.TagsAsync(new ListDirectoryTagsOptions { Limit = 10 });

var page = await client.Directory.BrowseAsync(new BrowseDirectoryOptions
{
    Tags = ["reporting"],
    Search = "digest",
    PageSize = 20,
});

foreach (var agent in page.Items)
{
    // Browse fills in install state for THIS instance, so one listing answers
    // both "what exists" and "what do I already have".
    Console.WriteLine($"{agent.AgentId} installed={agent.IsInstalled} v{agent.InstalledVersion}");
}

var detail = await client.Directory.GetAsync("weekly-digest");
var result = await client.Directory.InstallAsync("weekly-digest");

foreach (var update in await client.Directory.CheckUpdatesAsync())
{
    await client.Directory.InstallAsync(update.AgentId);  // applying one is just an install
}

Install does not overwrite by default. Installing over an existing installation throws on HTTP 409 unless you pass ForceUpdate = true. With it, local edits to an installed agent are overwritten with no undo.

Set UserId when you have it. Without it the server records the installer as null and creates the imported agent as the literal user "system".

The target instance is never a parameter — it comes from OpalConfig, and the server rejects a mismatch.

Other namespaces

Namespace Purpose
client.Skills create, search and update skills
client.Canvas read canvas artifacts produced by an execution
client.Memories inspect and manage conversation memories
client.Pats manage Personal Access Tokens

Error handling

Every failure surfaces as an OpalException subclass carrying Status, Code and RequestId:

try
{
    var result = await client.Agents.Specialized.RunAsync("agent-id", parameters);
}
catch (OpalAuthException)          { /* 401/403 — token invalid, expired or revoked */ }
catch (OpalNotFoundException)      { /* 404 — no such agent or execution */ }
catch (OpalRateLimitException ex)  { /* 429 — honour ex.RetryAfter */ }
catch (OpalConcurrencyException)   { /* 409 — ETag conflict on a concurrent update */ }
catch (OpalServerException)        { /* 5xx */ }
catch (OpalConnectionException)    { /* network or socket.io transport failure */ }

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  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.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.7.0 40 9/15/2026
0.6.0 83 9/11/2026