Repository navigation
0.9.0-preview.11
Pre-releaseThe event bus reconnects the way upstream's own client does, and the calls that answer only when
their work ends now wait for it. EventsClient.SubscribeAsync replaces a dropped, failed or
silent connection one second later, starts every connection with EventServerConnected, and gives
up after one minute in which every attempt failed, so a wrong address or password surfaces after a
minute rather than at once. NetworkTimeout sets how long a call may go without network progress,
and WaitForSessionAsync, RunShellCommandAsync, RunCommandAsync and GenerateTextAsync wait
with no window. The other breaking changes: generated string enums are extensible value types, the
upstream-experimental surface is [Experimental] and needs an opt-in, and OAuth, shellId and
savedPermissionId are spelled as they should be. Events that belong to a session implement
ISessionScopedEvent, the generated XML documentation says what each record is, the guides carry
the fixes from the preview.10 evaluation, and on Windows the background service's sidecar
publishes even while another process reads it.
💥 Breaking changes
-
EventsClient.SubscribeAsyncreconnects, as upstream's client does.- A connection that drops, fails, or receives nothing for 45 seconds is replaced one second
later. Every failure counts: an HTTP error status, a malformed frame, a failure frame and the
server ending the stream included. - Each connection has 2 seconds to answer and deliver its first event, so a server consistently
slower than that fails every attempt. - Each connection starts with
EventServerConnected, and the SDK fails one that does not. A
second one means the events in between were missed. - The stream no longer ends on its own. Your token, disposing the client, or one minute in which
every attempt failed ends it; the minute throws the last failure. - What changes for you: a wrong address or a refused password surfaces after one minute, not at
once, and code that expected the stream to end when the server went away now sees a new
EventServerConnectedinstead.OpenCodeClientOptions.NetworkTimeoutno longer reaches the
bus.
- A connection that drops, fails, or receives nothing for 45 seconds is replaced one second
-
The upstream-experimental surface is marked
[Experimental]. Every member that calls an
operation upstream serves under/api/experimental/, and every type only those operations use,
now reports a diagnostic when you use it. It is a compile error until you opt in with
#pragma warning disableor<NoWarn>for its id:OPENCODE001:client.Experimental(ExperimentalClient);OPENCODE002: the persistent-PTY family (client.PersistentPtys, its handle, connect door,
session, options and frames);OPENCODE003: the Session family's experimental operations, todaySessionClient.GetLogAsync.
Nothing on the wire changes. See Experimental APIs. Waiting for a
turn on the event bus, as the getting-started guide shows, needs no opt-in. -
Every generated name spells
OAuthas the brand does. Names built from operation ids and wire
members saidOauth, beside the schema-derivedCredentialOAuthandMcpOAuthConfig. They are now
BeginOAuthConnectionAsync,CancelOAuthConnectionAsync,CompleteOAuthConnectionAsync,
GetOAuthStatusAsync, theIntegrationOAuth*request and response types with their
OAuthConnectandOAuthStatusmembers,McpRemoteConfigOAuth(with itsKind), the
OpenCodeRoutesbuilders and templates, and theOAuthvalue of
ConnectionCredentialInfoMethod. Rename the uses; nothing on the wire changes. -
The shell and saved-permission ids have descriptive parameter names.
ShellsClient.GetShellClient, and theOpenCodeRoutes.Shellsbuilders, takeshellId.PermissionsClient.RemoveSavedAsyncandOpenCodeRoutes.Permissions.RemoveSavedtake
savedPermissionId.- Before, both were a bare
id. Only a call that named the argument (id: …) changes.
-
Generated string enums are extensible value types (ADR-0035).
-
Every enum the API defines, such as
VcsMode,PermissionEffect,SessionInboxDeliveryand
AgentInfoMode, is now areadonly struct, not a C#enum. -
Previously a server newer than the SDK broke reading whenever it sent a value the SDK did not
know, on a response or on an event stream frame. That value now reads as it was sent, and it is
written back unchanged. -
The pinned values are static properties with the same names, so
VcsMode.Committedstill
compiles. -
IsKnownreports whether this SDK's pin defines a value.new VcsMode("…"), the implicit
conversion fromstring, andFromStringcreate a value from any spelling. -
Equality is ordinal.
-
ToString()now returns the wire spelling (running), not the C# member name (Running).
Logs and string comparisons built on it change. -
A
defaultvalue has no spelling. A request body or query refuses it. -
What breaks: enum values used as constant patterns, and code that relied on
enumbehaviour
(numeric casts,Enum.GetValues,[Flags]). Compare with==instead:// Before switch (mode) { case VcsMode.Committed: /* … */ break; } if (rule is { Effect: PermissionEffect.Allow }) { /* … */ } // After if (mode == VcsMode.Committed) { /* … */ } if (rule.Effect == PermissionEffect.Allow) { /* … */ }
-
✨ Added
-
ISessionScopedEventroutes an event by its session. Every event whose payload names the
session it happened in (58 at the pin: thesession.*events,permission.asked/replied,
persistent-pty.added/removedandtui.session.select) implements it, so
e is ISessionScopedEvent { SessionId: var id }replaces a switch over every type. The member is
implemented explicitly, so the events gain no public member and serialize as before. The form
events are not session-scoped: a form's session id can be"global". -
NetworkTimeoutsets how long a call may go without network progress.
OpenCodeClientOptions.NetworkTimeoutsets it for the client and
OpenCodeRequestOptions.NetworkTimeoutfor one call; the default stays 100 seconds.
WaitForSessionAsync,RunShellCommandAsync,RunCommandAsyncand bothGenerateTextAsync
methods (onSessionClientandExperimentalClient) answer only when their work ends, so they
now wait with no window and the call's cancellation token is their deadline; before, each failed
after 100 seconds without headers, and a shell command kept running on the server. A client-wide
value does not reach them; a per-call value does.OpenCodeServer.CreateClientcarries the
client value over. See Requests.
🔧 Changes
- Generated XML documentation says what each record is. The pinned API describes almost none of
its schemas, so summaries now come from the wire: an event record names its event type, its
payload names the event it belongs to, an operation's body, response and query records name the
operation with its method and route, and an error record names its tag. The types builders found
hardest carry hand-written summaries from upstream's behaviour: assistant messages and their
finish, error and retry; text deltas and their end; the prompt and create requests; the execution
events; and a PTY's command and arguments, including which shell starts when the command is
absent. - The package README links open the docs of the release they came with. On NuGet.org, the
links to the guides point at the commit the package was built from, not atmaster. - The hosting internals join the flat
OpenCode.Sdk.Internalnamespace. The public API is
unchanged, byte for byte; only internal type names differ, as a stack trace or reflection shows
them.
🐛 Fixes
- An event stream no longer ends on an enum value the SDK does not know. Before, a frame with
such a value, such as a newreasononsession.execution.interrupted, failed to materialize and
ended the subscription withOpenCodeTransportException. The frame now reads as its typed event,
with the value kept (IsKnownis false). One-shot responses read the same way. See the Breaking
entry on extensible enums. - On Windows, publishing the background service's sidecar no longer fails while another process
reads it. The sidecar was replaced with a rename that Windows refuses with access denied while a
reader holds the file open. It is now replaced with a POSIX-semantics rename, which succeeds even
then, and falls back to the previous rename where the file system does not support it.
📚 Documentation
- The streaming guide streams an answer as it arrives. The recipe follows upstream's own client:
- wait for
EventServerConnectedbefore you prompt; - prompt with your own
msg_id, and match it toSessionInboxDelivered'sInboxId; - append
SessionTextDeltatext, and replace it withSessionTextEnded's finalText; - stop on the session's execution event.
- wait for
- The requests guide names the session-create location.
SessionCreateRequest.Locationis a
bodyLocationPublicRef, not theLocationSelectorthat headers and queries use. The guide also
shows that a collection expression cannot fill anOptionalcollection member (CS9174). - The terminals guide documents the two normal-PTY gaps. It says which shell a PTY gets without a
Command: the configuredshell, then$SHELL, then a per-OS fallback. It also shows how to
resize a normal PTY, throughPtyClient.UpdateAsyncwithSize. - The connection guide keeps a launched server around a host, and fixes the 401.
- A launched server stays in
await usingaround the host'sRunAsync. The container does not
dispose a handle you register. - A passwordless 401 carries the typed
UnauthorizedError, not an empty body.
- A launched server stays in
- The guides wait for a turn on the execution events, not on
SessionIdle. The pinned server
never publishessession.idleorsession.status, so a loop that waited forSessionIdlenever
ended. A turn ends with exactly one ofSessionExecutionSucceeded,SessionExecutionFailedor
SessionExecutionInterrupted.
📋 Important Notes
- This release is built on the .NET 11 release-candidate SDK. A preview package may build on a
go-live release candidate (ADR-0002); the first non-preview package waits for the GA SDK. The
net11.0asset needs the .NET 11 runtime, and anet10.0app keeps using thenet10.0asset.