Skip to content

0.9.0-preview.12

Pre-release
Pre-release

Choose a tag to compare

@Blind-Striker Blind-Striker released this 17 Oct 16:36
7bb2fae

Streams and automatic traversals take per-call options, and the public surface sheds its last
loose names before 1.0.
SubscribeAsync, ReadLogAsync (formerly GetLogAsync),
EnumerateMessagesAsync and EnumerateSessionsAsync accept OpenCodeRequestOptions, so a
per-call Location reaches every connection attempt and page request, and
EventStreamIdleTimeout sets the event bus's idle window. Query parameters take the types the
server reads (bool?, int?, long?), generated enums work in switch and patterns through
their Values constants, assistant content has one union type, PTY sizes share PtySize, and
OpenCodeRoutes is internal. Cancellation reports your own token at every public async door,
disposing the root client interrupts calls and streams with ObjectDisposedException, readiness
timeouts keep an outcome recorded before them, IntelliSense states only what the API declares, and
on .NET Framework and netstandard2.0 response bodies up to 16 MiB are pooled.

💥 Breaking changes

  • Streams and automatic cursor traversals take per-call options before cancellation.
    These four virtual method signatures change; recompile consumers and update overrides.
    Name a token as cancellationToken: token when omitting the new options argument.
    Calls that omit options keep their behavior. Streams and traversals refuse NoThrow;
    the event bus also refuses a per-call NetworkTimeout, including Timeout.InfiniteTimeSpan.

    Door Was Now
    EventsClient.SubscribeAsync (CancellationToken cancellationToken = default) (OpenCodeRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
    SessionClient.ReadLogAsync (SessionLogRequest? request = null, CancellationToken cancellationToken = default) (SessionLogRequest? request = null, OpenCodeRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
    SessionClient.EnumerateMessagesAsync (SessionMessageListRequest? request = null, CancellationToken cancellationToken = default) (SessionMessageListRequest? request = null, OpenCodeRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
    SessionsClient.EnumerateSessionsAsync (SessionListRequest? request = null, CancellationToken cancellationToken = default) (SessionListRequest? request = null, OpenCodeRequestOptions? requestOptions = null, CancellationToken cancellationToken = default)
  • Invalid per-call options name requestOptions in exceptions. An undefined ErrorBehavior
    on a one-shot call now reports ArgumentOutOfRangeException.ParamName as "requestOptions"
    rather than "options", and the new stream and traversal refusals name "requestOptions" too.

  • IOpenCodeStreamFailureCause exposes Tag. Every cause in
    OpenCodeStreamFailureException.Causes exposes its wire tag without a cast, including an
    unknown cause. Custom test implementations must supply the property. IStreamFailureCause
    inherits it rather than redeclaring it; concrete causes and their wire representation are unchanged.

  • Instruction-entry DTO names retain Put.
    ExperimentalSessionInstructionsEntryRequest is now
    ExperimentalSessionInstructionsEntryPutRequest, and
    ExperimentalSessionInstructionsEntryResponse is now
    ExperimentalSessionInstructionsEntryPutResponse.
    ExperimentalClient.SetSessionInstructionsEntryAsync keeps its method name.

  • The instruction-entry list method names its subject in the plural.
    ExperimentalClient.ListSessionInstructionsEntryAsync is now
    ListSessionInstructionsEntriesAsync, and
    ExperimentalSessionInstructionsEntryListResponse.SessionInstructionsEntry is now
    SessionInstructionsEntries.

  • The session log uses stream-reading terminology.
    SessionClient.GetLogAsync is now ReadLogAsync; its request and stream item types and
    its behavior are unchanged.

  • OpenCodeRoutes is no longer public. Call the typed clients to build and send request
    paths instead of using route constants or builders directly. Request paths on the wire are
    unchanged.

  • Assistant content has one union type. Message content and content-updated events use
    ISessionMessageAssistantContent; the encoded duplicate and its unknown carrier are removed.
    Existing text, reasoning, and tool records implement the surviving interface. The wire is unchanged.

    Surface Was Now
    SessionMessageContentUpdatedData.Content item ISessionMessageAssistantContentEncoded ISessionMessageAssistantContent
    Unknown assistant-content record UnknownSessionMessageAssistantContentEncoded UnknownSessionMessageAssistantContent
  • PTY requests share PtySize. Replace the three old size names with PtySize.
    Its Cols and Rows members remain required long values. The persistent requests remain experimental.
    The pty.update body now writes cols before rows, which the server does not observe.

    Property Was Now
    PtyUpdateRequest.Size PtyUpdateSize? PtySize?
    PersistentPtyCreateRequest.Size PersistentPtyCreateInputSize? PtySize?
    PersistentPtyUpdateRequest.Size PersistentPtyUpdateInputSize PtySize
  • Boolean query parameters use bool?. QueryBoolean is removed. Pass true or false,
    not QueryBoolean.True or QueryBoolean.False; leave the member null to omit it.
    The query text remains lowercase true or false.

    Property Was Now
    SessionLogRequest.Follow QueryBoolean? bool?
    SessionInterruptRequest.Resume QueryBoolean? bool?
    ProjectListRequest.Archived QueryBoolean? bool?
    ExperimentalSessionExportRequest.Sanitize QueryBoolean? bool?
  • Numeric query parameters are numbers. The API description declares them as strings, but the
    server reads each one as a number and upstream's own client takes a number, so the request
    records now do too. Pass Limit = 50, not Limit = "50". The value still goes out as query text,
    and the server still enforces its range.

    Property Was Now
    ListRequest.Limit (SessionListRequest, SessionMessageListRequest) string? int?
    FsFindRequest.Limit string? int?
    VcsBranchListRequest.Limit string? int?
    ShellOutputRequest.Limit string? int?
    ShellOutputRequest.Cursor string? long?
    PersistentPtyReadRequest.Lines string? int?
    SessionLogRequest.After string? long?
    SessionDiffRequest.Context string? int?
    VcsDiffRequest.Context string? int?
    ExperimentalSessionStatsRequest.From, .To (milliseconds since the Unix epoch) string? long?
  • OpenCodeApiException and OpenCodeServerException are sealed. They can no longer be used
    as base classes; their constructors and error data are unchanged.

  • SessionParentFilter is a record with value equality. Filters for the same parent compare
    equal and have the same hash code, so SessionListRequest records built with equal filters
    compare equal too. ToString() identifies RootOnly or Of and includes the parent id for Of.

✨ Added

  • Per-call Location applies to streams and automatic cursor traversals. Pass
    OpenCodeRequestOptions to override the ambient directory on every stream connection attempt
    or every page request. The server still ignores the header on routes that resolve a session's
    location or need no location; it does not filter the global event bus. A per-call
    NetworkTimeout bounds only opening the session log, and bounds each page request separately
    on a traversal, not the whole walk.

  • OpenCodeClientOptions.EventStreamIdleTimeout sets the event bus's idle window, as
    upstream's own client allows. A connection that receives nothing for that long is replaced; the
    default stays 45 seconds. Keep it above the server's 15-second keepalive. Any window above zero
    and at most int.MaxValue milliseconds is accepted, but not Timeout.InfiniteTimeSpan: a bus
    that never times out would hang on a half-open connection. The 2-second connect bound and the
    1-second delay before a new attempt stay fixed.

  • Enum values work in switch and in patterns. Every generated enum carries its pinned wire
    spellings as const string members of a nested Values class, and a Value property that
    returns a value's spelling. Match one against the other in a case label or a property pattern;
    a value a newer server sends matches none of them:

    switch (mode.Value) { case VcsMode.Values.Committed: /* … */ break; }
    if (rule is { Effect.Value: PermissionEffect.Values.Allow }) { /* … */ }

    Nothing else changes: the static properties, ==, IsKnown and the wire format stay as they
    were.

🔧 Changes

  • On .NET Framework and netstandard2.0, response bodies between 1 and 16 MiB are pooled instead
    of allocated on every call. The SDK then typically keeps one buffer per size class it has used,
    at most about 60 MiB after large responses; bodies above 16 MiB still allocate.

🐛 Fixes

  • IntelliSense documents declared units, defaults and family roles. Documentation inferred only
    from server implementation is removed; SDK types and runtime behavior are unchanged.
  • Generated XML summaries use grammatical prose. Articles and brand spellings are corrected,
    digits stay attached to their words, and members named Value read "Gets the value."
  • A background-service start failure no longer carries part of the PTY handoff ticket. The
    contender's stderr tail redacts the ticket even where the 8 KiB cut or the end of the pipe splits
    it, so no fragment of it reaches OpenCodeServerException messages.
  • Caller cancellation preserves your token across public async doors: HTTP, SSE, cursor
    traversal, both terminal families, and OpenCodeServer.StartAsync, DiscoverAsync, EnsureAsync,
    and StopAsync. OperationCanceledException.CancellationToken is the
    token you supplied at either entry point, including the same token at both enumeration entry
    points; two distinct cancellable tokens are observed without an identity guarantee.
  • A caller token already canceled at the launch boundary prevents the server process from starting.
  • Readiness timeouts preserve recorded outcomes: a readiness line, root exit, or stdout end
    recorded before timeout arbitration is reported instead of a timeout. Cancellation arriving only
    during teardown does not replace the selected failure.
  • POSIX pipe-creation failures throw OpenCodeServerException naming the command, with the
    underlying IOException retained.
  • Malformed PTY-handoff sidecar and ticket documents are refused, including trailing garbage
    or a second JSON value; trailing whitespace remains valid.
  • Endpoints with a query, fragment, or user info no longer escape best-effort service shutdown
    and handoff paths,
    so endpoint refusal does not prevent termination or replacement.
  • Disposing the root client interrupts HTTP calls and SSE streams with ObjectDisposedException,
    rather than misreporting a transport timeout. Caller cancellation already observed takes precedence;
    terminal sessions keep their separate ownership and disposal rules.
  • SSE releases its live response on cancellation or client disposal on every target, even while
    your code holds an event. A suspended enumeration reports the exception on its next pull.
  • A stream positioned past its end uploads zero bytes, with a zero Content-Length when it can
    seek, without rewinding or disposing the caller's stream.
  • Malformed PTY cursor control documents are refused, including truncation after the cursor
    value, trailing garbage, or a second JSON value. Trailing whitespace remains valid.
  • AddOpenCode keeps existing client registrations. Calling it again does not add another
    OpenCodeClient or sub-client registration. A consumer's earlier registration of the root client
    or any sub-client wins; missing sub-clients are resolved from the registered root client.