diff --git a/.editorconfig b/.editorconfig index 0a38ce15..4900700c 100644 --- a/.editorconfig +++ b/.editorconfig @@ -821,7 +821,7 @@ dotnet_diagnostic.CA1034.severity = none # The pinned OpenAPI schemas declare these wire members as plain strings without # format: uri. Winner: the sole protocol input's declared type; CA1056's name # inference remains enabled for every other property. -[src/OpenCode.Sdk/**/{ConfigInfoEnterprise,ConnectionStatus,ExperimentalIntegrationWellknownAddRequest,FormExternalField,IntegrationAttempt,McpOAuthConfig,McpRemoteConfig,McpResource,McpResourceTemplate,ProjectIcon,PromptFileSourceUri,PromptInputFileAttachment,ToolFileContent,WebSearchResult}.cs] +[src/OpenCode.Sdk/**/{ConfigInfoEnterprise,ConnectionStatus,ExperimentalIntegrationWellknownAddRequest,FormExternalField,IntegrationAttempt,McpOAuthConfig,McpRemoteConfig,McpResource,McpResourceTemplate,ProjectIcon,PromptFileSourceUri,PromptInputFileAttachment,ToolFileContent,VoiceWebSocketTransport,WebSearchResult}.cs] dotnet_diagnostic.CA1056.severity = none # The SDK's namespaces are API surface, not placement: a public type declares OpenCode.Sdk or diff --git a/CHANGELOG.md b/CHANGELOG.md index 7bade68a..14a541bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,22 @@ Nightly builds of `master` are on [GitHub Packages](README.md#nightly-builds-github-packages) as `0.9.0-nightly.{yyyyMMdd}.g{shortSha}`. +### ✨ Added + +- **The SDK speaks opencode 2.0.32.** `ExperimentalClient.CreateVoiceSessionAsync` opens an + experimental voice session over WebRTC or WebSocket. `ConfigModelCapabilities.Reasoning` tells a + model that does not reason (`ConfigModelReasoningUnsupported`) from one that does + (`ConfigModelReasoningSupported`, with its toggle, efforts and `ConfigModelReasoningBudget`); + `ConfigModelCapabilities.Temperature` and `ModelCapabilities.Temperature` say whether a model + takes a temperature, and `ConfigModel.ReleaseDate` carries its release date. + +### 🔧 Changes + +- **A spawned background service's saved `env` wins after startup.** + `OpenCodeServerEnsureOptions.Environment` entries still win over the channel's service-config + `env` at spawn, but a 2.0.32 service re-applies its saved `env` when it starts, so a key set in + both takes the saved value for everything the service reads after startup. + ### 🐛 Fixes - **IntelliSense reaches implementing members and restores declared halves.** Properties that diff --git a/docs/architecture/protocol-and-generation.md b/docs/architecture/protocol-and-generation.md index 74260a3a..e03c9b08 100644 --- a/docs/architecture/protocol-and-generation.md +++ b/docs/architecture/protocol-and-generation.md @@ -1,6 +1,6 @@ # Protocol and Generation Architecture -Date: 2026-10-17 +Date: 2026-10-18 Canonical current rules for the protocol surface, generator, generated models, and runtime materialization boundary. ADRs record why these decisions were made; dated research records the @@ -122,7 +122,9 @@ evidence and may contain superseded positions. `.generation-incomplete` marker, each carrying the bindability the same selection-path binder finds today — `[bindable]`, or `[refused: ]` — so wall-free drift among pending operations surfaces as a reviewed diff instead of accumulating silently; the marker also - lists the fingerprint-pinned transport-owned operations beside them while it exists. + lists the fingerprint-pinned transport-owned operations beside them while it exists. Selected + operations are listed in `tools/generation-profile.txt`; admitting an upstream operation adds its + id there, along with its naming and owner curation rows. ## Curation boundary diff --git a/docs/guide/connection-modes.md b/docs/guide/connection-modes.md index 66b34576..e82139e9 100644 --- a/docs/guide/connection-modes.md +++ b/docs/guide/connection-modes.md @@ -1,6 +1,6 @@ # 🔌 Connection modes -Date: 2026-10-16 +Date: 2026-10-18 There are three ways to get a client bound to a running opencode server: **let the SDK start one**, **point it at one you already have**, or **discover the background service the opencode CLI runs @@ -443,7 +443,7 @@ What it does: | `ExpectedVersion` | The version a reused service must report. Null accepts any ready, compatible service. | | `VersionPolicy` | What a service at another version gets: `Ignore` (the default) reuses it, `Replace` ends it and starts one at the expected version, `Error` throws without touching it. `Replace` and `Error` need `ExpectedVersion`. | | `Command` | The service command, `opencode serve --service` by default. The first entry is resolved the way `StartAsync` resolves it. | -| `Environment` | Extra variables for a spawned service, over the channel's service-config `env`. | +| `Environment` | Extra variables for a spawned service, over the channel's service-config `env` at spawn; a 2.0.32 service re-applies its saved `env` over them once it starts, so a key set in both takes the saved value for everything read after startup: set a key in one place, the caller's `Environment` or the saved `env`, not both. | | `OnStart` | Called at most once, before a new service process starts, with the reason (`Missing` or `VersionMismatch`) and the previous version. | The spawned command registers where its own build and environment say. A released `opencode` build diff --git a/external/opencode b/external/opencode index 042d0832..843a6c51 160000 --- a/external/opencode +++ b/external/opencode @@ -1 +1 @@ -Subproject commit 042d0832f5f3a73eaa039a58a7d565776f6d4290 +Subproject commit 843a6c514e606b436a7fe713422ace0a93ad3d61 diff --git a/spec/SNAPSHOT.md b/spec/SNAPSHOT.md index 36f16517..5ebfc3a0 100644 --- a/spec/SNAPSHOT.md +++ b/spec/SNAPSHOT.md @@ -1,6 +1,6 @@ # OpenAPI Snapshot -Date: 2026-10-16 +Date: 2026-10-17 `openapi.json` is the accepted snapshot of the upstream opencode OpenAPI 3.1 document (ADR-0005). The SDK is built against this snapshot, never against a live branch; the pin is @@ -13,13 +13,13 @@ their hash-pinned manifests. | Fact | Value | |---|---| | Upstream file | `packages/protocol/openapi.json` | -| Upstream release tag | `v2.0.29` | -| Commit | `042d0832f5f3a73eaa039a58a7d565776f6d4290` | -| Upstream product channel | npm `@opencode/cli@latest` (channel `latest`); this tag published as `2.0.29`, installing the `opencode` command (plus the transitional `opencode2` alias) | +| Upstream release tag | `v2.0.32` | +| Commit | `843a6c514e606b436a7fe713422ace0a93ad3d61` | +| Upstream product channel | npm `@opencode/cli@latest` (channel `latest`); this tag published as `2.0.32`, installing the `opencode` command (plus the transitional `opencode2` alias) | Upstream publishes from the `@opencode/cli` npm scope on the `latest` channel, versioned as -semver release tags (`v2.0.0`…). This pin is the release tag `v2.0.29`, so -`npm install -g @opencode/cli@2.0.29` installs a server built from exactly this commit; later +semver release tags (`v2.0.0`…). This pin is the release tag `v2.0.32`, so +`npm install -g @opencode/cli@2.0.32` installs a server built from exactly this commit; later releases usually work but are not what this repository tests. `beta` and `dev` tags still exist upstream but are not the release channel. The former `@opencode-ai/cli` scope is frozen: its `next` and `latest` tags stopped at `0.0.0-beta-17823` (published 2026-08-21) and the scope diff --git a/spec/openapi.json b/spec/openapi.json index a3428efb..47757173 100644 --- a/spec/openapi.json +++ b/spec/openapi.json @@ -5357,6 +5357,98 @@ } } }, + "/api/experimental/voice/session": { + "post": { + "tags": ["voice"], + "operationId": "experimental.voice.session", + "parameters": [], + "security": [], + "responses": { + "200": { + "description": "VoiceSessionResponse", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoiceSessionResponse" + } + } + } + }, + "400": { + "description": "InvalidRequestError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/InvalidRequestErrorEncoded" + } + } + } + }, + "401": { + "description": "UnauthorizedError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UnauthorizedErrorEncoded" + } + } + } + }, + "404": { + "description": "LocationNotFoundError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LocationNotFoundErrorEncoded" + } + } + } + }, + "503": { + "description": "ServiceUnavailableError", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ServiceUnavailableErrorEncoded" + } + } + } + } + }, + "description": "Create a server-brokered voice session using the server's configured provider credentials and return the WebRTC SDP answer or WebSocket ephemeral token.", + "summary": "Create voice session", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "model": { + "type": "string" + }, + "session": { + "anyOf": [ + { + "type": "object" + }, + { + "type": "null" + } + ] + }, + "transport": { + "$ref": "#/components/schemas/VoiceTransportInput" + } + }, + "required": ["model", "transport"], + "additionalProperties": false + } + } + }, + "required": true + } + } + }, "/api/provider": { "get": { "tags": ["provider"], @@ -14163,6 +14255,12 @@ "items": { "type": "string" } + }, + "temperature": { + "type": "boolean" + }, + "reasoning": { + "$ref": "#/components/schemas/Config.Model.Reasoning" } }, "additionalProperties": false @@ -14206,6 +14304,53 @@ "required": ["input", "output"], "additionalProperties": false }, + "Config.Model.Reasoning": { + "anyOf": [ + { + "type": "object", + "properties": { + "supported": { + "type": "boolean", + "enum": [false] + } + }, + "required": ["supported"], + "additionalProperties": false + }, + { + "type": "object", + "properties": { + "supported": { + "type": "boolean", + "enum": [true] + }, + "toggle": { + "type": "boolean" + }, + "effort": { + "type": "array", + "items": { + "type": "string" + } + }, + "budget": { + "type": "object", + "properties": { + "min": { + "type": "integer" + }, + "max": { + "type": "integer" + } + }, + "additionalProperties": false + } + }, + "required": ["supported"], + "additionalProperties": false + } + ] + }, "Config.Model.Settings": { "type": "object", "properties": { @@ -14242,6 +14387,11 @@ "name": { "type": "string" }, + "releaseDate": { + "type": "string", + "pattern": "^\\d{4}-\\d{2}(-\\d{2})?$", + "description": "Release date as YYYY-MM-DD or YYYY-MM" + }, "compatibility": { "$ref": "#/components/schemas/Model.Compatibility" }, @@ -16722,6 +16872,9 @@ "items": { "type": "string" } + }, + "temperature": { + "type": "boolean" } }, "required": ["tools", "input", "output"], @@ -21318,6 +21471,128 @@ "required": ["_tag", "providerID", "message"], "additionalProperties": false }, + "VoiceSessionResponse": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "transport": { + "$ref": "#/components/schemas/VoiceTransport" + } + }, + "required": ["transport"], + "additionalProperties": false + } + }, + "required": ["data"], + "additionalProperties": false + }, + "VoiceTransport": { + "anyOf": [ + { + "$ref": "#/components/schemas/VoiceWebRTCTransport" + }, + { + "$ref": "#/components/schemas/VoiceWebSocketTransport" + } + ] + }, + "VoiceTransportInput": { + "anyOf": [ + { + "$ref": "#/components/schemas/VoiceWebRTCTransportInput" + }, + { + "$ref": "#/components/schemas/VoiceWebSocketTransportInput" + } + ] + }, + "VoiceWebRTCTransport": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["webrtc"] + }, + "sdp": { + "type": "string" + } + }, + "required": ["type", "sdp"], + "additionalProperties": false + }, + "VoiceWebRTCTransportInput": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["webrtc"] + }, + "sdp": { + "type": "string" + }, + "dcid": { + "anyOf": [ + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": ["Infinity", "-Infinity", "NaN"] + } + ] + }, + { + "type": "null" + } + ] + } + }, + "required": ["type", "sdp"], + "additionalProperties": false + }, + "VoiceWebSocketTransport": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["websocket"] + }, + "url": { + "type": "string" + }, + "token": { + "type": "string" + }, + "expiresAt": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string", + "enum": ["Infinity", "-Infinity", "NaN"] + } + ] + } + }, + "required": ["type", "url", "token", "expiresAt"], + "additionalProperties": false + }, + "VoiceWebSocketTransportInput": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["websocket"] + } + }, + "required": ["type"], + "additionalProperties": false + }, "WebSearch.ID": { "type": "string" }, @@ -25595,7 +25870,6 @@ "enum": [ "session.list", "session.new", - "session.share", "session.interrupt", "session.background", "session.compact", @@ -25930,6 +26204,10 @@ "name": "generate", "description": "Experimental one-shot generation routes." }, + { + "name": "voice", + "description": "Experimental server-brokered voice session routes." + }, { "name": "provider", "description": "Experimental provider routes." diff --git a/spec/receipt.json b/spec/receipt.json index 01073a7d..9a4494e5 100644 --- a/spec/receipt.json +++ b/spec/receipt.json @@ -1,9 +1,9 @@ { "schemaVersion": 2, - "releaseTag": "v2.0.29", - "upstreamCommit": "042d0832f5f3a73eaa039a58a7d565776f6d4290", - "rawDocumentSha256": "3fcd666b597b746bad26f56a92fe782cc86338a0b4498f43b435a2f97e4fa05a", - "generatedBaselineSha256": "3fcd666b597b746bad26f56a92fe782cc86338a0b4498f43b435a2f97e4fa05a", + "releaseTag": "v2.0.32", + "upstreamCommit": "843a6c514e606b436a7fe713422ace0a93ad3d61", + "rawDocumentSha256": "88f2f560cfa96bbfed5cb99144e4d840b1f923a1d5a3fe2bc55819d008e55e24", + "generatedBaselineSha256": "88f2f560cfa96bbfed5cb99144e4d840b1f923a1d5a3fe2bc55819d008e55e24", "patches": [ { "manifest": "001-restore-sse-payloads.json", @@ -20,13 +20,15 @@ ] } ], - "normalizedDocumentSha256": "60640136cd3aac3823b79719721436757c618583acd60ec707f0a9a9a5296eea", + "normalizedDocumentSha256": "6bd6bfba0e91251f26efb53cc07a9533c9057071009afe7556d610f9bda10921", "normalizedDocumentPath": null, - "operationSetDigest": "b1752db03ff7c451bc1674c7aba69300b29939b64111a37ef7adeaa9eaa6b511", - "operationCount": 142, - "addedOperations": [], + "operationSetDigest": "b8b7b5e1809c1985c0ba38fcc1c87352b23a6190a8a7a2582c31913b6ec3a4b5", + "operationCount": 143, + "addedOperations": [ + "experimental.voice.session" + ], "removedOperations": [], - "componentCount": 418, + "componentCount": 426, "contentSchemaCount": 2, "watchedSources": [ { @@ -71,7 +73,7 @@ }, { "path": "packages/server/src/routes.ts", - "sha256": "7d61d4a59322812ccdee2d269d7abae00e313224bf3971dcc5965bbef886e1ca", + "sha256": "e486f6b670f7c2eb4b73a2c029b08a5c5a9442df2f646d22143f316324d88bdc", "anchorMatched": true }, { @@ -116,12 +118,12 @@ }, { "path": "packages/cli/package.json", - "sha256": "f69914f2c1cb2b038c752542ab4754f83c16e39a30147e09976bcd62e077c919", + "sha256": "ffc734f35af71d5396ecf1f162725c903a149264c6c46ab9ab3f6ca22442c482", "anchorMatched": true }, { "path": "packages/core/package.json", - "sha256": "0e9a4c79e28afb605be8952c185d8936af3bb92410e860281d9e510a16b74821", + "sha256": "4553035ed83131c02328a9f1369ca09c4fb3630e5d16726a117442aeb54b6f9e", "anchorMatched": true }, { @@ -146,7 +148,7 @@ }, { "path": "packages/tui/src/component/terminal-pane.tsx", - "sha256": "1ab67b8b99c42c4bf470466072a9226047733ee8a752127a6243e56063aa3591", + "sha256": "fda95e06a5d4eefbf1ff6f580f71f3ef8e049ced9c9b807b89b4e56aeecb5c55", "anchorMatched": true }, { @@ -166,7 +168,7 @@ }, { "path": "packages/cli/src/server-process.ts", - "sha256": "6ba7e3a0d9165dc358db80dec6f7f8598ad2f9cf1ecf5471386facdea65cadc1", + "sha256": "5467b66bb6a45151754f920443e1f50d755d751da1c7fa7390651bd6641f5e92", "anchorMatched": true }, { @@ -281,7 +283,7 @@ }, { "path": "packages/cli/src/services/service-config.ts", - "sha256": "ae1b8f6d28ff38063e6f31995c2b94e9e8196c3a02bf3b7afbe020af3e48b725", + "sha256": "1a38bd1ba12990938b8da3c9f4ad1cab14dd24d50e743437bdd0a6758d0e7ad3", "anchorMatched": true }, { @@ -321,12 +323,12 @@ }, { "path": "packages/client/test/service.test.ts", - "sha256": "98b171926f67814e3a0b44ac9da8c4099e8f506f916424b2606f98595cbdb2a7", + "sha256": "0a9d55eaae24739200d8b9c6c4cd088d378f36d181fbde429adf4f6f833f4183", "anchorMatched": true }, { "path": "packages/cli/test/service.test.ts", - "sha256": "c97c285027dd9c37884f36fbfc6ce5e9fea558ae6bf491871f5de06bc9559095", + "sha256": "7cda29fd29474ba6770eb33d8d8e266ee57ee11c14a91ca6f3c2958d4b1ad452", "anchorMatched": true }, { @@ -356,7 +358,7 @@ }, { "path": "package.json", - "sha256": "a0d3f21a366eea08da3beaee0d54944d79df5e750b79cc5af31ac9aeffd51094", + "sha256": "81dfe93dff4adcf82f37b6342917e0a5afa7c7c5e623adb8e92b00f99eda5404", "anchorMatched": true }, { @@ -401,12 +403,12 @@ }, { "path": "packages/protocol/src/api.ts", - "sha256": "9bc2d903aaaa3070fc895af250c4ad6b2a892c9e7247ca31ad10627c43525e05", + "sha256": "92ddbb151c8156523bacd4ef42104f7a6ac3b59d44cfd4783472a8309afc3e3f", "anchorMatched": true }, { "path": "packages/client/test/service-reconnect.test.ts", - "sha256": "2028bf60f9fadfcc96796c6e8c43d905b7ba61a54da3445ca16fa84a9f696c85", + "sha256": "cb114111ac9c0d68dd5fab8237ed050e55062afe6b6815cb06258562500a7af7", "anchorMatched": true }, { diff --git a/spec/source-watch.json b/spec/source-watch.json index 6490e893..4aba8d9e 100644 --- a/spec/source-watch.json +++ b/spec/source-watch.json @@ -75,7 +75,7 @@ }, { "path": "packages/server/src/routes.ts", - "sha256": "7d61d4a59322812ccdee2d269d7abae00e313224bf3971dcc5965bbef886e1ca", + "sha256": "e486f6b670f7c2eb4b73a2c029b08a5c5a9442df2f646d22143f316324d88bdc", "behavior": "The PTY ticket service and the persistent-PTY service are wired into the server layer graph.", "anchor": { "type": "contains", @@ -156,7 +156,7 @@ }, { "path": "packages/cli/package.json", - "sha256": "f69914f2c1cb2b038c752542ab4754f83c16e39a30147e09976bcd62e077c919", + "sha256": "ffc734f35af71d5396ecf1f162725c903a149264c6c46ab9ab3f6ca22442c482", "behavior": "The CLI package bin map names \u0022opencode\u0022 as the installed command and keeps \u0022opencode2\u0022 as a transitional alias of the same launcher, so the shipped launcher default resolves \u0022opencode\u0022 from PATH. The CLI depends on the opencode-pty package at 0.2.0.", "anchor": { "type": "contains", @@ -165,7 +165,7 @@ }, { "path": "packages/core/package.json", - "sha256": "0e9a4c79e28afb605be8952c185d8936af3bb92410e860281d9e510a16b74821", + "sha256": "4553035ed83131c02328a9f1369ca09c4fb3630e5d16726a117442aeb54b6f9e", "behavior": "The opencode-pty daemon dependency is pinned at 0.2.0, which takes its runtime directory and name as arguments instead of OPENCODE_PTY_RUNTIME_DIR; the daemon protocol the persistent-PTY door speaks stays at version 7.", "anchor": { "type": "contains", @@ -210,7 +210,7 @@ }, { "path": "packages/tui/src/component/terminal-pane.tsx", - "sha256": "1ab67b8b99c42c4bf470466072a9226047733ee8a752127a6243e56063aa3591", + "sha256": "fda95e06a5d4eefbf1ff6f580f71f3ef8e049ced9c9b807b89b4e56aeecb5c55", "behavior": "Behavioral oracle: the TUI resumes a terminal from the snapshot tail cursor.", "anchor": { "type": "contains", @@ -246,7 +246,7 @@ }, { "path": "packages/cli/src/server-process.ts", - "sha256": "6ba7e3a0d9165dc358db80dec6f7f8598ad2f9cf1ecf5471386facdea65cadc1", + "sha256": "5467b66bb6a45151754f920443e1f50d755d751da1c7fa7390651bd6641f5e92", "behavior": "In stdio mode the serve command prints exactly one JSON line carrying the bound url to stdout after full boot and exits when stdin reaches EOF, which is the readiness line ServerReadyLine parses and the lease OpenCodeServer releases. In service mode it changes into the user home (which ServerIsolation\u0027s isolated home relies on), returns at once when a compatible incumbent already answers, and, when the bind finds the port in use, spends up to 15 seconds re-checking for a recognized incumbent and retrying the bind every 100 milliseconds: a recognized incumbent makes it exit 0 as the loser, and a freed port makes it the service. That exit 0 is the exit the Ensure election reads as a lost race and answers by doubling its spawn delay; a loser that exited non-zero would fail every multi-contender Ensure. On Windows, a service run from a package-manager or curl install first holds a second hard link to its own image under the cache directory, so an installer can replace the binary while the daemon runs; the pid and start time the stop door identifies it by are unchanged, and a daemon the stop door ends hard leaves that link for the next service start to sweep. An OPENCODE_PTY_HANDOFF that does not decode is logged and ignored, so the server starts with fresh persistent terminals instead of failing. In service mode with remote access configured, it attaches the remote-access tunnel in the background after binding; a tunnel failure is logged and changes neither the readiness line nor the exit status.", "anchor": { "type": "contains", @@ -301,7 +301,7 @@ { "path": "packages/core/src/session/session.ts", "sha256": "53d67aef56a1a6b518fad623d51a15b26f071db25c7e5f1c71a21c36ac3a9d11", - "behavior": "session.shell answers only after the shell command ends (it joins the running fiber) and experimental.session.wait only after the session's execution is idle, so neither sends response headers while it waits; the SDK's network timeout leaves both unbounded unless a call sets a window.", + "behavior": "session.shell answers only after the shell command ends (it joins the running fiber) and experimental.session.wait only after the session\u0027s execution is idle, so neither sends response headers while it waits; the SDK\u0027s network timeout leaves both unbounded unless a call sets a window.", "anchor": { "type": "contains", "text": "yield* execution.awaitIdle(sessionID)" @@ -310,7 +310,7 @@ { "path": "packages/server/src/handlers/generate.ts", "sha256": "9231f95384e907545dc53cab06c6577a4a7c34abbd709b6a685da534226de4d3", - "behavior": "experimental.generate.text answers with the whole generation in one body, so it sends no response headers until the model finishes; the SDK's network timeout leaves it unbounded unless a call sets a window.", + "behavior": "experimental.generate.text answers with the whole generation in one body, so it sends no response headers until the model finishes; the SDK\u0027s network timeout leaves it unbounded unless a call sets a window.", "anchor": { "type": "contains", "text": ".text(request.payload)" @@ -319,7 +319,7 @@ { "path": "packages/core/src/session/generate.ts", "sha256": "ad58915073c82d64d50c48641d5a6160e20b5788cb26190026f1a4ba517a3257", - "behavior": "session.generate returns the whole generation in one body, so it sends no response headers until the model finishes; the SDK's network timeout leaves it unbounded unless a call sets a window.", + "behavior": "session.generate returns the whole generation in one body, so it sends no response headers until the model finishes; the SDK\u0027s network timeout leaves it unbounded unless a call sets a window.", "anchor": { "type": "contains", "text": "const response = yield* llm.generate(prepared.request, prepared.options)" @@ -328,7 +328,7 @@ { "path": "packages/core/src/session/command.ts", "sha256": "90f3c32cfb52746de287dbbad1f9bbc9ccbff9c9bc9dc565a652e55831eda32f", - "behavior": "session.command awaits the command's execution before it answers, so a command that does long work holds the response headers until it ends; the SDK's network timeout leaves it unbounded unless a call sets a window.", + "behavior": "session.command awaits the command\u0027s execution before it answers, so a command that does long work holds the response headers until it ends; the SDK\u0027s network timeout leaves it unbounded unless a call sets a window.", "anchor": { "type": "contains", "text": "yield* commands.execute({" @@ -453,7 +453,7 @@ }, { "path": "packages/cli/src/services/service-config.ts", - "sha256": "ae1b8f6d28ff38063e6f31995c2b94e9e8196c3a02bf3b7afbe020af3e48b725", + "sha256": "1a38bd1ba12990938b8da3c9f4ad1cab14dd24d50e743437bdd0a6758d0e7ad3", "behavior": "The release channels latest, dev, beta, and next share service.json; every other channel is sanitized into service-\u003Cchannel\u003E.json; a legacy SHA-1 filename exists for every channel except latest and local; versionBelongsToChannel admits the exact installed version or the 0.0.0-\u003Cchannel\u003E-N[.M] spelling; migration copies a donor exclusively with mode 0600, ignores write failures, and admits only the current shape. Info declares optional disabled (a boolean), remote (an object holding a string route), and remoteAccepted (the literal true, which the CLI writes when remote access is accepted while pairing or turned off after it was on, and which the service get, set, and unset commands do not reach) beside hostname, port, password, cors, and env. read turns a document that fails both the current shape and the legacy shape (remote as a boolean) into an empty config, so an invalid document, including one whose remoteAccepted is present but not true, is absent config; a legacy document is rewritten in place, true getting a generated route and false dropping the key.", "anchor": { "type": "contains", @@ -525,7 +525,7 @@ }, { "path": "packages/client/test/service.test.ts", - "sha256": "98b171926f67814e3a0b44ac9da8c4099e8f506f916424b2606f98595cbdb2a7", + "sha256": "0a9d55eaae24739200d8b9c6c4cd088d378f36d181fbde429adf4f6f833f4183", "behavior": "Upstream\u0027s own discovery expectations: a compatible registered service is reused, a waiting one is awaited, a failed one is reported without spawning, and a version-mismatched one is replaced; stop escalates by pid when the registration disappears and preserves a successor\u0027s registration; a contender failure is retained without recruiting replacements, survives until the deadline, and is dropped after an eviction. ServiceEnsurerTests, ServiceStopperTests, ServiceDiscoveryTests, and the Ensure live tests re-prove them.", "anchor": { "type": "contains", @@ -534,7 +534,7 @@ }, { "path": "packages/cli/test/service.test.ts", - "sha256": "c97c285027dd9c37884f36fbfc6ce5e9fea558ae6bf491871f5de06bc9559095", + "sha256": "7cda29fd29474ba6770eb33d8d8e266ee57ee11c14a91ca6f3c2958d4b1ad452", "behavior": "Upstream\u0027s own filename and channel expectations: the release channels share service.json, custom channels are sanitized, and version-to-channel membership follows the pre-release spelling; ServicePathResolverTests and ServiceMigrationTests re-prove them. A managed-service contender that finds its port occupied binds once the port is released.", "anchor": { "type": "contains", @@ -588,7 +588,7 @@ }, { "path": "package.json", - "sha256": "a0d3f21a366eea08da3beaee0d54944d79df5e750b79cc5af31ac9aeffd51094", + "sha256": "81dfe93dff4adcf82f37b6342917e0a5afa7c7c5e623adb8e92b00f99eda5404", "behavior": "The workspace catalog pins the Effect release the protocol document is generated with, whose JSON Schema encoding of StructWithRest (a struct\u0027s properties beside a single-element allOf carrying only the rest\u0027s additionalProperties) the projector admits as an open object, and whose union decode (SchemaAST Union.getParser: literal sentinels, then the first member in declaration order that decodes) the structural union first-match arm mirrors, as tools/oracles/first-match-union.ts proves; an Effect bump is reviewed against both, and the oracle re-run, before the snapshot moves.", "anchor": { "type": "contains", @@ -669,7 +669,7 @@ }, { "path": "packages/protocol/src/api.ts", - "sha256": "9bc2d903aaaa3070fc895af250c4ad6b2a892c9e7247ca31ad10627c43525e05", + "sha256": "92ddbb151c8156523bacd4ef42104f7a6ac3b59d44cfd4783472a8309afc3e3f", "behavior": "The API assembly decides which groups carry the location middleware. It passes the middleware into makePtyGroup, which applies it to every PTY endpoint except pty.connect, so pty.connect resolves its location in its own handler; the persistent-PTY group carries none.", "anchor": { "type": "contains", @@ -678,7 +678,7 @@ }, { "path": "packages/client/test/service-reconnect.test.ts", - "sha256": "2028bf60f9fadfcc96796c6e8c43d905b7ba61a54da3445ca16fa84a9f696c85", + "sha256": "cb114111ac9c0d68dd5fab8237ed050e55062afe6b6815cb06258562500a7af7", "behavior": "Behavioral oracle for both clients: a registered owner whose health endpoint disappears fails ensure with the incompatible-health-protocol error and is neither signalled, handed off, nor replaced; a nonmatching version requirement replaces it, and an explicit stop ends it. ServiceEnsurerTests re-proves the election side.", "anchor": { "type": "contains", diff --git a/src/OpenCode.Sdk/.generated-manifest.json b/src/OpenCode.Sdk/.generated-manifest.json index b7fa771b..9287909e 100644 --- a/src/OpenCode.Sdk/.generated-manifest.json +++ b/src/OpenCode.Sdk/.generated-manifest.json @@ -60,6 +60,7 @@ "Config/Internal/ConfigInfoWebsearchJsonConverter.cs", "Config/Internal/ConfigLspEntryJsonConverter.cs", "Config/Internal/ConfigModelPricingJsonConverter.cs", + "Config/Internal/ConfigModelReasoningJsonConverter.cs", "Config/Internal/ConfigModelSelectionJsonConverter.cs", "Config/Internal/ConfigProviderSettingsChunkTimeoutJsonConverter.cs", "Config/Internal/ConfigProviderSettingsHeaderTimeoutJsonConverter.cs", @@ -69,6 +70,7 @@ "Config/Internal/ConfigShellsResponseAdapter.cs", "Config/Internal/OpenCodeRoutes.Config.cs", "Config/Internal/UnknownConfigEntryJsonConverter.cs", + "Config/Internal/UnknownConfigModelReasoningJsonConverter.cs", "Config/Models/ConfigAgent.cs", "Config/Models/ConfigAgentMode.cs", "Config/Models/ConfigAgentRequest.cs", @@ -117,6 +119,9 @@ "Config/Models/ConfigModelLimit.cs", "Config/Models/ConfigModelPricing.cs", "Config/Models/ConfigModelPricingKind.cs", + "Config/Models/ConfigModelReasoningBudget.cs", + "Config/Models/ConfigModelReasoningSupported.cs", + "Config/Models/ConfigModelReasoningUnsupported.cs", "Config/Models/ConfigModelSelection.cs", "Config/Models/ConfigModelSelectionExplicit.cs", "Config/Models/ConfigModelSelectionKind.cs", @@ -141,7 +146,9 @@ "Config/Models/ConfigWebSearchInfo.cs", "Config/Models/ConfigWorktree.cs", "Config/Models/IConfigEntry.cs", + "Config/Models/IConfigModelReasoning.cs", "Config/Models/UnknownConfigEntry.cs", + "Config/Models/UnknownConfigModelReasoning.cs", "Credentials/CredentialActivateResponse.cs", "Credentials/CredentialCreateRequest.cs", "Credentials/CredentialCreateResponse.cs", @@ -231,6 +238,8 @@ "Experimental/ExperimentalSessionStatsRequest.cs", "Experimental/ExperimentalSessionStatsResponse.cs", "Experimental/ExperimentalSessionWaitResponse.cs", + "Experimental/ExperimentalVoiceSessionRequest.cs", + "Experimental/ExperimentalVoiceSessionResponse.cs", "Experimental/Internal/ExperimentalConfigUpdateResponseAdapter.cs", "Experimental/Internal/ExperimentalFsWriteResponseAdapter.cs", "Experimental/Internal/ExperimentalFsWriteResponseEnvelope.cs", @@ -257,7 +266,13 @@ "Experimental/Internal/ExperimentalSessionStatsResponseAdapter.cs", "Experimental/Internal/ExperimentalSessionStatsResponseEnvelope.cs", "Experimental/Internal/ExperimentalSessionWaitResponseAdapter.cs", + "Experimental/Internal/ExperimentalVoiceSessionResponseAdapter.cs", + "Experimental/Internal/ExperimentalVoiceSessionResponseEnvelope.cs", "Experimental/Internal/OpenCodeRoutes.Experimental.cs", + "Experimental/Internal/UnknownVoiceTransportInputJsonConverter.cs", + "Experimental/Internal/UnknownVoiceTransportJsonConverter.cs", + "Experimental/Internal/VoiceTransportInputJsonConverter.cs", + "Experimental/Internal/VoiceTransportJsonConverter.cs", "Experimental/Models/ExperimentalGenerateTextData.cs", "Experimental/Models/ExperimentalMigrationV1Status.cs", "Experimental/Models/ExperimentalMigrationV1StatusError.cs", @@ -267,9 +282,18 @@ "Experimental/Models/ExperimentalMigrationV1StatusRunning.cs", "Experimental/Models/ExperimentalMigrationV1StatusRunningProgress.cs", "Experimental/Models/ExperimentalSessionStatsRequestTools.cs", + "Experimental/Models/ExperimentalVoiceSessionData.cs", + "Experimental/Models/IVoiceTransport.cs", + "Experimental/Models/IVoiceTransportInput.cs", "Experimental/Models/InstructionEntryInfo.cs", "Experimental/Models/InstructionEntrySnapshot.cs", "Experimental/Models/InstructionEntryValueTooLargeError.cs", + "Experimental/Models/UnknownVoiceTransport.cs", + "Experimental/Models/UnknownVoiceTransportInput.cs", + "Experimental/Models/VoiceWebRtcTransport.cs", + "Experimental/Models/VoiceWebRtcTransportInput.cs", + "Experimental/Models/VoiceWebSocketTransport.cs", + "Experimental/Models/VoiceWebSocketTransportInput.cs", "FileSystem/FileSystemClient.cs", "FileSystem/FsFindRequest.cs", "FileSystem/FsFindResponse.cs", @@ -446,6 +470,7 @@ "Internal/Serialization/OptionalOfPermissionRuleListJsonConverter.cs", "Internal/Serialization/OptionalOfSessionInboxDeliveryJsonConverter.cs", "Internal/Serialization/OptionalOfSessionMessageLocationSwitchedPreviousJsonConverter.cs", + "Internal/Serialization/OptionalOfSpecialNumberJsonConverter.cs", "Internal/Serialization/OptionalOfStringJsonConverter.cs", "LanguageModels/Internal/ModelCacheFormatJsonConverter.cs", "LanguageModels/Internal/ModelDefaultResponseAdapter.cs", diff --git a/src/OpenCode.Sdk/.generated-reach.txt b/src/OpenCode.Sdk/.generated-reach.txt index bfab9ee7..63abf224 100644 --- a/src/OpenCode.Sdk/.generated-reach.txt +++ b/src/OpenCode.Sdk/.generated-reach.txt @@ -56,6 +56,9 @@ ConfigModelCostTier owner=Config reached-by=Config ConfigModelLimit owner=Config reached-by=Config ConfigModelPricing owner=Config reached-by=Config ConfigModelPricingKind owner=Config reached-by=Config +ConfigModelReasoningBudget owner=Config reached-by=Config +ConfigModelReasoningSupported owner=Config reached-by=Config +ConfigModelReasoningUnsupported owner=Config reached-by=Config ConfigModelSelection owner=Config reached-by=Config ConfigModelSelectionExplicit owner=Config reached-by=Config ConfigModelSelectionKind owner=Config reached-by=Config @@ -112,6 +115,8 @@ ExperimentalSessionImportRequest owner=Experimental reached-by=Experimental ExperimentalSessionInstructionsEntryPutRequest owner=Experimental reached-by=Experimental ExperimentalSessionSkillRequest owner=Experimental reached-by=Experimental ExperimentalSessionStatsRequestTools owner=Experimental reached-by=Experimental +ExperimentalVoiceSessionData owner=Experimental reached-by=Experimental +ExperimentalVoiceSessionRequest owner=Experimental reached-by=Experimental FileDiffInfo owner=Vcs reached-by=Events,Experimental,Sessions,Vcs FileDiffInfoStatus owner=Vcs reached-by=Events,Experimental,Sessions,Vcs FileNotFoundError owner=FileSystem reached-by=FileSystem @@ -152,6 +157,7 @@ FormWhenValue owner=Forms reached-by=Events,Forms,Integrations,Sessions FormWhenValueKind owner=Forms reached-by=Events,Forms,Integrations,Sessions FsFindRequestType owner=FileSystem reached-by=FileSystem IConfigEntry owner=Config reached-by=Config +IConfigModelReasoning owner=Config reached-by=Config IConnectionInfo owner=Integrations reached-by=Integrations ICredentialValue owner=Credentials reached-by=Credentials IDurableEnvelope owner=Sessions reached-by=Events,Sessions @@ -187,6 +193,8 @@ ISessionStatsTools owner=Sessions reached-by=Experimental ISessionStatus owner=Sessions reached-by=Events IStreamFailureCause owner=Common reached-by=Events,Sessions IToolContent owner=Sessions reached-by=Events,Experimental,Sessions +IVoiceTransport owner=Experimental reached-by=Experimental +IVoiceTransportInput owner=Experimental reached-by=Experimental InstallationUpdateAvailable owner=Events reached-by=Events InstallationUpdateAvailableData owner=Events reached-by=Events InstallationUpdated owner=Events reached-by=Events @@ -684,6 +692,7 @@ TuiToastShowData owner=Events reached-by=Events TuiToastShowDataVariant owner=Events reached-by=Events UnauthorizedError owner=Common reached-by=Agents,Commands,Config,Credentials,Debug,Events,Experimental,FileSystem,Forms,Integrations,LanguageModels,Location,McpServers,Permissions,PersistentPtys,Plugins,Projects,Providers,Ptys,References,Rpc,Server,Sessions,Shells,Skills,Vcs,Websearch,Worktrees UnknownConfigEntry owner=Config reached-by=Config +UnknownConfigModelReasoning owner=Config reached-by=Config UnknownConnectionInfo owner=Integrations reached-by=Integrations UnknownCredentialValue owner=Credentials reached-by=Credentials UnknownError owner=Common reached-by=Experimental,Sessions @@ -714,6 +723,8 @@ UnknownSessionStatsTools owner=Sessions reached-by=Experimental UnknownSessionStatus owner=Sessions reached-by=Events UnknownStreamFailureCause owner=Common reached-by=Events,Sessions UnknownToolContent owner=Sessions reached-by=Events,Experimental,Sessions +UnknownVoiceTransport owner=Experimental reached-by=Experimental +UnknownVoiceTransportInput owner=Experimental reached-by=Experimental VcsBase owner=Vcs reached-by=Vcs VcsBaseSource owner=Vcs reached-by=Vcs VcsBranch owner=Vcs reached-by=Vcs @@ -724,6 +735,10 @@ VcsFileStatusStatus owner=Vcs reached-by=Vcs VcsInfo owner=Vcs reached-by=Vcs VcsInitNotSupportedError owner=Vcs reached-by=Vcs VcsMode owner=Vcs reached-by=Vcs +VoiceWebRtcTransport owner=Experimental reached-by=Experimental +VoiceWebRtcTransportInput owner=Experimental reached-by=Experimental +VoiceWebSocketTransport owner=Experimental reached-by=Experimental +VoiceWebSocketTransportInput owner=Experimental reached-by=Experimental WebSearchProvider owner=Websearch reached-by=Websearch WebSearchResponse owner=Websearch reached-by=Websearch WebSearchResult owner=Websearch reached-by=Websearch diff --git a/src/OpenCode.Sdk/.generation-incomplete b/src/OpenCode.Sdk/.generation-incomplete index 5630842c..82e3f32a 100644 --- a/src/OpenCode.Sdk/.generation-incomplete +++ b/src/OpenCode.Sdk/.generation-incomplete @@ -1,5 +1,5 @@ Generation is complete at the declared coverage; packages may be published. -Selected operations: 140 +Selected operations: 141 Pending operations: 0 Declined operations: 0 Transport-owned operations: 2 @@ -34,6 +34,7 @@ Selected: - experimental.session.skill - experimental.session.stats - experimental.session.wait +- experimental.voice.session - form.list - fs.find - fs.list diff --git a/src/OpenCode.Sdk/.gitattributes b/src/OpenCode.Sdk/.gitattributes index 0189cb59..9caa2c1b 100644 --- a/src/OpenCode.Sdk/.gitattributes +++ b/src/OpenCode.Sdk/.gitattributes @@ -60,6 +60,7 @@ /Config/Internal/ConfigInfoWebsearchJsonConverter.cs linguist-generated /Config/Internal/ConfigLspEntryJsonConverter.cs linguist-generated /Config/Internal/ConfigModelPricingJsonConverter.cs linguist-generated +/Config/Internal/ConfigModelReasoningJsonConverter.cs linguist-generated /Config/Internal/ConfigModelSelectionJsonConverter.cs linguist-generated /Config/Internal/ConfigProviderSettingsChunkTimeoutJsonConverter.cs linguist-generated /Config/Internal/ConfigProviderSettingsHeaderTimeoutJsonConverter.cs linguist-generated @@ -69,6 +70,7 @@ /Config/Internal/ConfigShellsResponseAdapter.cs linguist-generated /Config/Internal/OpenCodeRoutes.Config.cs linguist-generated /Config/Internal/UnknownConfigEntryJsonConverter.cs linguist-generated +/Config/Internal/UnknownConfigModelReasoningJsonConverter.cs linguist-generated /Config/Models/ConfigAgent.cs linguist-generated /Config/Models/ConfigAgentMode.cs linguist-generated /Config/Models/ConfigAgentRequest.cs linguist-generated @@ -117,6 +119,9 @@ /Config/Models/ConfigModelLimit.cs linguist-generated /Config/Models/ConfigModelPricing.cs linguist-generated /Config/Models/ConfigModelPricingKind.cs linguist-generated +/Config/Models/ConfigModelReasoningBudget.cs linguist-generated +/Config/Models/ConfigModelReasoningSupported.cs linguist-generated +/Config/Models/ConfigModelReasoningUnsupported.cs linguist-generated /Config/Models/ConfigModelSelection.cs linguist-generated /Config/Models/ConfigModelSelectionExplicit.cs linguist-generated /Config/Models/ConfigModelSelectionKind.cs linguist-generated @@ -141,7 +146,9 @@ /Config/Models/ConfigWebSearchInfo.cs linguist-generated /Config/Models/ConfigWorktree.cs linguist-generated /Config/Models/IConfigEntry.cs linguist-generated +/Config/Models/IConfigModelReasoning.cs linguist-generated /Config/Models/UnknownConfigEntry.cs linguist-generated +/Config/Models/UnknownConfigModelReasoning.cs linguist-generated /Credentials/CredentialActivateResponse.cs linguist-generated /Credentials/CredentialCreateRequest.cs linguist-generated /Credentials/CredentialCreateResponse.cs linguist-generated @@ -231,6 +238,8 @@ /Experimental/ExperimentalSessionStatsRequest.cs linguist-generated /Experimental/ExperimentalSessionStatsResponse.cs linguist-generated /Experimental/ExperimentalSessionWaitResponse.cs linguist-generated +/Experimental/ExperimentalVoiceSessionRequest.cs linguist-generated +/Experimental/ExperimentalVoiceSessionResponse.cs linguist-generated /Experimental/Internal/ExperimentalConfigUpdateResponseAdapter.cs linguist-generated /Experimental/Internal/ExperimentalFsWriteResponseAdapter.cs linguist-generated /Experimental/Internal/ExperimentalFsWriteResponseEnvelope.cs linguist-generated @@ -257,7 +266,13 @@ /Experimental/Internal/ExperimentalSessionStatsResponseAdapter.cs linguist-generated /Experimental/Internal/ExperimentalSessionStatsResponseEnvelope.cs linguist-generated /Experimental/Internal/ExperimentalSessionWaitResponseAdapter.cs linguist-generated +/Experimental/Internal/ExperimentalVoiceSessionResponseAdapter.cs linguist-generated +/Experimental/Internal/ExperimentalVoiceSessionResponseEnvelope.cs linguist-generated /Experimental/Internal/OpenCodeRoutes.Experimental.cs linguist-generated +/Experimental/Internal/UnknownVoiceTransportInputJsonConverter.cs linguist-generated +/Experimental/Internal/UnknownVoiceTransportJsonConverter.cs linguist-generated +/Experimental/Internal/VoiceTransportInputJsonConverter.cs linguist-generated +/Experimental/Internal/VoiceTransportJsonConverter.cs linguist-generated /Experimental/Models/ExperimentalGenerateTextData.cs linguist-generated /Experimental/Models/ExperimentalMigrationV1Status.cs linguist-generated /Experimental/Models/ExperimentalMigrationV1StatusError.cs linguist-generated @@ -267,9 +282,18 @@ /Experimental/Models/ExperimentalMigrationV1StatusRunning.cs linguist-generated /Experimental/Models/ExperimentalMigrationV1StatusRunningProgress.cs linguist-generated /Experimental/Models/ExperimentalSessionStatsRequestTools.cs linguist-generated +/Experimental/Models/ExperimentalVoiceSessionData.cs linguist-generated +/Experimental/Models/IVoiceTransport.cs linguist-generated +/Experimental/Models/IVoiceTransportInput.cs linguist-generated /Experimental/Models/InstructionEntryInfo.cs linguist-generated /Experimental/Models/InstructionEntrySnapshot.cs linguist-generated /Experimental/Models/InstructionEntryValueTooLargeError.cs linguist-generated +/Experimental/Models/UnknownVoiceTransport.cs linguist-generated +/Experimental/Models/UnknownVoiceTransportInput.cs linguist-generated +/Experimental/Models/VoiceWebRtcTransport.cs linguist-generated +/Experimental/Models/VoiceWebRtcTransportInput.cs linguist-generated +/Experimental/Models/VoiceWebSocketTransport.cs linguist-generated +/Experimental/Models/VoiceWebSocketTransportInput.cs linguist-generated /FileSystem/FileSystemClient.cs linguist-generated /FileSystem/FsFindRequest.cs linguist-generated /FileSystem/FsFindResponse.cs linguist-generated @@ -446,6 +470,7 @@ /Internal/Serialization/OptionalOfPermissionRuleListJsonConverter.cs linguist-generated /Internal/Serialization/OptionalOfSessionInboxDeliveryJsonConverter.cs linguist-generated /Internal/Serialization/OptionalOfSessionMessageLocationSwitchedPreviousJsonConverter.cs linguist-generated +/Internal/Serialization/OptionalOfSpecialNumberJsonConverter.cs linguist-generated /Internal/Serialization/OptionalOfStringJsonConverter.cs linguist-generated /LanguageModels/Internal/ModelCacheFormatJsonConverter.cs linguist-generated /LanguageModels/Internal/ModelDefaultResponseAdapter.cs linguist-generated diff --git a/src/OpenCode.Sdk/Config/Internal/ConfigModelReasoningJsonConverter.cs b/src/OpenCode.Sdk/Config/Internal/ConfigModelReasoningJsonConverter.cs new file mode 100644 index 00000000..462a966f --- /dev/null +++ b/src/OpenCode.Sdk/Config/Internal/ConfigModelReasoningJsonConverter.cs @@ -0,0 +1,48 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Collections.Frozen; +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; + +internal sealed class ConfigModelReasoningJsonConverter : JsonConverter +{ + private static readonly FrozenDictionary TypesByTag = new Dictionary() + { + [false] = typeof(ConfigModelReasoningUnsupported), + [true] = typeof(ConfigModelReasoningSupported) + }.ToFrozenDictionary(); + private static readonly UnionDiscriminatorReader DiscriminatorReader = new(); + public override IConfigModelReasoning Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + var marker = DiscriminatorReader.ReadBoolean(ref reader, "supported", "ConfigModelReasoning"); + if (TypesByTag.TryGetValue(marker, out var targetType)) + { + var typeInfo = OpenCodeJsonContext.Default.GetTypeInfo(targetType) ?? throw new JsonException("The generated context has no metadata for ConfigModelReasoning."); + return JsonSerializer.Deserialize(ref reader, typeInfo) as IConfigModelReasoning ?? throw new JsonException("The ConfigModelReasoning payload deserialized to null."); + } + + using var document = JsonDocument.ParseValue(ref reader); + var payload = document.RootElement; + return new UnknownConfigModelReasoning(marker, payload); + } + + public override void Write(Utf8JsonWriter writer, IConfigModelReasoning value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + ArgumentNullException.ThrowIfNull(options); + if (value is UnknownConfigModelReasoning unknown) + { + unknown.Payload.WriteTo(writer); + return; + } + + var typeInfo = OpenCodeJsonContext.Default.GetTypeInfo(value.GetType()) ?? throw new JsonException("The generated context has no metadata for ConfigModelReasoning."); + JsonSerializer.Serialize(writer, value, typeInfo); + } +} diff --git a/src/OpenCode.Sdk/Config/Internal/UnknownConfigModelReasoningJsonConverter.cs b/src/OpenCode.Sdk/Config/Internal/UnknownConfigModelReasoningJsonConverter.cs new file mode 100644 index 00000000..a857db3c --- /dev/null +++ b/src/OpenCode.Sdk/Config/Internal/UnknownConfigModelReasoningJsonConverter.cs @@ -0,0 +1,43 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; + +internal sealed class UnknownConfigModelReasoningJsonConverter : JsonConverter +{ + public override UnknownConfigModelReasoning Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + using var document = JsonDocument.ParseValue(ref reader); + var payload = document.RootElement; + if (payload.ValueKind != JsonValueKind.Object) + { + throw new JsonException("The ConfigModelReasoning payload must be a JSON object."); + } + + if (!payload.TryGetProperty("supported", out var markerElement)) + { + throw new JsonException("The ConfigModelReasoning payload must contain 'supported'."); + } + + if (markerElement.ValueKind is not JsonValueKind.True and not JsonValueKind.False) + { + throw new JsonException("The 'supported' marker must be a boolean."); + } + + var marker = markerElement.GetBoolean(); + return new UnknownConfigModelReasoning(marker, payload); + } + + public override void Write(Utf8JsonWriter writer, UnknownConfigModelReasoning value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + ArgumentNullException.ThrowIfNull(options); + value.Payload.WriteTo(writer); + } +} diff --git a/src/OpenCode.Sdk/Config/Models/ConfigModel.cs b/src/OpenCode.Sdk/Config/Models/ConfigModel.cs index 1d27368d..27b0764f 100644 --- a/src/OpenCode.Sdk/Config/Models/ConfigModel.cs +++ b/src/OpenCode.Sdk/Config/Models/ConfigModel.cs @@ -31,6 +31,13 @@ public sealed record ConfigModel [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Name { get; init; } + /// + /// Release date as YYYY-MM-DD or YYYY-MM + /// + [JsonPropertyName("releaseDate")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? ReleaseDate { get; init; } + /// /// Gets the compatibility value. /// @@ -104,5 +111,5 @@ public sealed record ConfigModel /// /// Prints the record's members with its secret members masked. /// - public override string ToString() => RecordPrinter.Format(nameof(ConfigModel), ("ModelId", ModelId), ("Family", Family), ("Name", Name), ("Compatibility", Compatibility), ("Package", Package), ("Settings", Settings), ("Headers", RecordPrinter.Redact(Headers)), ("Body", Body), ("Capabilities", Capabilities), ("Variants", Variants), ("Cost", Cost), ("Disabled", Disabled), ("Limit", Limit)); + public override string ToString() => RecordPrinter.Format(nameof(ConfigModel), ("ModelId", ModelId), ("Family", Family), ("Name", Name), ("ReleaseDate", ReleaseDate), ("Compatibility", Compatibility), ("Package", Package), ("Settings", Settings), ("Headers", RecordPrinter.Redact(Headers)), ("Body", Body), ("Capabilities", Capabilities), ("Variants", Variants), ("Cost", Cost), ("Disabled", Disabled), ("Limit", Limit)); } diff --git a/src/OpenCode.Sdk/Config/Models/ConfigModelCapabilities.cs b/src/OpenCode.Sdk/Config/Models/ConfigModelCapabilities.cs index 44686bef..581b3c69 100644 --- a/src/OpenCode.Sdk/Config/Models/ConfigModelCapabilities.cs +++ b/src/OpenCode.Sdk/Config/Models/ConfigModelCapabilities.cs @@ -28,4 +28,18 @@ public sealed record ConfigModelCapabilities [JsonPropertyName("output")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public IReadOnlyList? Output { get; init; } + + /// + /// Gets the temperature value. + /// + [JsonPropertyName("temperature")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Temperature { get; init; } + + /// + /// Gets the reasoning value. + /// + [JsonPropertyName("reasoning")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IConfigModelReasoning? Reasoning { get; init; } } diff --git a/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningBudget.cs b/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningBudget.cs new file mode 100644 index 00000000..bd34f094 --- /dev/null +++ b/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningBudget.cs @@ -0,0 +1,24 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a config model reasoning budget value. +/// +public sealed record ConfigModelReasoningBudget +{ + /// + /// Gets the min value. + /// + [JsonPropertyName("min")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public long? Min { get; init; } + + /// + /// Gets the max value. + /// + [JsonPropertyName("max")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public long? Max { get; init; } +} diff --git a/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningSupported.cs b/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningSupported.cs new file mode 100644 index 00000000..b8240eb2 --- /dev/null +++ b/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningSupported.cs @@ -0,0 +1,37 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a config model reasoning supported value. +/// +public sealed record ConfigModelReasoningSupported : IConfigModelReasoning +{ + /// + /// Gets the supported value. + /// + [JsonPropertyName("supported")] + public bool Supported => true; + + /// + /// Gets the toggle value. + /// + [JsonPropertyName("toggle")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Toggle { get; init; } + + /// + /// Gets the effort value. + /// + [JsonPropertyName("effort")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public IReadOnlyList? Effort { get; init; } + + /// + /// Gets the budget value. + /// + [JsonPropertyName("budget")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public ConfigModelReasoningBudget? Budget { get; init; } +} diff --git a/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningUnsupported.cs b/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningUnsupported.cs new file mode 100644 index 00000000..38a6d19c --- /dev/null +++ b/src/OpenCode.Sdk/Config/Models/ConfigModelReasoningUnsupported.cs @@ -0,0 +1,16 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a config model reasoning unsupported value. +/// +public sealed record ConfigModelReasoningUnsupported : IConfigModelReasoning +{ + /// + /// Gets the supported value. + /// + [JsonPropertyName("supported")] + public bool Supported => false; +} diff --git a/src/OpenCode.Sdk/Config/Models/IConfigModelReasoning.cs b/src/OpenCode.Sdk/Config/Models/IConfigModelReasoning.cs new file mode 100644 index 00000000..031725e4 --- /dev/null +++ b/src/OpenCode.Sdk/Config/Models/IConfigModelReasoning.cs @@ -0,0 +1,21 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a config model reasoning union. +/// +/// +/// Consume this interface; do not implement it outside tests. Its members follow the pinned protocol document and change when the SDK moves to a newer pin, which a minor version may do. +/// +[JsonConverter(typeof(ConfigModelReasoningJsonConverter))] +public interface IConfigModelReasoning +{ + /// + /// Gets the 'supported' union marker. + /// + [JsonPropertyName("supported")] + public bool Supported { get; } +} diff --git a/src/OpenCode.Sdk/Config/Models/UnknownConfigModelReasoning.cs b/src/OpenCode.Sdk/Config/Models/UnknownConfigModelReasoning.cs new file mode 100644 index 00000000..c3840057 --- /dev/null +++ b/src/OpenCode.Sdk/Config/Models/UnknownConfigModelReasoning.cs @@ -0,0 +1,40 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Preserves an unknown config model reasoning payload. +/// +[JsonConverter(typeof(UnknownConfigModelReasoningJsonConverter))] +public sealed record UnknownConfigModelReasoning : IConfigModelReasoning +{ + private readonly bool _marker; + /// + /// Initializes an unknown union value from its marker and raw payload. + /// + [JsonConstructor] + public UnknownConfigModelReasoning(bool supported, JsonElement payload) + { + if (payload.ValueKind is JsonValueKind.Undefined) + { + throw new ArgumentException("The payload must be a parsed JSON element.", nameof(payload)); + } + + UnionPayloadGuard.Instance.RequireBoolean(payload, "supported", supported); + _marker = supported; + Payload = payload.Clone(); + } + + /// + /// Gets the unrecognized 'supported' marker. + /// + [JsonPropertyName("supported")] + public bool Supported => _marker; + /// + /// Gets the preserved raw JSON payload. + /// + public JsonElement Payload { get; } +} diff --git a/src/OpenCode.Sdk/Experimental/ExperimentalClient.cs b/src/OpenCode.Sdk/Experimental/ExperimentalClient.cs index 4b1869e6..03c60f86 100644 --- a/src/OpenCode.Sdk/Experimental/ExperimentalClient.cs +++ b/src/OpenCode.Sdk/Experimental/ExperimentalClient.cs @@ -103,6 +103,24 @@ public virtual Task ConnectMcpServerAsync(string return Pipeline.ExecuteAsync("experimental.mcp.connect", HttpMethod.Post, OpenCodeRoutes.Experimental.ConnectMcpServer(server, request), ExperimentalMcpConnectResponseAdapter.Instance, requestOptions, cancellationToken); } + /// + /// Create voice session. Create a server-brokered voice session using the server's configured provider credentials and return the WebRTC SDP answer or WebSocket ephemeral token. + /// + /// The request body. + /// The per-call options. + /// The cancellation token. + /// The 'ExperimentalVoiceSessionResponse' envelope. + /// The API returned an error status (declared: 400, 401, 404, 503) and NoThrow was not selected. + /// The server could not be reached or returned a malformed success body. + /// + /// Operation experimental.voice.session: POST /api/experimental/voice/session. + /// + public virtual Task CreateVoiceSessionAsync(ExperimentalVoiceSessionRequest request, OpenCodeRequestOptions? requestOptions = null, CancellationToken cancellationToken = default) + { + ArgumentNullException.ThrowIfNull(request); + return Pipeline.ExecuteAsync("experimental.voice.session", HttpMethod.Post, OpenCodeRoutes.Experimental.CreateVoiceSession, request, OpenCodeJsonContext.Default.ExperimentalVoiceSessionRequest, ExperimentalVoiceSessionResponseAdapter.Instance, requestOptions, cancellationToken); + } + /// /// Disconnect MCP server. Disconnect an MCP server at runtime, removing its tools until reconnected. /// diff --git a/src/OpenCode.Sdk/Experimental/ExperimentalVoiceSessionRequest.cs b/src/OpenCode.Sdk/Experimental/ExperimentalVoiceSessionRequest.cs new file mode 100644 index 00000000..265dab86 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/ExperimentalVoiceSessionRequest.cs @@ -0,0 +1,33 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// The body of 'experimental.voice.session' (POST /api/experimental/voice/session). +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record ExperimentalVoiceSessionRequest +{ + /// + /// Gets the model value. + /// + [JsonPropertyName("model")] + public required string Model { get; init; } + + /// + /// Gets the session value. + /// + [JsonPropertyName("session")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(OptionalOfJsonElementDictionaryJsonConverter))] + public Optional?> Session { get; init; } + + /// + /// Gets the transport value. + /// + [JsonPropertyName("transport")] + public required IVoiceTransportInput Transport { get; init; } +} diff --git a/src/OpenCode.Sdk/Experimental/ExperimentalVoiceSessionResponse.cs b/src/OpenCode.Sdk/Experimental/ExperimentalVoiceSessionResponse.cs new file mode 100644 index 00000000..1f65b6e8 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/ExperimentalVoiceSessionResponse.cs @@ -0,0 +1,60 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Diagnostics.CodeAnalysis; +using System.Text; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk; +/// +/// The response of 'experimental.voice.session' (POST /api/experimental/voice/session). +/// +[Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record ExperimentalVoiceSessionResponse : OpenCodeResponse +{ + private readonly ExperimentalVoiceSessionData? _voiceSession; + /// + /// Initializes a success instance of the 'ExperimentalVoiceSessionResponse' envelope. + /// + public ExperimentalVoiceSessionResponse() + { + } + + /// + /// Initializes an error-path instance; the payload stays unset behind its guard. + /// + [SetsRequiredMembers] + internal ExperimentalVoiceSessionResponse(int status, IOpenCodeError? error, string? rawBody) + { + Status = status; + IsError = true; + Error = error; + RawBody = rawBody; + VoiceSession = null!; + } + + /// + /// Gets the VoiceSession payload; guarded on the error path. + /// + public required ExperimentalVoiceSessionData VoiceSession { get => _voiceSession ?? throw new InvalidOperationException("The response is an error; check IsError before accessing VoiceSession."); init => _voiceSession = value; } + + /// + /// Prints the shared metadata and appends the payload only when it is present. + /// + protected override bool PrintMembers(StringBuilder builder) + { + ArgumentNullException.ThrowIfNull(builder); + var printed = base.PrintMembers(builder); + if (_voiceSession is null) + { + return printed; + } + + if (printed) + { + _ = builder.Append(", "); + } + + _ = builder.Append("VoiceSession = ").Append(_voiceSession); + return true; + } +} diff --git a/src/OpenCode.Sdk/Experimental/Internal/ExperimentalVoiceSessionResponseAdapter.cs b/src/OpenCode.Sdk/Experimental/Internal/ExperimentalVoiceSessionResponseAdapter.cs new file mode 100644 index 00000000..ec0db39f --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Internal/ExperimentalVoiceSessionResponseAdapter.cs @@ -0,0 +1,64 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +namespace OpenCode.Sdk.Internal; +/// +/// Adapts the 'POST /api/experimental/voice/session' responses onto 'ExperimentalVoiceSessionResponse'. +/// +internal sealed class ExperimentalVoiceSessionResponseAdapter : ResponseAdapter +{ + private static readonly string[] Status400Tags = ["InvalidRequestError"]; + private static readonly string[] Status401Tags = ["UnauthorizedError"]; + private static readonly string[] Status404Tags = ["LocationNotFoundError"]; + private static readonly string[] Status503Tags = ["ServiceUnavailableError"]; + private ExperimentalVoiceSessionResponseAdapter() + { + } + + /// + /// Gets the shared adapter instance. + /// + public static ExperimentalVoiceSessionResponseAdapter Instance { get; } = new ExperimentalVoiceSessionResponseAdapter(); + + /// + /// Classifies a status under this operation's pinned contract. + /// + public override StatusVerdict Classify(int status) => status switch + { + 200 => StatusVerdict.Success, + >= 200 and < 300 => StatusVerdict.UndeclaredSuccess, + 400 => StatusVerdict.DeclaredError, + 401 => StatusVerdict.DeclaredError, + 404 => StatusVerdict.DeclaredError, + 503 => StatusVerdict.DeclaredError, + _ => StatusVerdict.UndeclaredError + }; + /// + /// Maps the declared UTF-8 success body onto the typed envelope. + /// + public override ExperimentalVoiceSessionResponse AdaptSuccess(int status, ReadOnlySpan utf8Body) => new() + { + Status = status, + VoiceSession = ReadBarePayload(utf8Body, OpenCodeJsonContext.Default.ExperimentalVoiceSessionResponseEnvelope).Data + }; + /// + /// Maps one buffered response onto the typed envelope. + /// + public override ExperimentalVoiceSessionResponse Adapt(int status, string rawBody) + { + ArgumentNullException.ThrowIfNull(rawBody); + return status switch + { + 200 => new ExperimentalVoiceSessionResponse + { + Status = status, + VoiceSession = ReadBarePayload(rawBody, OpenCodeJsonContext.Default.ExperimentalVoiceSessionResponseEnvelope).Data + }, + >= 200 and < 300 => throw StatusVerdictFailures.UndeclaredSuccess(status), + 400 => new ExperimentalVoiceSessionResponse(status, ReadTolerantError(rawBody, Status400Tags), rawBody), + 401 => new ExperimentalVoiceSessionResponse(status, ReadTolerantError(rawBody, Status401Tags), rawBody), + 404 => new ExperimentalVoiceSessionResponse(status, ReadTolerantError(rawBody, Status404Tags), rawBody), + 503 => new ExperimentalVoiceSessionResponse(status, ReadTolerantError(rawBody, Status503Tags), rawBody), + _ => new ExperimentalVoiceSessionResponse(status, ReadTolerantError(rawBody, null), rawBody) + }; + } +} diff --git a/src/OpenCode.Sdk/Experimental/Internal/ExperimentalVoiceSessionResponseEnvelope.cs b/src/OpenCode.Sdk/Experimental/Internal/ExperimentalVoiceSessionResponseEnvelope.cs new file mode 100644 index 00000000..58ac8c4e --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Internal/ExperimentalVoiceSessionResponseEnvelope.cs @@ -0,0 +1,14 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; +/// +/// Carries the wire body of 'ExperimentalVoiceSessionResponse' through one deserialization pass. +/// +internal sealed record ExperimentalVoiceSessionResponseEnvelope +{ + [JsonPropertyName("data")] + public required ExperimentalVoiceSessionData Data { get; init; } +} diff --git a/src/OpenCode.Sdk/Experimental/Internal/OpenCodeRoutes.Experimental.cs b/src/OpenCode.Sdk/Experimental/Internal/OpenCodeRoutes.Experimental.cs index dd207179..1fc82658 100644 --- a/src/OpenCode.Sdk/Experimental/Internal/OpenCodeRoutes.Experimental.cs +++ b/src/OpenCode.Sdk/Experimental/Internal/OpenCodeRoutes.Experimental.cs @@ -95,6 +95,10 @@ public static string ConnectMcpServer(string server, ExperimentalMcpConnectReque return path + query.Value; } + /// + /// The 'POST /api/experimental/voice/session' route. + /// + public const string CreateVoiceSession = "/api/experimental/voice/session"; /// /// Builds the '/api/experimental/mcp/{server}/disconnect' route. /// diff --git a/src/OpenCode.Sdk/Experimental/Internal/UnknownVoiceTransportInputJsonConverter.cs b/src/OpenCode.Sdk/Experimental/Internal/UnknownVoiceTransportInputJsonConverter.cs new file mode 100644 index 00000000..392df466 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Internal/UnknownVoiceTransportInputJsonConverter.cs @@ -0,0 +1,48 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; + +internal sealed class UnknownVoiceTransportInputJsonConverter : JsonConverter +{ + public override UnknownVoiceTransportInput Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + using var document = JsonDocument.ParseValue(ref reader); + var payload = document.RootElement; + if (payload.ValueKind != JsonValueKind.Object) + { + throw new JsonException("The VoiceTransportInput payload must be a JSON object."); + } + + if (!payload.TryGetProperty("type", out var markerElement)) + { + throw new JsonException("The VoiceTransportInput payload must contain 'type'."); + } + + if (markerElement.ValueKind != JsonValueKind.String) + { + throw new JsonException("The 'type' marker must be a string."); + } + + var marker = markerElement.GetString(); + if (marker is null || string.IsNullOrWhiteSpace(marker)) + { + throw new JsonException("The 'type' marker must be a non-empty string."); + } + + return new UnknownVoiceTransportInput(marker, payload); + } + + public override void Write(Utf8JsonWriter writer, UnknownVoiceTransportInput value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + ArgumentNullException.ThrowIfNull(options); + value.Payload.WriteTo(writer); + } +} diff --git a/src/OpenCode.Sdk/Experimental/Internal/UnknownVoiceTransportJsonConverter.cs b/src/OpenCode.Sdk/Experimental/Internal/UnknownVoiceTransportJsonConverter.cs new file mode 100644 index 00000000..6bae6785 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Internal/UnknownVoiceTransportJsonConverter.cs @@ -0,0 +1,48 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; + +internal sealed class UnknownVoiceTransportJsonConverter : JsonConverter +{ + public override UnknownVoiceTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + using var document = JsonDocument.ParseValue(ref reader); + var payload = document.RootElement; + if (payload.ValueKind != JsonValueKind.Object) + { + throw new JsonException("The VoiceTransport payload must be a JSON object."); + } + + if (!payload.TryGetProperty("type", out var markerElement)) + { + throw new JsonException("The VoiceTransport payload must contain 'type'."); + } + + if (markerElement.ValueKind != JsonValueKind.String) + { + throw new JsonException("The 'type' marker must be a string."); + } + + var marker = markerElement.GetString(); + if (marker is null || string.IsNullOrWhiteSpace(marker)) + { + throw new JsonException("The 'type' marker must be a non-empty string."); + } + + return new UnknownVoiceTransport(marker, payload); + } + + public override void Write(Utf8JsonWriter writer, UnknownVoiceTransport value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + ArgumentNullException.ThrowIfNull(options); + value.Payload.WriteTo(writer); + } +} diff --git a/src/OpenCode.Sdk/Experimental/Internal/VoiceTransportInputJsonConverter.cs b/src/OpenCode.Sdk/Experimental/Internal/VoiceTransportInputJsonConverter.cs new file mode 100644 index 00000000..d7ddc865 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Internal/VoiceTransportInputJsonConverter.cs @@ -0,0 +1,47 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Collections.Frozen; +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; + +internal sealed class VoiceTransportInputJsonConverter : JsonConverter +{ + private static readonly FrozenDictionary TypesByTag = new Dictionary(StringComparer.Ordinal) + { + ["webrtc"] = typeof(VoiceWebRtcTransportInput), + ["websocket"] = typeof(VoiceWebSocketTransportInput) + }.ToFrozenDictionary(StringComparer.Ordinal); + private static readonly UnionDiscriminatorReader DiscriminatorReader = new(); + public override IVoiceTransportInput Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + if (DiscriminatorReader.TryFindKnown(ref reader, "type", "VoiceTransportInput", TypesByTag, out var targetType, out var marker)) + { + var typeInfo = OpenCodeJsonContext.Default.GetTypeInfo(targetType) ?? throw new JsonException("The generated context has no metadata for VoiceTransportInput."); + return JsonSerializer.Deserialize(ref reader, typeInfo) as IVoiceTransportInput ?? throw new JsonException("The VoiceTransportInput payload deserialized to null."); + } + + using var document = JsonDocument.ParseValue(ref reader); + var payload = document.RootElement; + return new UnknownVoiceTransportInput(marker, payload); + } + + public override void Write(Utf8JsonWriter writer, IVoiceTransportInput value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + ArgumentNullException.ThrowIfNull(options); + if (value is UnknownVoiceTransportInput unknown) + { + unknown.Payload.WriteTo(writer); + return; + } + + var typeInfo = OpenCodeJsonContext.Default.GetTypeInfo(value.GetType()) ?? throw new JsonException("The generated context has no metadata for VoiceTransportInput."); + JsonSerializer.Serialize(writer, value, typeInfo); + } +} diff --git a/src/OpenCode.Sdk/Experimental/Internal/VoiceTransportJsonConverter.cs b/src/OpenCode.Sdk/Experimental/Internal/VoiceTransportJsonConverter.cs new file mode 100644 index 00000000..290ab201 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Internal/VoiceTransportJsonConverter.cs @@ -0,0 +1,47 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Collections.Frozen; +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Models; + +namespace OpenCode.Sdk.Internal; + +internal sealed class VoiceTransportJsonConverter : JsonConverter +{ + private static readonly FrozenDictionary TypesByTag = new Dictionary(StringComparer.Ordinal) + { + ["webrtc"] = typeof(VoiceWebRtcTransport), + ["websocket"] = typeof(VoiceWebSocketTransport) + }.ToFrozenDictionary(StringComparer.Ordinal); + private static readonly UnionDiscriminatorReader DiscriminatorReader = new(); + public override IVoiceTransport Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + if (DiscriminatorReader.TryFindKnown(ref reader, "type", "VoiceTransport", TypesByTag, out var targetType, out var marker)) + { + var typeInfo = OpenCodeJsonContext.Default.GetTypeInfo(targetType) ?? throw new JsonException("The generated context has no metadata for VoiceTransport."); + return JsonSerializer.Deserialize(ref reader, typeInfo) as IVoiceTransport ?? throw new JsonException("The VoiceTransport payload deserialized to null."); + } + + using var document = JsonDocument.ParseValue(ref reader); + var payload = document.RootElement; + return new UnknownVoiceTransport(marker, payload); + } + + public override void Write(Utf8JsonWriter writer, IVoiceTransport value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(value); + ArgumentNullException.ThrowIfNull(options); + if (value is UnknownVoiceTransport unknown) + { + unknown.Payload.WriteTo(writer); + return; + } + + var typeInfo = OpenCodeJsonContext.Default.GetTypeInfo(value.GetType()) ?? throw new JsonException("The generated context has no metadata for VoiceTransport."); + JsonSerializer.Serialize(writer, value, typeInfo); + } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/ExperimentalVoiceSessionData.cs b/src/OpenCode.Sdk/Experimental/Models/ExperimentalVoiceSessionData.cs new file mode 100644 index 00000000..15bc2c27 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/ExperimentalVoiceSessionData.cs @@ -0,0 +1,17 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Models; +/// +/// Represents an experimental voice session data value. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record ExperimentalVoiceSessionData +{ + /// + /// Gets the transport value. + /// + [JsonPropertyName("transport")] + public required IVoiceTransport Transport { get; init; } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/IVoiceTransport.cs b/src/OpenCode.Sdk/Experimental/Models/IVoiceTransport.cs new file mode 100644 index 00000000..34238f11 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/IVoiceTransport.cs @@ -0,0 +1,22 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a voice transport union. +/// +/// +/// Consume this interface; do not implement it outside tests. Its members follow the pinned protocol document and change when the SDK moves to a newer pin, which a minor version may do. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +[JsonConverter(typeof(VoiceTransportJsonConverter))] +public interface IVoiceTransport +{ + /// + /// Gets the 'type' union marker. + /// + [JsonPropertyName("type")] + public string Type { get; } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/IVoiceTransportInput.cs b/src/OpenCode.Sdk/Experimental/Models/IVoiceTransportInput.cs new file mode 100644 index 00000000..ff603133 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/IVoiceTransportInput.cs @@ -0,0 +1,22 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a voice transport input union. +/// +/// +/// Consume this interface; do not implement it outside tests. Its members follow the pinned protocol document and change when the SDK moves to a newer pin, which a minor version may do. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +[JsonConverter(typeof(VoiceTransportInputJsonConverter))] +public interface IVoiceTransportInput +{ + /// + /// Gets the 'type' union marker. + /// + [JsonPropertyName("type")] + public string Type { get; } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/UnknownVoiceTransport.cs b/src/OpenCode.Sdk/Experimental/Models/UnknownVoiceTransport.cs new file mode 100644 index 00000000..de3cf309 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/UnknownVoiceTransport.cs @@ -0,0 +1,47 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Preserves an unknown voice transport payload. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +[JsonConverter(typeof(UnknownVoiceTransportJsonConverter))] +public sealed record UnknownVoiceTransport : IVoiceTransport +{ + private readonly string _marker; + /// + /// Initializes an unknown union value from its marker and raw payload. + /// + [JsonConstructor] + public UnknownVoiceTransport(string type, JsonElement payload) + { + ArgumentException.ThrowIfNullOrWhiteSpace(type); + if (payload.ValueKind is JsonValueKind.Undefined) + { + throw new ArgumentException("The payload must be a parsed JSON element.", nameof(payload)); + } + + UnionPayloadGuard.Instance.RequireString(payload, "type", type); + _marker = type; + Payload = payload.Clone(); + } + + /// + /// Gets the unrecognized 'type' marker. + /// + [JsonPropertyName("type")] + public string Type => _marker; + /// + /// Gets the preserved raw JSON payload. + /// + public JsonElement Payload { get; } + + /// + /// Prints the marker with the preserved payload masked, since a known arm of this union carries a secret. + /// + public override string ToString() => RecordPrinter.Format(nameof(UnknownVoiceTransport), ("Type", Type), ("Payload", RecordPrinter.Redact(Payload))); +} diff --git a/src/OpenCode.Sdk/Experimental/Models/UnknownVoiceTransportInput.cs b/src/OpenCode.Sdk/Experimental/Models/UnknownVoiceTransportInput.cs new file mode 100644 index 00000000..3bf024f5 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/UnknownVoiceTransportInput.cs @@ -0,0 +1,42 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Preserves an unknown voice transport input payload. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +[JsonConverter(typeof(UnknownVoiceTransportInputJsonConverter))] +public sealed record UnknownVoiceTransportInput : IVoiceTransportInput +{ + private readonly string _marker; + /// + /// Initializes an unknown union value from its marker and raw payload. + /// + [JsonConstructor] + public UnknownVoiceTransportInput(string type, JsonElement payload) + { + ArgumentException.ThrowIfNullOrWhiteSpace(type); + if (payload.ValueKind is JsonValueKind.Undefined) + { + throw new ArgumentException("The payload must be a parsed JSON element.", nameof(payload)); + } + + UnionPayloadGuard.Instance.RequireString(payload, "type", type); + _marker = type; + Payload = payload.Clone(); + } + + /// + /// Gets the unrecognized 'type' marker. + /// + [JsonPropertyName("type")] + public string Type => _marker; + /// + /// Gets the preserved raw JSON payload. + /// + public JsonElement Payload { get; } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/VoiceWebRtcTransport.cs b/src/OpenCode.Sdk/Experimental/Models/VoiceWebRtcTransport.cs new file mode 100644 index 00000000..ddf78bfb --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/VoiceWebRtcTransport.cs @@ -0,0 +1,23 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a voice web rtc transport value. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record VoiceWebRtcTransport : IVoiceTransport +{ + /// + /// Gets the type value. + /// + [JsonPropertyName("type")] + public string Type => "webrtc"; + + /// + /// Gets the sdp value. + /// + [JsonPropertyName("sdp")] + public required string Sdp { get; init; } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/VoiceWebRtcTransportInput.cs b/src/OpenCode.Sdk/Experimental/Models/VoiceWebRtcTransportInput.cs new file mode 100644 index 00000000..22fba56d --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/VoiceWebRtcTransportInput.cs @@ -0,0 +1,32 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a voice web rtc transport input value. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record VoiceWebRtcTransportInput : IVoiceTransportInput +{ + /// + /// Gets the type value. + /// + [JsonPropertyName("type")] + public string Type => "webrtc"; + + /// + /// Gets the sdp value. + /// + [JsonPropertyName("sdp")] + public required string Sdp { get; init; } + + /// + /// Gets the dcid value. + /// + [JsonPropertyName("dcid")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] + [JsonConverter(typeof(OptionalOfSpecialNumberJsonConverter))] + public Optional Dcid { get; init; } +} diff --git a/src/OpenCode.Sdk/Experimental/Models/VoiceWebSocketTransport.cs b/src/OpenCode.Sdk/Experimental/Models/VoiceWebSocketTransport.cs new file mode 100644 index 00000000..ca845d20 --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/VoiceWebSocketTransport.cs @@ -0,0 +1,42 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; +using OpenCode.Sdk.Internal; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a voice web socket transport value. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record VoiceWebSocketTransport : IVoiceTransport +{ + /// + /// Gets the type value. + /// + [JsonPropertyName("type")] + public string Type => "websocket"; + + /// + /// Gets the url value. + /// + [JsonPropertyName("url")] + public required string Url { get; init; } + + /// + /// Gets the token value. + /// + [JsonPropertyName("token")] + public required string Token { get; init; } + + /// + /// Gets the expires at value. + /// + [JsonPropertyName("expiresAt")] + [JsonNumberHandling(JsonNumberHandling.AllowNamedFloatingPointLiterals)] + public required double ExpiresAt { get; init; } + + /// + /// Prints the record's members with its secret members masked. + /// + public override string ToString() => RecordPrinter.Format(nameof(VoiceWebSocketTransport), ("Type", Type), ("Url", Url), ("Token", RecordPrinter.Redact(Token)), ("ExpiresAt", ExpiresAt)); +} diff --git a/src/OpenCode.Sdk/Experimental/Models/VoiceWebSocketTransportInput.cs b/src/OpenCode.Sdk/Experimental/Models/VoiceWebSocketTransportInput.cs new file mode 100644 index 00000000..54950d2a --- /dev/null +++ b/src/OpenCode.Sdk/Experimental/Models/VoiceWebSocketTransportInput.cs @@ -0,0 +1,17 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Models; +/// +/// Represents a voice web socket transport input value. +/// +[System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat = "https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/experimental.md#opencode001")] +public sealed record VoiceWebSocketTransportInput : IVoiceTransportInput +{ + /// + /// Gets the type value. + /// + [JsonPropertyName("type")] + public string Type => "websocket"; +} diff --git a/src/OpenCode.Sdk/Hosting/BackgroundService/Internal/Ensure/ServiceElection.cs b/src/OpenCode.Sdk/Hosting/BackgroundService/Internal/Ensure/ServiceElection.cs index 10027b8b..cac21f56 100644 --- a/src/OpenCode.Sdk/Hosting/BackgroundService/Internal/Ensure/ServiceElection.cs +++ b/src/OpenCode.Sdk/Hosting/BackgroundService/Internal/Ensure/ServiceElection.cs @@ -291,7 +291,12 @@ [.. request.Command.Skip(1)], } } - /// The channel's service-config env (the CLI's ServiceConfig.options()), with the caller's entries over it. + /// + /// The channel's service-config env (the CLI's ServiceConfig.options()), with the + /// caller's entries winning at spawn. The pinned server (server-process.ts, service mode) + /// re-applies its saved env over everything inherited, so saved entries win for everything + /// the server reads after startup. + /// private async Task?> OverlayAsync(CancellationToken cancellationToken) { IReadOnlyDictionary? configured = null; diff --git a/src/OpenCode.Sdk/Hosting/BackgroundService/OpenCodeServerEnsureOptions.cs b/src/OpenCode.Sdk/Hosting/BackgroundService/OpenCodeServerEnsureOptions.cs index 45478575..01bf1192 100644 --- a/src/OpenCode.Sdk/Hosting/BackgroundService/OpenCodeServerEnsureOptions.cs +++ b/src/OpenCode.Sdk/Hosting/BackgroundService/OpenCodeServerEnsureOptions.cs @@ -58,7 +58,9 @@ public sealed class OpenCodeServerEnsureOptions /// /// Gets or sets extra environment entries layered onto a spawned contender after the channel's - /// service-config map and before the handoff variable. + /// service-config map and before the handoff variable. An opencode 2.0.32 service re-applies its + /// saved service-config env when it starts, so a key set in both takes the saved value for + /// everything the service reads after startup. /// public IReadOnlyDictionary? Environment { get; set; } diff --git a/src/OpenCode.Sdk/Internal/Serialization/OpenCodeJsonContext.cs b/src/OpenCode.Sdk/Internal/Serialization/OpenCodeJsonContext.cs index f2812ccc..95e56c15 100644 --- a/src/OpenCode.Sdk/Internal/Serialization/OpenCodeJsonContext.cs +++ b/src/OpenCode.Sdk/Internal/Serialization/OpenCodeJsonContext.cs @@ -58,6 +58,9 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(ConfigModelCostTier))] [JsonSerializable(typeof(ConfigModelLimit))] [JsonSerializable(typeof(ConfigModelPricing))] +[JsonSerializable(typeof(ConfigModelReasoningBudget))] +[JsonSerializable(typeof(ConfigModelReasoningSupported))] +[JsonSerializable(typeof(ConfigModelReasoningUnsupported))] [JsonSerializable(typeof(ConfigModelSelection))] [JsonSerializable(typeof(ConfigModelSelectionExplicit))] [JsonSerializable(typeof(ConfigModelSettings))] @@ -116,6 +119,9 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(ExperimentalSessionSkillRequest))] [JsonSerializable(typeof(ExperimentalSessionStatsRequestTools))] [JsonSerializable(typeof(ExperimentalSessionStatsResponseEnvelope))] +[JsonSerializable(typeof(ExperimentalVoiceSessionData))] +[JsonSerializable(typeof(ExperimentalVoiceSessionRequest))] +[JsonSerializable(typeof(ExperimentalVoiceSessionResponseEnvelope))] [JsonSerializable(typeof(FileDiffInfo))] [JsonSerializable(typeof(FileDiffInfoStatus))] [JsonSerializable(typeof(FileNotFoundError))] @@ -157,6 +163,7 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(FsFindResponseEnvelope))] [JsonSerializable(typeof(FsListResponseEnvelope))] [JsonSerializable(typeof(IConfigEntry))] +[JsonSerializable(typeof(IConfigModelReasoning))] [JsonSerializable(typeof(IConnectionInfo))] [JsonSerializable(typeof(ICredentialValue))] [JsonSerializable(typeof(IEvent))] @@ -191,6 +198,8 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(IStreamFailureCause))] [JsonSerializable(typeof(IStreamFailureCause[]))] [JsonSerializable(typeof(IToolContent))] +[JsonSerializable(typeof(IVoiceTransport))] +[JsonSerializable(typeof(IVoiceTransportInput))] [JsonSerializable(typeof(InstallationUpdateAvailable))] [JsonSerializable(typeof(InstallationUpdateAvailableData))] [JsonSerializable(typeof(InstallationUpdated))] @@ -740,6 +749,7 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(TuiToastShowDataVariant))] [JsonSerializable(typeof(UnauthorizedError))] [JsonSerializable(typeof(UnknownConfigEntry))] +[JsonSerializable(typeof(UnknownConfigModelReasoning))] [JsonSerializable(typeof(UnknownConnectionInfo))] [JsonSerializable(typeof(UnknownCredentialValue))] [JsonSerializable(typeof(UnknownError))] @@ -770,6 +780,8 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(UnknownSessionStatus))] [JsonSerializable(typeof(UnknownStreamFailureCause))] [JsonSerializable(typeof(UnknownToolContent))] +[JsonSerializable(typeof(UnknownVoiceTransport))] +[JsonSerializable(typeof(UnknownVoiceTransportInput))] [JsonSerializable(typeof(VcsBase))] [JsonSerializable(typeof(VcsBaseResponseEnvelope))] [JsonSerializable(typeof(VcsBaseSource))] @@ -785,6 +797,10 @@ namespace OpenCode.Sdk.Internal; [JsonSerializable(typeof(VcsMode))] [JsonSerializable(typeof(VcsResponseEnvelope))] [JsonSerializable(typeof(VcsStatusResponseEnvelope))] +[JsonSerializable(typeof(VoiceWebRtcTransport))] +[JsonSerializable(typeof(VoiceWebRtcTransportInput))] +[JsonSerializable(typeof(VoiceWebSocketTransport))] +[JsonSerializable(typeof(VoiceWebSocketTransportInput))] [JsonSerializable(typeof(WebSearchProvider))] [JsonSerializable(typeof(WebSearchResponse))] [JsonSerializable(typeof(WebSearchResult))] diff --git a/src/OpenCode.Sdk/Internal/Serialization/OptionalOfSpecialNumberJsonConverter.cs b/src/OpenCode.Sdk/Internal/Serialization/OptionalOfSpecialNumberJsonConverter.cs new file mode 100644 index 00000000..4fe425ac --- /dev/null +++ b/src/OpenCode.Sdk/Internal/Serialization/OptionalOfSpecialNumberJsonConverter.cs @@ -0,0 +1,65 @@ +// Generated by OpenCode.Sdk.Tools from spec/openapi.json. +// Do not edit by hand — change tools/curation.json or the emitters, then regenerate. +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace OpenCode.Sdk.Internal; + +internal sealed class OptionalOfSpecialNumberJsonConverter : JsonConverter> +{ + public override bool HandleNull => true; + + public override Optional Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(typeToConvert); + ArgumentNullException.ThrowIfNull(options); + if (reader.TokenType == JsonTokenType.Null) + { + return Optional.Null; + } + + if (reader.TokenType == JsonTokenType.String) + { + return new Optional(reader.GetString() switch + { + "NaN" => double.NaN, + "Infinity" => double.PositiveInfinity, + "-Infinity" => double.NegativeInfinity, + _ => throw new JsonException("The JSON string is not a named floating-point literal.") + }); + } + + return new Optional(reader.GetDouble()); + } + + public override void Write(Utf8JsonWriter writer, Optional value, JsonSerializerOptions options) + { + ArgumentNullException.ThrowIfNull(writer); + ArgumentNullException.ThrowIfNull(options); + if (value.Value is null) + { + writer.WriteNullValue(); + return; + } + + if (double.IsNaN(value.Value.Value)) + { + writer.WriteStringValue("NaN"); + return; + } + + if (double.IsPositiveInfinity(value.Value.Value)) + { + writer.WriteStringValue("Infinity"); + return; + } + + if (double.IsNegativeInfinity(value.Value.Value)) + { + writer.WriteStringValue("-Infinity"); + return; + } + + writer.WriteNumberValue(value.Value.Value); + } +} diff --git a/src/OpenCode.Sdk/LanguageModels/Models/ModelCapabilities.cs b/src/OpenCode.Sdk/LanguageModels/Models/ModelCapabilities.cs index 3e7ed872..46757bc8 100644 --- a/src/OpenCode.Sdk/LanguageModels/Models/ModelCapabilities.cs +++ b/src/OpenCode.Sdk/LanguageModels/Models/ModelCapabilities.cs @@ -25,4 +25,11 @@ public sealed record ModelCapabilities /// [JsonPropertyName("output")] public required IReadOnlyList Output { get; init; } + + /// + /// Gets the temperature value. + /// + [JsonPropertyName("temperature")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? Temperature { get; init; } } diff --git a/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContext.cs b/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContext.cs index 182a647c..1b717e77 100644 --- a/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContext.cs +++ b/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContext.cs @@ -22,6 +22,7 @@ namespace OpenCode.Sdk.Tests.Harness; internal sealed class EnsureServiceContext : IAsyncDisposable { private const int RealDaemonPort = 49374; + private const int WindowsReleasePort = 45278; private const int LogTailLines = 40; private static readonly TimeSpan ExitBound = TimeSpan.FromSeconds(15); @@ -294,7 +295,7 @@ private async Task InitializeAsync(CancellationToken cancellationToken) { _runRoot = new TestRunRoot(_fileSystem); _isolation = ServerIsolation.For(_fileSystem, _runRoot.Path); - _port = ReservePort(); + _port = RefuseRealDaemonPort(LoopbackPortReservation.Reserve()); _registrationFile = _fileSystem.Path.Combine( Environment["XDG_STATE_HOME"], "opencode", _channelFile!); await SeedConfigAsync(cancellationToken).ConfigureAwait(false); @@ -304,14 +305,13 @@ private async Task InitializeAsync(CancellationToken cancellationToken) _shimPath = await OpenCodeCommandShim.WriteAsync(_fileSystem, _shimDirectory, cancellationToken).ConfigureAwait(false); } - private static int ReservePort() + internal static int RefuseRealDaemonPort(int port) { - var port = LoopbackPortReservation.Reserve(); - if (port == RealDaemonPort) + if (port is WindowsReleasePort or RealDaemonPort) { throw new InvalidOperationException( - $"The reserved loopback port is {RealDaemonPort.ToString(CultureInfo.InvariantCulture)}, the release " - + "channels' default where the maintainer's real daemon lives; the environment is contaminated. " + $"The reserved loopback port is {port.ToString(CultureInfo.InvariantCulture)} and could belong to a real daemon " + + "(the Windows release default or an explicitly configured service); the environment is contaminated. " + "Refusing to run rather than touch it."); } diff --git a/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContextTests.cs b/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContextTests.cs new file mode 100644 index 00000000..2f179e97 --- /dev/null +++ b/tests/OpenCode.Sdk.Tests/Harness/EnsureServiceContextTests.cs @@ -0,0 +1,29 @@ +using System.Globalization; + +namespace OpenCode.Sdk.Tests.Harness; + +public sealed class EnsureServiceContextTests +{ + [Test] + [Arguments(45278)] + [Arguments(49374)] + public async Task RefuseRealDaemonPort_Should_Refuse_A_Port_That_Could_Belong_To_A_Real_Daemon(int port) + { + var exception = Assert.Throws(() => EnsureServiceContext.RefuseRealDaemonPort(port)); + + await Assert.That(exception.Message).IsEqualTo( + $"The reserved loopback port is {port.ToString(CultureInfo.InvariantCulture)} and could belong to a real daemon " + + "(the Windows release default or an explicitly configured service); the environment is contaminated. " + + "Refusing to run rather than touch it."); + } + + [Test] + [Arguments(45277)] + [Arguments(45279)] + [Arguments(49373)] + [Arguments(49375)] + public async Task RefuseRealDaemonPort_Should_Keep_Any_Other_Reserved_Port(int port) + { + await Assert.That(EnsureServiceContext.RefuseRealDaemonPort(port)).IsEqualTo(port); + } +} diff --git a/tests/OpenCode.Sdk.Tests/Serialization/OpenCodeJsonContextTests.cs b/tests/OpenCode.Sdk.Tests/Serialization/OpenCodeJsonContextTests.cs index 0f9d1a6a..d179f484 100644 --- a/tests/OpenCode.Sdk.Tests/Serialization/OpenCodeJsonContextTests.cs +++ b/tests/OpenCode.Sdk.Tests/Serialization/OpenCodeJsonContextTests.cs @@ -494,6 +494,34 @@ public async Task Deserialize_Should_Round_Trip_A_Declared_Special_Number(string await Assert.That(exit.GetString()).IsEqualTo(wireValue); } + [Test] + [Arguments(null, null)] + [Arguments("null", null)] + [Arguments("7.25", 7.25)] + [Arguments("\"NaN\"", double.NaN)] + [Arguments("\"Infinity\"", double.PositiveInfinity)] + [Arguments("\"-Infinity\"", double.NegativeInfinity)] + public async Task Deserialize_Should_Round_Trip_The_Voice_Dcid_States(string? wireValue, double? expected) + { + var json = wireValue is null + ? """{"type":"webrtc","sdp":"offer"}""" + : $$"""{"type":"webrtc","sdp":"offer","dcid":{{wireValue}}}"""; + + var result = _serializer.Deserialize(json); + + await Assert.That(result.Dcid.IsSet).IsEqualTo(wireValue is not null); + await Assert.That(result.Dcid.Value).IsEqualTo(expected); + await Assert.That(_serializer.Serialize(result)).IsEqualTo(json); + } + + [Test] + public async Task Deserialize_Should_Refuse_A_Lowercase_Named_Literal_For_The_Voice_Dcid() + { + const string json = """{"type":"webrtc","sdp":"offer","dcid":"nan"}"""; + + _ = await Assert.That(() => _serializer.Deserialize(json)).Throws(); + } + [Test] public async Task Deserialize_Should_Preserve_An_Ordinary_Finite_Special_Number() { diff --git a/tests/OpenCode.Sdk.Tests/Snapshots/PublicApi.verified.txt b/tests/OpenCode.Sdk.Tests/Snapshots/PublicApi.verified.txt index fe544133..58b50021 100644 --- a/tests/OpenCode.Sdk.Tests/Snapshots/PublicApi.verified.txt +++ b/tests/OpenCode.Sdk.Tests/Snapshots/PublicApi.verified.txt @@ -149,6 +149,7 @@ namespace OpenCode.Sdk public virtual System.Threading.Tasks.Task AddIntegrationWellknownAsync(OpenCode.Sdk.Models.ExperimentalIntegrationWellknownAddRequest request, OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } public virtual System.Threading.Tasks.Task AddMcpServerAsync(string server, OpenCode.Sdk.Models.ExperimentalMcpAddRequest request, OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } public virtual System.Threading.Tasks.Task ConnectMcpServerAsync(string server, OpenCode.Sdk.ExperimentalMcpConnectRequest? request = null, OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } + public virtual System.Threading.Tasks.Task CreateVoiceSessionAsync(OpenCode.Sdk.Models.ExperimentalVoiceSessionRequest request, OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } public virtual System.Threading.Tasks.Task DisconnectMcpServerAsync(string server, OpenCode.Sdk.ExperimentalMcpDisconnectRequest? request = null, OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } public virtual System.Threading.Tasks.Task GenerateTextAsync(OpenCode.Sdk.Models.ExperimentalGenerateTextRequest request, OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } public virtual System.Threading.Tasks.Task GetMigrationV1StatusAsync(OpenCode.Sdk.OpenCodeRequestOptions? requestOptions = null, System.Threading.CancellationToken cancellationToken = default) { } @@ -327,6 +328,14 @@ namespace OpenCode.Sdk { public ExperimentalSessionWaitResponse() { } } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class ExperimentalVoiceSessionResponse : OpenCode.Sdk.OpenCodeResponse, System.IEquatable + { + public ExperimentalVoiceSessionResponse() { } + public required OpenCode.Sdk.Models.ExperimentalVoiceSessionData VoiceSession { get; init; } + protected override bool PrintMembers(System.Text.StringBuilder builder) { } + } public class FileSystemClient { protected FileSystemClient() { } @@ -2628,6 +2637,9 @@ namespace OpenCode.Sdk.Models [System.Text.Json.Serialization.JsonPropertyName("package")] public string? Package { get; init; } [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("releaseDate")] + public string? ReleaseDate { get; init; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] [System.Text.Json.Serialization.JsonPropertyName("settings")] public OpenCode.Sdk.Models.ConfigModelSettings? Settings { get; init; } [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] @@ -2645,6 +2657,12 @@ namespace OpenCode.Sdk.Models [System.Text.Json.Serialization.JsonPropertyName("output")] public System.Collections.Generic.IReadOnlyList? Output { get; init; } [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("reasoning")] + public OpenCode.Sdk.Models.IConfigModelReasoning? Reasoning { get; init; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("temperature")] + public bool? Temperature { get; init; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] [System.Text.Json.Serialization.JsonPropertyName("tools")] public bool? Tools { get; init; } } @@ -2711,6 +2729,37 @@ namespace OpenCode.Sdk.Models ConfigModelCostList = 1, Unknown = 2, } + public sealed class ConfigModelReasoningBudget : System.IEquatable + { + public ConfigModelReasoningBudget() { } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("max")] + public long? Max { get; init; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("min")] + public long? Min { get; init; } + } + public sealed class ConfigModelReasoningSupported : OpenCode.Sdk.Models.IConfigModelReasoning, System.IEquatable + { + public ConfigModelReasoningSupported() { } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("budget")] + public OpenCode.Sdk.Models.ConfigModelReasoningBudget? Budget { get; init; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("effort")] + public System.Collections.Generic.IReadOnlyList? Effort { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("supported")] + public bool Supported { get; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("toggle")] + public bool? Toggle { get; init; } + } + public sealed class ConfigModelReasoningUnsupported : OpenCode.Sdk.Models.IConfigModelReasoning, System.IEquatable + { + public ConfigModelReasoningUnsupported() { } + [System.Text.Json.Serialization.JsonPropertyName("supported")] + public bool Supported { get; } + } [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.ConfigModelSelectionJsonConverter))] public sealed class ConfigModelSelection : System.IEquatable { @@ -3436,6 +3485,28 @@ namespace OpenCode.Sdk.Models public const string Summary = "summary"; } } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class ExperimentalVoiceSessionData : System.IEquatable + { + public ExperimentalVoiceSessionData() { } + [System.Text.Json.Serialization.JsonPropertyName("transport")] + public required OpenCode.Sdk.Models.IVoiceTransport Transport { get; init; } + } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class ExperimentalVoiceSessionRequest : System.IEquatable + { + public ExperimentalVoiceSessionRequest() { } + [System.Text.Json.Serialization.JsonPropertyName("model")] + public required string Model { get; init; } + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.OptionalOfJsonElementDictionaryJsonConverter))] + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] + [System.Text.Json.Serialization.JsonPropertyName("session")] + public OpenCode.Sdk.Optional?> Session { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("transport")] + public required OpenCode.Sdk.Models.IVoiceTransportInput Transport { get; init; } + } public sealed class FileDiffInfo : System.IEquatable { public FileDiffInfo() { } @@ -4081,6 +4152,12 @@ namespace OpenCode.Sdk.Models [System.Text.Json.Serialization.JsonPropertyName("type")] string Type { get; } } + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.ConfigModelReasoningJsonConverter))] + public interface IConfigModelReasoning + { + [System.Text.Json.Serialization.JsonPropertyName("supported")] + bool Supported { get; } + } [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.ConnectionInfoJsonConverter))] public interface IConnectionInfo { @@ -4356,6 +4433,22 @@ namespace OpenCode.Sdk.Models [System.Text.Json.Serialization.JsonPropertyName("type")] string Type { get; } } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.VoiceTransportJsonConverter))] + public interface IVoiceTransport + { + [System.Text.Json.Serialization.JsonPropertyName("type")] + string Type { get; } + } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.VoiceTransportInputJsonConverter))] + public interface IVoiceTransportInput + { + [System.Text.Json.Serialization.JsonPropertyName("type")] + string Type { get; } + } public sealed class InstallationUpdateAvailable : OpenCode.Sdk.Models.IEvent, System.IEquatable { public InstallationUpdateAvailable() { } @@ -5263,6 +5356,9 @@ namespace OpenCode.Sdk.Models public required System.Collections.Generic.IReadOnlyList Input { get; init; } [System.Text.Json.Serialization.JsonPropertyName("output")] public required System.Collections.Generic.IReadOnlyList Output { get; init; } + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull)] + [System.Text.Json.Serialization.JsonPropertyName("temperature")] + public bool? Temperature { get; init; } [System.Text.Json.Serialization.JsonPropertyName("tools")] public required bool Tools { get; init; } } @@ -11300,6 +11396,15 @@ namespace OpenCode.Sdk.Models public string Type { get; } public override string ToString() { } } + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.UnknownConfigModelReasoningJsonConverter))] + public sealed class UnknownConfigModelReasoning : OpenCode.Sdk.Models.IConfigModelReasoning, System.IEquatable + { + [System.Text.Json.Serialization.JsonConstructor] + public UnknownConfigModelReasoning(bool supported, System.Text.Json.JsonElement payload) { } + public System.Text.Json.JsonElement Payload { get; } + [System.Text.Json.Serialization.JsonPropertyName("supported")] + public bool Supported { get; } + } [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.UnknownConnectionInfoJsonConverter))] public sealed class UnknownConnectionInfo : OpenCode.Sdk.Models.IConnectionInfo, System.IEquatable { @@ -11578,6 +11683,29 @@ namespace OpenCode.Sdk.Models [System.Text.Json.Serialization.JsonPropertyName("type")] public string Type { get; } } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.UnknownVoiceTransportJsonConverter))] + public sealed class UnknownVoiceTransport : OpenCode.Sdk.Models.IVoiceTransport, System.IEquatable + { + [System.Text.Json.Serialization.JsonConstructor] + public UnknownVoiceTransport(string type, System.Text.Json.JsonElement payload) { } + public System.Text.Json.JsonElement Payload { get; } + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string Type { get; } + public override string ToString() { } + } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.UnknownVoiceTransportInputJsonConverter))] + public sealed class UnknownVoiceTransportInput : OpenCode.Sdk.Models.IVoiceTransportInput, System.IEquatable + { + [System.Text.Json.Serialization.JsonConstructor] + public UnknownVoiceTransportInput(string type, System.Text.Json.JsonElement payload) { } + public System.Text.Json.JsonElement Payload { get; } + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string Type { get; } + } public sealed class VcsBase : System.IEquatable { public VcsBase() { } @@ -11724,6 +11852,54 @@ namespace OpenCode.Sdk.Models public const string Working = "working"; } } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class VoiceWebRtcTransport : OpenCode.Sdk.Models.IVoiceTransport, System.IEquatable + { + public VoiceWebRtcTransport() { } + [System.Text.Json.Serialization.JsonPropertyName("sdp")] + public required string Sdp { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string Type { get; } + } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class VoiceWebRtcTransportInput : OpenCode.Sdk.Models.IVoiceTransportInput, System.IEquatable + { + public VoiceWebRtcTransportInput() { } + [System.Text.Json.Serialization.JsonConverter(typeof(OpenCode.Sdk.Internal.OptionalOfSpecialNumberJsonConverter))] + [System.Text.Json.Serialization.JsonIgnore(Condition=System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingDefault)] + [System.Text.Json.Serialization.JsonPropertyName("dcid")] + public OpenCode.Sdk.Optional Dcid { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("sdp")] + public required string Sdp { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string Type { get; } + } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class VoiceWebSocketTransport : OpenCode.Sdk.Models.IVoiceTransport, System.IEquatable + { + public VoiceWebSocketTransport() { } + [System.Text.Json.Serialization.JsonNumberHandling(System.Text.Json.Serialization.JsonNumberHandling.AllowNamedFloatingPointLiterals)] + [System.Text.Json.Serialization.JsonPropertyName("expiresAt")] + public required double ExpiresAt { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("token")] + public required string Token { get; init; } + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string Type { get; } + [System.Text.Json.Serialization.JsonPropertyName("url")] + public required string Url { get; init; } + public override string ToString() { } + } + [System.Diagnostics.CodeAnalysis.Experimental("OPENCODE001", UrlFormat="https://github.com/opencode-dotnet/opencode-sdk-dotnet/blob/master/docs/guide/exp" + + "erimental.md#opencode001")] + public sealed class VoiceWebSocketTransportInput : OpenCode.Sdk.Models.IVoiceTransportInput, System.IEquatable + { + public VoiceWebSocketTransportInput() { } + [System.Text.Json.Serialization.JsonPropertyName("type")] + public string Type { get; } + } public sealed class WebSearchProvider : System.IEquatable { public WebSearchProvider() { } diff --git a/tests/OpenCode.Sdk.Tools.Tests/Snapshots/SuppressionLedgerTests.Ledger_Should_Count_Every_Suppression_In_The_Repository.verified.txt b/tests/OpenCode.Sdk.Tools.Tests/Snapshots/SuppressionLedgerTests.Ledger_Should_Count_Every_Suppression_In_The_Repository.verified.txt index b0aab1fa..ec7bf6f0 100644 --- a/tests/OpenCode.Sdk.Tools.Tests/Snapshots/SuppressionLedgerTests.Ledger_Should_Count_Every_Suppression_In_The_Repository.verified.txt +++ b/tests/OpenCode.Sdk.Tools.Tests/Snapshots/SuppressionLedgerTests.Ledger_Should_Count_Every_Suppression_In_The_Repository.verified.txt @@ -90,7 +90,7 @@ editorconfig .editorconfig [*.{g.cs,generated.cs,designer.cs}] generated_code = editorconfig .editorconfig [src/OpenCode.Sdk.Extensions/OpenCodeServiceCollectionExtensions.cs] dotnet_diagnostic.IDE0130.severity = none editorconfig .editorconfig [src/OpenCode.Sdk/**.cs] dotnet_diagnostic.IDE0130.severity = none editorconfig .editorconfig [src/OpenCode.Sdk/**/Models/**.cs] dotnet_diagnostic.CA1034.severity = none -editorconfig .editorconfig [src/OpenCode.Sdk/**/{ConfigInfoEnterprise,ConnectionStatus,ExperimentalIntegrationWellknownAddRequest,FormExternalField,IntegrationAttempt,McpOAuthConfig,McpRemoteConfig,McpResource,McpResourceTemplate,ProjectIcon,PromptFileSourceUri,PromptInputFileAttachment,ToolFileContent,WebSearchResult}.cs] dotnet_diagnostic.CA1056.severity = none +editorconfig .editorconfig [src/OpenCode.Sdk/**/{ConfigInfoEnterprise,ConnectionStatus,ExperimentalIntegrationWellknownAddRequest,FormExternalField,IntegrationAttempt,McpOAuthConfig,McpRemoteConfig,McpResource,McpResourceTemplate,ProjectIcon,PromptFileSourceUri,PromptInputFileAttachment,ToolFileContent,VoiceWebSocketTransport,WebSearchResult}.cs] dotnet_diagnostic.CA1056.severity = none editorconfig .editorconfig [src/OpenCode.Sdk/Common/Optional.cs] dotnet_diagnostic.CA1000.severity = none editorconfig .editorconfig [src/OpenCode.Sdk/Common/Optional.cs] dotnet_diagnostic.CA2225.severity = none editorconfig .editorconfig [src/OpenCode.Sdk/Hosting/BackgroundService/Internal/Contender/ServiceContender.cs] dotnet_diagnostic.MA0045.severity = none diff --git a/tools/curation.json b/tools/curation.json index 175cf315..ce9ba4f4 100644 --- a/tools/curation.json +++ b/tools/curation.json @@ -2326,6 +2326,11 @@ } ], "operationNames": [ + { + "operationId": "experimental.voice.session", + "methodName": "CreateVoiceSessionAsync", + "reason": "The operation creates a voice session; its closing segment 'session' is not a naming verb." + }, { "operationId": "event.subscribe", "methodName": "SubscribeAsync", @@ -2878,6 +2883,21 @@ } ], "schemaNames": [ + { + "schema": "Config.Model.Reasoning#/anyOf/supported=false", + "dotnetName": "ConfigModelReasoningUnsupported", + "reason": "The supported=false arm declares that the configured model does not support reasoning; name the capability rather than its boolean literal." + }, + { + "schema": "Config.Model.Reasoning#/anyOf/supported=true", + "dotnetName": "ConfigModelReasoningSupported", + "reason": "The supported=true arm carries reasoning toggle, effort and budget capabilities; name the capability rather than its boolean literal." + }, + { + "schema": "Config.Model.Reasoning#/anyOf/supported=true/properties/budget", + "dotnetName": "ConfigModelReasoningBudget", + "reason": "The reasoning budget declares the supported arm's minimum and maximum; name the capability rather than its boolean literal." + }, { "schema": "PersistentPty.CreateInput#/properties/size", "dotnetName": "PtySize", @@ -3177,6 +3197,11 @@ } ], "modelOwners": [ + { + "match": "Voice", + "owner": "Experimental", + "reason": "The voice transport unions and their WebRTC and WebSocket arms belong to the experimental voice session operation alone." + }, { "match": "Prompt", "owner": "Sessions", diff --git a/tools/generation-profile.txt b/tools/generation-profile.txt index 211c2165..e77f66b9 100644 --- a/tools/generation-profile.txt +++ b/tools/generation-profile.txt @@ -28,6 +28,7 @@ experimental.session.instructions.entry.remove experimental.session.skill experimental.session.stats experimental.session.wait +experimental.voice.session form.list fs.find fs.list