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
<PackageReference Include="Optimizely.Opal.AgentSdk" Version="0.7.0" />
<PackageVersion Include="Optimizely.Opal.AgentSdk" Version="0.7.0" />
<PackageReference Include="Optimizely.Opal.AgentSdk" />
paket add Optimizely.Opal.AgentSdk --version 0.7.0
#r "nuget: Optimizely.Opal.AgentSdk, 0.7.0"
#:package Optimizely.Opal.AgentSdk@0.7.0
#addin nuget:?package=Optimizely.Opal.AgentSdk&version=0.7.0
#tool nuget:?package=Optimizely.Opal.AgentSdk&version=0.7.0
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 */ }
Links
License
MIT
| Product | Versions 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. |
-
net8.0
- SocketIOClient (>= 4.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.