From 1eb42feaaea411117ff9328016e31aa9e0ccb3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pascal=20Andr=C3=A9?= Date: Thu, 24 Sep 2026 20:42:08 +0200 Subject: [PATCH] chore(opencode): qualify 2.0.16 with aligned clients and plugin (#772) Align the server/UI client, bundled plugin and recommended runtime at 2.0.16 while retaining the demonstrated 2.0.7 minimum. Verify the real generated-client detailed errors without duplicate proxy prefixes or failed mutation replay; retain explicit installation and daemon restart boundaries. Independent gatekeeper audited all 63 upstream commits and the full comparison, published packages, native consumers and targeted regressions: zero actionable findings. Native pruning/UI and historical migrations pass across Windows, Linux and macOS; minimum/current runtime contracts and dedicated Tauri jobs pass. Generic browser failures were independently reproduced on the exact 2.0.15 base and documented in the published review. Admin squash merge explicitly requested by the user. --- .../codenomad-architecture-guide/SKILL.md | 2 +- .../references/architecture-overview.md | 2 +- .../references/sdk-api-reference.md | 6 +- .../references/sdk-critical-behaviors.md | 4 +- .../references/sdk-integration-patterns.md | 2 +- dev-docs/OPENCODE_V2_COMPATIBILITY.md | 45 ++- package-lock.json | 286 ++++++------------ packages/server/package.json | 4 +- .../src/opencode-update/service.test.ts | 14 +- .../src/opencode/runtime-support.test.ts | 4 +- .../server/src/opencode/runtime-support.ts | 2 +- .../src/opencode/session-pruning/package.json | 2 +- packages/ui/package.json | 2 +- packages/ui/src/lib/opencode-api.ts | 7 +- packages/ui/src/lib/sdk-manager.test.ts | 34 +++ 15 files changed, 198 insertions(+), 218 deletions(-) diff --git a/.opencode/skills/codenomad-architecture-guide/SKILL.md b/.opencode/skills/codenomad-architecture-guide/SKILL.md index 54983a5d3..4e215e49c 100644 --- a/.opencode/skills/codenomad-architecture-guide/SKILL.md +++ b/.opencode/skills/codenomad-architecture-guide/SKILL.md @@ -18,7 +18,7 @@ description: | - Runtime requirements live in `opencode/runtime-support.ts`; each blocking requirement needs demonstrated API/behavior evidence. Distinguish technically incompatible, recommended/tested and unverified versions. Setup uses bundled Node/npm and a versioned user prefix; shared-daemon restart is explicit and uses the existing native-parent launch bridge. Retire obsolete wire translations while retaining current identity/authority checks. See `dev-docs/OPENCODE_V2_POST_BETA.md` for precise retirement boundaries and validation evidence. -- Server and UI pin the official `@opencode/client@2.0.15` together; the pruning plugin pins `@opencode/plugin@2.0.15`. Review official V2 docs, installed declarations, generated wire paths and native integration tests when upgrading. Qualify against the latest published stable runtime, but never infer the minimum from that version or from the dependency pin. Record technical minimum requirements and tested scenarios in PR/CI logs and update `dev-docs/OPENCODE_V2_COMPATIBILITY.md` in place rather than adding per-version reports. The runtime CLI is managed independently. +- Server and UI pin the official `@opencode/client@2.0.16` together; the pruning plugin pins `@opencode/plugin@2.0.16`. Review official V2 docs, installed declarations, generated wire paths and native integration tests when upgrading. Qualify against the latest published stable runtime, but never infer the minimum from that version or from the dependency pin. Record technical minimum requirements and tested scenarios in PR/CI logs and update `dev-docs/OPENCODE_V2_COMPATIBILITY.md` in place rather than adding per-version reports. The runtime CLI is managed independently. - Do not use `@opencode-ai/sdk`, `@opencode-ai/sdk/v2/client`, or `createOpencodeClient()`; follow installed `@opencode/client` declarations. - There is no legacy `packages/opencode-plugin/`. Do not restore the V1 compatibility runtime or add general plugin extension points. The narrow integrations are the bundled `codenomad.automation` plugin and bundled session-pruning RPC; see `dev-docs/DEVELOPER_MODE.md`, `dev-docs/BROWSER_AUTOMATION.md` and `dev-docs/SESSION_PRUNING_RPC.md`. All automation tools follow backend presence without a Developer Mode gate, sharing the authenticated native transport and execution-time session/window fences. - The server uses the selected host or WSL CLI's official `service status`, `service start`, and `service get password` lifecycle to connect to one externally owned global OpenCode daemon. It owns no private port/database/registration/PID and never stops the daemon on backend shutdown. WSL requires Windows localhost forwarding and uses no cross-namespace PID operations. diff --git a/.opencode/skills/codenomad-architecture-guide/references/architecture-overview.md b/.opencode/skills/codenomad-architecture-guide/references/architecture-overview.md index 2fe039fdc..feef0e72f 100644 --- a/.opencode/skills/codenomad-architecture-guide/references/architecture-overview.md +++ b/.opencode/skills/codenomad-architecture-guide/references/architecture-overview.md @@ -20,7 +20,7 @@ Client-state V3 is a per-window envelope over the V2 content-addressed partition | Owner | Responsibilities | Main paths | |---|---|---| -| OpenCode V2 | Sessions, messages, permissions, Forms, files, session Shell/instructions, background Shells, interactive PTYs | pinned stable `@opencode/client@2.0.15` contract across server and UI | +| OpenCode V2 | Sessions, messages, permissions, Forms, files, session Shell/instructions, background Shells, interactive PTYs | pinned stable `@opencode/client@2.0.16` contract across server and UI | | CodeNomad server | Shared service lifecycle, locations, proxy authorization, Git mutations, Yolo, auth, storage, speech, SSE multiplexing, Developer Mode bridge | `packages/server/src/` | | CodeNomad UI | Generated Promise clients, state reconciliation, interaction and rendering | `packages/ui/src/` | | Desktop hosts | Start CodeNomad and provide native OS integration | `packages/electron-app/`, `packages/tauri-app/` | diff --git a/.opencode/skills/codenomad-architecture-guide/references/sdk-api-reference.md b/.opencode/skills/codenomad-architecture-guide/references/sdk-api-reference.md index 1732d9302..91bf2e3c4 100644 --- a/.opencode/skills/codenomad-architecture-guide/references/sdk-api-reference.md +++ b/.opencode/skills/codenomad-architecture-guide/references/sdk-api-reference.md @@ -2,11 +2,11 @@ ## Package -CodeNomad server and UI pin `@opencode/client@2.0.15`. The runtime CLI is managed independently; startup validates authenticated loopback `/api/status`, then `/api/health`, then `/api/info`, advancing only on HTTP 404 with the same endpoint, credentials and deadline. Each response has its own validated schema and a 64 KiB bound. The shared transport maps canonical `server.info()` to the discovered route. Older services do not expose `paths.tmp`; consumers may use only the metadata actually provided. Discovery does not prove compatibility for other APIs. Review official V2 docs, installed declarations, generated routes and native regression tests together when upgrading. +CodeNomad server and UI pin `@opencode/client@2.0.16`. The runtime CLI is managed independently; startup validates authenticated loopback `/api/status`, then `/api/health`, then `/api/info`, advancing only on HTTP 404 with the same endpoint, credentials and deadline. Each response has its own validated schema and a 64 KiB bound. The shared transport maps canonical `server.info()` to the discovered route. Older services do not expose `paths.tmp`; consumers may use only the metadata actually provided. Discovery does not prove compatibility for other APIs. Review official V2 docs, installed declarations, generated routes and native regression tests together when upgrading. -Runtime requirements live in `packages/server/src/opencode/runtime-support.ts`: minimum 2.0.7 for native step-start timestamps, independently of recommended/tested 2.0.15. The shared connection binds authenticated runtime identity, the canonical client and forwarding transport. Admission precedes functional requests/plugin provisioning; unlisted versions, including custom/prerelease/future labels, require authenticated bounded API recognition. Legacy request/response/event and live location translations are retired. Never add operation-specific retry fallbacks in UI stores or Yolo. See `dev-docs/OPENCODE_V2_POST_BETA.md` for precise boundaries, setup and migration evidence. +Runtime requirements live in `packages/server/src/opencode/runtime-support.ts`: minimum 2.0.7 for native step-start timestamps, independently of recommended/tested 2.0.16. The shared connection binds authenticated runtime identity, the canonical client and forwarding transport. Admission precedes functional requests/plugin provisioning; unlisted versions, including custom/prerelease/future labels, require authenticated bounded API recognition. Legacy request/response/event and live location translations are retired. Never add operation-specific retry fallbacks in UI stores or Yolo. See `dev-docs/OPENCODE_V2_POST_BETA.md` for precise boundaries, setup and migration evidence. -The Promise client preserves the full `baseUrl` path prefix. Forward its generated URL unchanged through `createInstanceFetch`; remove the proxy prefix only when classifying reads for scheduling. Declared API errors are `Error` instances retaining their native `_tag`/data fields; undeclared statuses remain `ClientError("UnexpectedStatus")`. +The Promise client preserves the full `baseUrl` path prefix. Forward its generated URL unchanged through `createInstanceFetch`; remove the proxy prefix only when classifying reads for scheduling. Declared API errors are `Error` instances retaining their native `_tag`/data fields. Client errors retain structured `reason`/`cause`; 2.0.16 adds status, content type or cause detail to `message`. Classify by structured fields rather than exact message text. - Promise client: `import { OpenCode } from "@opencode/client"` - Service authentication headers: `import { Service } from "@opencode/client/service"` diff --git a/.opencode/skills/codenomad-architecture-guide/references/sdk-critical-behaviors.md b/.opencode/skills/codenomad-architecture-guide/references/sdk-critical-behaviors.md index 55da4198e..d77fdc8a8 100644 --- a/.opencode/skills/codenomad-architecture-guide/references/sdk-critical-behaviors.md +++ b/.opencode/skills/codenomad-architecture-guide/references/sdk-critical-behaviors.md @@ -2,12 +2,12 @@ ## Contract -- Server and UI pin `@opencode/client@2.0.15`. Manage the runtime CLI independently: startup checks authenticated loopback `/api/status`, then `/api/health`, then `/api/info`, advancing only on HTTP 404. All probes share the endpoint, credentials, 64 KiB response bound and absolute deadline; authentication, transport and malformed response failures do not trigger fallback. The shared transport maps canonical `server.info()` to the discovered route. Older services do not provide `paths.tmp`; never infer that path from the backend host. Discovery alone does not prove client/API compatibility. Review documentation, installed declarations and proxy/API parity whenever the client contract changes. +- Server and UI pin `@opencode/client@2.0.16`. Manage the runtime CLI independently: startup checks authenticated loopback `/api/status`, then `/api/health`, then `/api/info`, advancing only on HTTP 404. All probes share the endpoint, credentials, 64 KiB response bound and absolute deadline; authentication, transport and malformed response failures do not trigger fallback. The shared transport maps canonical `server.info()` to the discovered route. Older services do not provide `paths.tmp`; never infer that path from the backend host. Discovery alone does not prove client/API compatibility. Review documentation, installed declarations and proxy/API parity whenever the client contract changes. - The package root is the generated zero-Effect Promise client. Use installed declarations, not current public `@opencode-ai/sdk` examples. - Native routes are `/api/*`; CodeNomad exposes them only through the authorized `/workspaces/:id/instance` proxy. - That proxy is an explicit method/path allowlist. Future upstream APIs are not exposed automatically. - Proxy authorization and forwarding share one acquired connection. A stale generation must be rejected at actual HTTP dispatch, including after asynchronous body preparation; late streams cannot invalidate a replacement connection. -- The technical minimum is 2.0.7: native `session.step.started.data.started` is consumed directly after removing its older fallback. Recommendation/qualification 2.0.15 is independent. Unknown version labels, prereleases and future majors require bounded authenticated API recognition, including session environment support; they are not rejected solely by label. Legacy HTTP inbox and event conversions are retired. Native `session.inbox.enqueued` still has a distinct shape: its timestamp belongs to event metadata, not an HTTP inbox record. +- The technical minimum is 2.0.7: native `session.step.started.data.started` is consumed directly after removing its older fallback. Recommendation/qualification 2.0.16 is independent. Unknown version labels, prereleases and future majors require bounded authenticated API recognition, including session environment support; they are not rejected solely by label. Legacy HTTP inbox and event conversions are retired. Native `session.inbox.enqueued` still has a distinct shape: its timestamp belongs to event metadata, not an HTTP inbox record. - Never retry a write using another contract after a 400/404/transport failure. Keep the setup/recovery path distinct from functional transport and never replay prompts. ## Location Is Authority diff --git a/.opencode/skills/codenomad-architecture-guide/references/sdk-integration-patterns.md b/.opencode/skills/codenomad-architecture-guide/references/sdk-integration-patterns.md index abca8ea75..18f7cc911 100644 --- a/.opencode/skills/codenomad-architecture-guide/references/sdk-integration-patterns.md +++ b/.opencode/skills/codenomad-architecture-guide/references/sdk-integration-patterns.md @@ -10,7 +10,7 @@ OpenCode owns standard state/database. Allowed configured environment variables Workspace creation calls `client.location.get({ location: { directory } })` and records the returned directory. Its `project` field supplies project metadata. Public locations have no workspace selector. Explicit Stop Workspace calls `client.debug.location.evict` before removing the logical workspace. Ordinary tab/window close only detaches local UI and never evicts. -The technical minimum is 2.0.7 for the native step-start timestamp, with 2.0.15 independently recommended/tested; old request/response/event and live location translations are retired. Preserve historical `workspaceID` validation internally: obsolete public selectors must be rejected rather than erased from imports, cursors, pending Forms or move rollback. Native 2.0.3→2.0.7/2.0.15 migration collapses workspace selectors to local directory scope while preserving session IDs. See `dev-docs/OPENCODE_V2_COMPATIBILITY.md` for current qualification and `dev-docs/OPENCODE_V2_POST_BETA.md` for the transition record. +The technical minimum is 2.0.7 for the native step-start timestamp, with 2.0.16 independently recommended/tested; old request/response/event and live location translations are retired. Preserve historical `workspaceID` validation internally: obsolete public selectors must be rejected rather than erased from imports, cursors, pending Forms or move rollback. Native historical migrations collapse workspace selectors to local directory scope while preserving session IDs. See `dev-docs/OPENCODE_V2_COMPATIBILITY.md` for current qualification and `dev-docs/OPENCODE_V2_POST_BETA.md` for the transition record. The instance proxy is method/path allowlisted, rejects unowned paths, `directory`, `location.directory`, and `location[directory]` values, and verifies session location before forwarding. Keep this check at the server trust boundary; new upstream routes require explicit review. diff --git a/dev-docs/OPENCODE_V2_COMPATIBILITY.md b/dev-docs/OPENCODE_V2_COMPATIBILITY.md index 20a0cc692..c1af7b091 100644 --- a/dev-docs/OPENCODE_V2_COMPATIBILITY.md +++ b/dev-docs/OPENCODE_V2_COMPATIBILITY.md @@ -15,7 +15,7 @@ runtimes. Keep detailed results in the change's PR and CI logs, not in per-versi reports. Update this reference in place. PR #696's corrected technical minimum is **2.0.7**, when native step-start events -gain the `data.started` field consumed by the current Solid reducer. **2.0.15** +gain the `data.started` field consumed by the current Solid reducer. **2.0.16** is the recommended release-tested target, not the minimum. Unlisted versions, including prereleases/custom labels/future majors, undergo authenticated contract recognition rather than being refused solely for their label. Missing canonical @@ -74,7 +74,7 @@ it mocks HTTP APIs with `{}` and does not provide the current bounded history query contract. Those tests use synthetic browser data, not a 2.0.14 daemon; they are not native-runtime qualification evidence. -### Current stable target: 2.0.15 +### 2.0.15 qualification baseline Server/UI client, bundled plugin and recommendation advance together to **2.0.15**; the technical minimum stays **2.0.7**. The upstream client now preserves a @@ -116,6 +116,47 @@ startup phase diagnostics for a recurrence; this passing rerun does not establis the cause or a fix for the intermittent exit. Detailed results and remaining CI gates belong in #752 rather than being inferred from other passing platforms. +### Current stable target: 2.0.16 + +Server/UI client, bundled plugin and recommendation advance together to **2.0.16**; +the evidence-based minimum remains **2.0.7**. Comparing the tagged +`packages/protocol/openapi.json` documents for 2.0.15 and 2.0.16 finds identical +paths and component schemas. Published client declarations differ only in the +optional `ClientError` detail; plugin declarations are unchanged. + +Client failures now include status/content-type/cause detail in `message` +([#50929](https://github.com/anomalyco/opencode/pull/50929)). Structured `reason`, +`cause`, and declared native error fields remain available. Generated-client +regressions exercise the real proxy adapter and error formatter for HTTP 500, +wrong content type and transport errors, retaining one prefixed request with +credentials and no mutation replay. The 2.0.15 URL correction remains required. + +The release also adds MCP resource tools and Code Mode operations, provider/media +fixes and runtime read/subagent improvements. These are upstream runtime changes; +this qualification does not expose additional proxy APIs or introduce new media, +provider-authentication or transcript UI workflows. The shared npm native-upgrade +boundary stays at 2.0.15; installer activation remains separate from daemon restart. +Windows qualification with Node 24.20.0 passes 98 server tests, 75 UI tests, +25 setup/auth-recovery browser scenarios and three tool-image browser scenarios, +plus server/UI/Electron typechecks and production server/UI/plugin builds. All +four historical migrations (2.0.3 and beta-19271 to 2.0.7 and 2.0.16) pass. +The 2.0.16 native pruning/UI suite passes through concurrency, persistence and +restart. The isolated npm upgrade advances the installed CLI to 2.0.16 while +preserving the running 2.0.15 daemon's version and PID. + +Native suites pass on both 2.0.7 and 2.0.16 with the 2.0.16 dependencies: +automation/discovery, relay/proxy/ownership, worktrees, Forms/permissions, +history/pruning/concurrency, per-send environment, inclusive forks, idle/busy +side questions and blank-session cleanup. The Git-degraded scenario passes on +2.0.16 and on the isolated minimum-runtime rerun described below. + +The minimum-runtime Git-degraded fixture initially retained the advisory after +PATH recovery; its isolated rerun passes without code changes. Preserve that +failure as unresolved intermittent evidence, not a corrected regression. The +independent gatekeeper reports zero actionable findings. Detailed native suite +completion and cross-platform qualification belong in the PR/CI; do not treat +unchanged wire types or a passing rerun as proof of untested behavior. + The audit and implementation record below is historical context, not a runtime qualification matrix to maintain. diff --git a/package-lock.json b/package-lock.json index 12d793a2d..fd326ca13 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4844,14 +4844,14 @@ } }, "node_modules/@opencode/ai": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@opencode/ai/-/ai-2.0.15.tgz", - "integrity": "sha512-dY3HG6bp+UMJOi+m1nfzvQaTPpCSmMNDswAOLQq/MJghGrD54WrcEC4IhAQ1hJY7b/hhQYOzPAMeJsvF4aRYYQ==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@opencode/ai/-/ai-2.0.16.tgz", + "integrity": "sha512-QNOut+LUDNqMm2gqzr5xiOkj2xxD3JQX/DG3Z358aBJMgYZkDHY+ui20J7Jl7XdudQb4Jot2hIQ7t6q17BpOFw==", "dev": true, "license": "MIT", "dependencies": { "@aws-sdk/credential-providers": "3.1057.0", - "@opencode/schema": "2.0.15", + "@opencode/schema": "2.0.16", "@smithy/eventstream-codec": "4.2.14", "@smithy/util-utf8": "4.2.2", "aws4fetch": "1.0.20", @@ -4860,13 +4860,13 @@ } }, "node_modules/@opencode/client": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@opencode/client/-/client-2.0.15.tgz", - "integrity": "sha512-Lu3Pb6Z/lJ797VryQrsdqEgVBXjtYZWOxLNZKyBUDgXhO/GpzOpXi1H1Uo4g+qWrH8LwUISDBdxHHOWJy5HcqQ==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@opencode/client/-/client-2.0.16.tgz", + "integrity": "sha512-lEZM5NY8cKVwnlIJVHiERIaIMJfWfKTlQvKKkIz7NcNEwUXlrTXOA79kS5n17HtjYer1u1XiQIIggYtEYJXHRw==", "license": "MIT", "dependencies": { - "@opencode/protocol": "2.0.15", - "@opencode/schema": "2.0.15" + "@opencode/protocol": "2.0.16", + "@opencode/schema": "2.0.16" }, "peerDependencies": { "effect": "4.0.0-rc.112", @@ -4882,24 +4882,24 @@ } }, "node_modules/@opencode/plugin": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@opencode/plugin/-/plugin-2.0.15.tgz", - "integrity": "sha512-TsSXd2g/433De7ViY+5d/tlLtkZCSmYKo8ScnhtcmPVoM/U5ptQDFXEXx5psTlAfkKcISJh+5vMSOe4u3uCqUQ==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@opencode/plugin/-/plugin-2.0.16.tgz", + "integrity": "sha512-Ir+7fJNUqo81dfzLAcQ8cZVLREbUxx/jbbiPQFR6wCjKWdJXLtMWJIAnYnYxBLf4Im7GcQQfdnlsG5xT9NYNrQ==", "dev": true, "license": "MIT", "dependencies": { "@ai-sdk/provider": "3.0.8", - "@opencode/ai": "2.0.15", - "@opencode/client": "2.0.15", - "@opencode/protocol": "2.0.15", - "@opencode/schema": "2.0.15", - "@opencode/util": "2.0.15", + "@opencode/ai": "2.0.16", + "@opencode/client": "2.0.16", + "@opencode/protocol": "2.0.16", + "@opencode/schema": "2.0.16", + "@opencode/util": "2.0.16", "@standard-schema/spec": "1.1.0", "effect": "4.0.0-rc.112", "zod": "4.1.8" }, "peerDependencies": { - "@opencode/theme": "2.0.15", + "@opencode/theme": "2.0.16", "@opentui/core": ">=0.5.10", "@opentui/solid": ">=0.5.10", "solid-js": ">=1.9.0" @@ -4930,19 +4930,19 @@ } }, "node_modules/@opencode/protocol": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@opencode/protocol/-/protocol-2.0.15.tgz", - "integrity": "sha512-fFi3VYdgNQzT4TswfKGul7DzseOyhA4b/hE/elUe3xxm5iki4tP/pjT8CU3rRWX8sK/75ICr36NAUhSayFtJuw==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@opencode/protocol/-/protocol-2.0.16.tgz", + "integrity": "sha512-TQQOXLngSH4D9Z/B6DkyioFsHyApaczjU9EdD5BJ0UtwihKs3iT5mLQlicctKzcWomNoCqIM45XEjBv+WrSynA==", "license": "MIT", "dependencies": { - "@opencode/schema": "2.0.15", + "@opencode/schema": "2.0.16", "effect": "4.0.0-rc.112" } }, "node_modules/@opencode/schema": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@opencode/schema/-/schema-2.0.15.tgz", - "integrity": "sha512-D0JDwF4uTNmFA2MyiSnZ5DlT/t2PF1EEwVbRPulB/Ppcss/z5Fcn6VybHrapki2bKDVQu1Mnn8zBW3G6KP5I6g==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@opencode/schema/-/schema-2.0.16.tgz", + "integrity": "sha512-MXENqPxsEzv77AAQDZsyYM4kSqUSApGEKD3xQ6kDDDvm5f5SHMffcNQ+uJXbh7rFo7Fm7nzdvoDHeZwiXm2UvQ==", "license": "MIT", "dependencies": { "@standard-schema/spec": "1.1.0", @@ -4950,9 +4950,9 @@ } }, "node_modules/@opencode/util": { - "version": "2.0.15", - "resolved": "https://registry.npmjs.org/@opencode/util/-/util-2.0.15.tgz", - "integrity": "sha512-s0Z+2rxb+2Tif+ZV1EvTiGQ0tRiemIZq3WOOjBw2XPBSBt2Sfv9rWXJhTzSpkOZucfTyymuIVXslXPGFF7aaQA==", + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/@opencode/util/-/util-2.0.16.tgz", + "integrity": "sha512-VfY65xppJgSQSXT3x5hH3jM6e5Z08pYEaRA3sKv1K7I8jIFNma8IGZXSoYWaSXPnDt1svxd1sZ9b7tA1RRhOGw==", "dev": true, "license": "MIT", "dependencies": { @@ -4962,10 +4962,14 @@ "@npmcli/arborist": "9.4.0", "@npmcli/config": "10.8.1", "@opentelemetry/api": "1.9.0", - "@opentelemetry/context-async-hooks": "2.6.1", - "@opentelemetry/exporter-trace-otlp-http": "0.214.0", - "@opentelemetry/sdk-trace-base": "2.6.1", - "@opentelemetry/sdk-trace-node": "2.6.1", + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/context-async-hooks": "2.8.0", + "@opentelemetry/exporter-trace-otlp-http": "0.219.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/sdk-logs": "0.219.0", + "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0", + "@opentelemetry/sdk-trace-node": "2.8.0", "cross-spawn": "7.0.6", "effect": "4.0.0-rc.112", "glob": "13.0.5", @@ -5108,9 +5112,9 @@ } }, "node_modules/@opentelemetry/api-logs": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.214.0.tgz", - "integrity": "sha512-40lSJeqYO8Uz2Yj7u94/SJWE/wONa7rmMKjI1ZcIjgf3MHNHv1OZUCrCETGuaRF62d5pQD1wKIW+L4lmSMTzZA==", + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.219.0.tgz", + "integrity": "sha512-FFx7YnaYJlIjqWW/AG/yAZ0L/NEY724PipXXXQLdtZPbLwBGbUMTGL1i/esI56TWfTUXxhLfpgrnWJCG8aUJyg==", "dev": true, "license": "Apache-2.0", "peer": true, @@ -5122,9 +5126,9 @@ } }, "node_modules/@opentelemetry/context-async-hooks": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.6.1.tgz", - "integrity": "sha512-XHzhwRNkBpeP8Fs/qjGrAf9r9PRv67wkJQ/7ZPaBQQ68DYlTBBx5MF9LvPx7mhuXcDessKK2b+DcxqwpgkcivQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-2.8.0.tgz", + "integrity": "sha512-/3FIraneMcng67SUJCxvyInk/oxzwsxyadufk0wwfOBLf5wqtAGX4MoQASwSbndBPeARzBryUM9Azr5kHIdWLw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5135,9 +5139,9 @@ } }, "node_modules/@opentelemetry/core": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.6.1.tgz", - "integrity": "sha512-8xHSGWpJP9wBxgBpnqGL0R3PbdWQndL1Qp50qrg71+B28zK5OQmUgcDKLJgzyAAV38t4tOyLMGDD60LneR5W8g==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.8.0.tgz", + "integrity": "sha512-hd1Lfh8p545nNz+jq1Ejfz+Mn1hyLuxYn1YzTfFNrxr8urEWMNQLPf1Th8kjOH+HxwawCrtgBp8JpBUR4ZSgww==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -5151,17 +5155,17 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.214.0.tgz", - "integrity": "sha512-kIN8nTBMgV2hXzV/a20BCFilPZdAIMYYJGSgfMMRm/Xa+07y5hRDS2Vm12A/z8Cdu3Sq++ZvJfElokX2rkgGgw==", + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.219.0.tgz", + "integrity": "sha512-9t6SvBXXBEjOBcIzgozvBbd3jWrv3Gt3ngGhl1fhdZ/zRc7oZDVOFEqbi2zlBpW9BXhgDMKv422J0DL/3iQWfw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/otlp-exporter-base": "0.214.0", - "@opentelemetry/otlp-transformer": "0.214.0", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/sdk-trace-base": "2.6.1" + "@opentelemetry/core": "2.8.0", + "@opentelemetry/otlp-exporter-base": "0.219.0", + "@opentelemetry/otlp-transformer": "0.219.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -5171,14 +5175,14 @@ } }, "node_modules/@opentelemetry/otlp-exporter-base": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.214.0.tgz", - "integrity": "sha512-u1Gdv0/E9wP+apqWf7Wv2npXmgJtxsW2XL0TEv9FZloTZRuMBKmu8cYVXwS4Hm3q/f/3FuCnPTgiwYvIqRSpRg==", + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.219.0.tgz", + "integrity": "sha512-zvIxQX/AZUVKDU+hCuYx+7UkiP7GRdnk1ZbFQRYzHvYp47cAWR4j3IhoPhV9KaeXEv2xdGq3IA6PnpzDmLcmSA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/otlp-transformer": "0.214.0" + "@opentelemetry/core": "2.8.0", + "@opentelemetry/otlp-transformer": "0.219.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -5188,19 +5192,18 @@ } }, "node_modules/@opentelemetry/otlp-transformer": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.214.0.tgz", - "integrity": "sha512-DSaYcuBRh6uozfsWN3R8HsN0yDhCuWP7tOFdkUOVaWD1KVJg8m4qiLUsg/tNhTLS9HUYUcwNpwL2eroLtsZZ/w==", + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.219.0.tgz", + "integrity": "sha512-aaYKAyXhw9VchKZVGOopD3Gw/kPsyrX2c6IQ0AW32mTjqmZOh5Y6Gf5OYqTNqVktAeBjmFinhyFaCwW6GYK9YQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.214.0", - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", - "@opentelemetry/sdk-logs": "0.214.0", - "@opentelemetry/sdk-metrics": "2.6.1", - "@opentelemetry/sdk-trace-base": "2.6.1", - "protobufjs": "^7.0.0" + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", + "@opentelemetry/sdk-logs": "0.219.0", + "@opentelemetry/sdk-metrics": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -5210,14 +5213,14 @@ } }, "node_modules/@opentelemetry/resources": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.6.1.tgz", - "integrity": "sha512-lID/vxSuKWXM55XhAKNoYXu9Cutoq5hFdkbTdI/zDKQktXzcWBVhNsOkiZFTMU9UtEWuGRNe0HUgmsFldIdxVA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.8.0.tgz", + "integrity": "sha512-qmXQ27ilDbUK/vGMqwL8D4/rhn76C+sherM4wTbjlfknR8Nvfc/hCxjRJPhkzZzUsPiNg16SA31NxMabwttRjg==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@opentelemetry/core": "2.6.1", + "@opentelemetry/core": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -5228,16 +5231,16 @@ } }, "node_modules/@opentelemetry/sdk-logs": { - "version": "0.214.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.214.0.tgz", - "integrity": "sha512-zf6acnScjhsaBUU22zXZ/sLWim1dfhUAbGXdMmHmNG3LfBnQ3DKsOCITb2IZwoUsNNMTogqFKBnlIPPftUgGwA==", + "version": "0.219.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.219.0.tgz", + "integrity": "sha512-s6lTKRakaPClvKoWHRChxnXjDMkM/TQ30ff78jN6EBGf7MI7VzANE5PU3f4z9qDUudWjvZjOLHG0rBnBKYvoXA==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@opentelemetry/api-logs": "0.214.0", - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", + "@opentelemetry/api-logs": "0.219.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -5248,15 +5251,15 @@ } }, "node_modules/@opentelemetry/sdk-metrics": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.6.1.tgz", - "integrity": "sha512-9t9hJHX15meBy2NmTJxL+NJfXmnausR2xUDvE19XQce0Qi/GBtDGamU8nS1RMbdgDmhgpm3VaOu2+fiS/SfTpQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.8.0.tgz", + "integrity": "sha512-UDBGaj6W0Rgy5rTTaoxs8gVGF/aGkAKyjurJv7se6wjRxJu7FoquTLT/vt54DZfo4crbprYfhX/SOK9+BPw1qg==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1" + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -5266,15 +5269,15 @@ } }, "node_modules/@opentelemetry/sdk-trace-base": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.6.1.tgz", - "integrity": "sha512-r86ut4T1e8vNwB35CqCcKd45yzqH6/6Wzvpk2/cZB8PsPLlZFTvrh8yfOS3CYZYcUmAx4hHTZJ8AO8Dj8nrdhw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.8.0.tgz", + "integrity": "sha512-mhU4jp+vW0mGbFRd+GeXHvmfA4aDqWjBjLC3pE5XMpLs0IE2ryYb019Ts2AQrOq67gaTF25D91+fgvEHDZEnuQ==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@opentelemetry/core": "2.6.1", - "@opentelemetry/resources": "2.6.1", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/resources": "2.8.0", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { @@ -5285,16 +5288,16 @@ } }, "node_modules/@opentelemetry/sdk-trace-node": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.6.1.tgz", - "integrity": "sha512-Hh2i4FwHWRFhnO2Q/p6svMxy8MPsNCG0uuzUY3glqm0rwM0nQvbTO1dXSp9OqQoTKXcQzaz9q1f65fsurmOhNw==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-node/-/sdk-trace-node-2.8.0.tgz", + "integrity": "sha512-nZt9OGufioAc3AfoLTqA9bsAeaMJAictYDdI2VcNQ+PmT+3rfKjAZDZvgPfd8VPX0O5Bw1hdQF6kDK8VSpZiWg==", "dev": true, "license": "Apache-2.0", "peer": true, "dependencies": { - "@opentelemetry/context-async-hooks": "2.6.1", - "@opentelemetry/core": "2.6.1", - "@opentelemetry/sdk-trace-base": "2.6.1" + "@opentelemetry/context-async-hooks": "2.8.0", + "@opentelemetry/core": "2.8.0", + "@opentelemetry/sdk-trace-base": "2.8.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -5340,72 +5343,6 @@ "url": "https://opencollective.com/popperjs" } }, - "node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@protobufjs/utf8": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz", - "integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/@quansync/fs": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.0.0.tgz", @@ -12252,13 +12189,6 @@ "dev": true, "license": "MIT" }, - "node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/lowercase-keys": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", @@ -14219,30 +14149,6 @@ "url": "https://github.com/sponsors/wooorm" } }, - "node_modules/protobufjs": { - "version": "7.6.6", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz", - "integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==", - "dev": true, - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.1", - "@protobufjs/fetch": "^1.1.1", - "@protobufjs/float": "^1.0.2", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.1", - "@types/node": ">=13.7.0", - "long": "^5.3.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -18560,7 +18466,7 @@ "@fastify/cors": "^8.5.0", "@fastify/reply-from": "^9.8.0", "@fastify/static": "^7.0.4", - "@opencode/client": "2.0.15", + "@opencode/client": "2.0.16", "commander": "^12.1.0", "fastify": "^4.28.1", "fuzzysort": "^2.0.4", @@ -18576,7 +18482,7 @@ "codenomad": "dist/bin.js" }, "devDependencies": { - "@opencode/plugin": "2.0.15", + "@opencode/plugin": "2.0.16", "@types/node-forge": "^1.3.14", "@types/yauzl": "^2.10.0", "cross-env": "^7.0.3", @@ -18616,7 +18522,7 @@ "dependencies": { "@git-diff-view/solid": "^0.0.8", "@kobalte/core": "0.13.11", - "@opencode/client": "2.0.15", + "@opencode/client": "2.0.16", "@solidjs/router": "^0.13.0", "@suid/icons-material": "^0.9.0", "@suid/material": "^0.19.0", diff --git a/packages/server/package.json b/packages/server/package.json index 63020151c..d58a05044 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -30,7 +30,7 @@ "@fastify/cors": "^8.5.0", "@fastify/reply-from": "^9.8.0", "@fastify/static": "^7.0.4", - "@opencode/client": "2.0.15", + "@opencode/client": "2.0.16", "commander": "^12.1.0", "fastify": "^4.28.1", "fuzzysort": "^2.0.4", @@ -44,7 +44,7 @@ }, "devDependencies": { "esbuild": "^0.25.12", - "@opencode/plugin": "2.0.15", + "@opencode/plugin": "2.0.16", "@types/node-forge": "^1.3.14", "@types/yauzl": "^2.10.0", "cross-env": "^7.0.3", diff --git a/packages/server/src/opencode-update/service.test.ts b/packages/server/src/opencode-update/service.test.ts index 1e1ce4403..4f0f8589f 100644 --- a/packages/server/src/opencode-update/service.test.ts +++ b/packages/server/src/opencode-update/service.test.ts @@ -277,29 +277,29 @@ test("beta version comparison remains numeric", () => { assert.equal(compareOpenCodeVersionStrings("0.0.0-beta-10000", "0.0.0-beta-9999") > 0, true) }) -test("2.0.7 through 2.0.14 remain usable; recommendation only offers an optional update", async () => { - for (const version of ["2.0.7", "2.0.8", "2.0.9", "2.0.10", "2.0.11", "2.0.12", "2.0.13", "2.0.14", "2.0.15"]) { +test("2.0.7 through 2.0.16 remain usable; recommendation only offers an optional update", async () => { + for (const version of ["2.0.7", "2.0.8", "2.0.9", "2.0.10", "2.0.11", "2.0.12", "2.0.13", "2.0.14", "2.0.15", "2.0.16"]) { const endpoint: Endpoint = { url: "http://127.0.0.1:9876" } rememberRuntime(endpoint, { version, pid: 123, discovery: "info" }) const service = new OpenCodeUpdateService(deps({ - probeBinary: () => ({ valid: true, version }), resolveLatestVersion: async () => "2.0.15", + probeBinary: () => ({ valid: true, version }), resolveLatestVersion: async () => "2.0.16", lifecycle: async () => ({ discover: async () => endpoint, ensure: async () => { throw new Error("must retain daemon") } }), })) const status = await service.start() assert.equal(status.minimumVersion, "2.0.7") - assert.equal(status.recommendedVersion, "2.0.15") + assert.equal(status.recommendedVersion, "2.0.16") assert.equal(status.state, "ready") assert.equal(status.serviceState, "ready") - assert.equal(status.versionAssessment, version === "2.0.15" ? "tested" : "untested", "only the current recommendation is release-qualified") + assert.equal(status.versionAssessment, version === "2.0.16" ? "tested" : "untested", "only the current recommendation is release-qualified") assert.equal(status.incompatibilityReason, undefined) - assert.equal(status.canUpgrade, version !== "2.0.15") + assert.equal(status.canUpgrade, version !== "2.0.16") assert.equal(status.canRestart, false) } }) test("a current daemon remains usable through an older selected discovery CLI", async () => { const endpoint: Endpoint = { url: "http://127.0.0.1:9876" } - rememberRuntime(endpoint, { version: "2.0.15", pid: 123, discovery: "info" }) + rememberRuntime(endpoint, { version: "2.0.16", pid: 123, discovery: "info" }) const service = new OpenCodeUpdateService(deps({ probeBinary: () => ({ valid: true, version: "2.0.3" }), lifecycle: async () => ({ discover: async () => endpoint, ensure: async () => { throw new Error("must retain daemon") } }), diff --git a/packages/server/src/opencode/runtime-support.test.ts b/packages/server/src/opencode/runtime-support.test.ts index 5e4c71c56..af4f796f5 100644 --- a/packages/server/src/opencode/runtime-support.test.ts +++ b/packages/server/src/opencode/runtime-support.test.ts @@ -7,8 +7,8 @@ import { OpenCodeSharedService } from "../workspaces/opencode-service" test("native timestamp minimum is distinct from recommendation and unfamiliar version labels", () => { assert.equal(MINIMUM_OPENCODE_VERSION, "2.0.7") - assert.equal(RECOMMENDED_OPENCODE_VERSION, "2.0.15") - for (const version of ["2.0.7", "2.0.8", "2.0.9", "2.0.10", "2.0.11", "2.0.12", "2.0.13", "2.0.14", "2.0.15", "2.1.0", "2.0.15-dev.1", "3.0.0", "custom-build"]) { + assert.equal(RECOMMENDED_OPENCODE_VERSION, "2.0.16") + for (const version of ["2.0.7", "2.0.8", "2.0.9", "2.0.10", "2.0.11", "2.0.12", "2.0.13", "2.0.14", "2.0.15", "2.0.16", "2.1.0", "2.0.16-dev.1", "3.0.0", "custom-build"]) { assert.equal(isBelowOpenCodeMinimum(version), false, version) } for (const version of ["2.0.0", "2.0.4", "2.0.5", "2.0.6", "0.0.0-beta-19507"]) assert.equal(isBelowOpenCodeMinimum(version), true, version) diff --git a/packages/server/src/opencode/runtime-support.ts b/packages/server/src/opencode/runtime-support.ts index 348da0034..869eeb3c1 100644 --- a/packages/server/src/opencode/runtime-support.ts +++ b/packages/server/src/opencode/runtime-support.ts @@ -5,7 +5,7 @@ */ export const MINIMUM_OPENCODE_VERSION = "2.0.7" /** Qualification target, independent of the technically required minimum. */ -export const RECOMMENDED_OPENCODE_VERSION = "2.0.15" +export const RECOMMENDED_OPENCODE_VERSION = "2.0.16" export const OPENCODE_UPDATE_REQUIRED = "opencode_update_required" export function isBelowOpenCodeMinimum(version: string): boolean { diff --git a/packages/server/src/opencode/session-pruning/package.json b/packages/server/src/opencode/session-pruning/package.json index 389b07970..775577457 100644 --- a/packages/server/src/opencode/session-pruning/package.json +++ b/packages/server/src/opencode/session-pruning/package.json @@ -17,7 +17,7 @@ "transaction.ts", "tui.ts", "tui-reload.ts" ], "dependencies": { - "@opencode/plugin": "2.0.15", + "@opencode/plugin": "2.0.16", "zod": "^3.25.76" } } diff --git a/packages/ui/package.json b/packages/ui/package.json index 44e43e34e..1ae01df21 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -14,7 +14,7 @@ "dependencies": { "@git-diff-view/solid": "^0.0.8", "@kobalte/core": "0.13.11", - "@opencode/client": "2.0.15", + "@opencode/client": "2.0.16", "@solidjs/router": "^0.13.0", "@suid/icons-material": "^0.9.0", "@suid/material": "^0.19.0", diff --git a/packages/ui/src/lib/opencode-api.ts b/packages/ui/src/lib/opencode-api.ts index 9334b154a..0e922117d 100644 --- a/packages/ui/src/lib/opencode-api.ts +++ b/packages/ui/src/lib/opencode-api.ts @@ -14,10 +14,9 @@ export function getOpencodeErrorMessage(error: unknown, fallback: string): strin const nested = extract(candidate.cause) ?? extract(candidate.error) ?? extract(candidate.body) if (nested) return nested - // The generated client reports undeclared HTTP statuses as a bare reason - // (for example "UnexpectedStatus" with { status: 500 } as the cause), - // which hides the real failure in send dialogs. Surface the status code - // only when no deeper detail exists. + // Classify undeclared HTTP statuses by structured reason/cause, independently + // of the client's message formatting (2.0.16 adds detail to that message). + // Surface the status code only when no deeper detail exists. if (candidate.reason === "UnexpectedStatus") { const status = Number(candidate.cause?.status) if (Number.isInteger(status) && status >= 100 && status <= 599) return `Unexpected status ${status}` diff --git a/packages/ui/src/lib/sdk-manager.test.ts b/packages/ui/src/lib/sdk-manager.test.ts index 08023e7c3..387a68b2b 100644 --- a/packages/ui/src/lib/sdk-manager.test.ts +++ b/packages/ui/src/lib/sdk-manager.test.ts @@ -4,6 +4,7 @@ import { OpenCode } from "@opencode/client" import { buildInstanceBaseUrl, createInstanceFetch, sdkManager } from "./sdk-manager.ts" import { tGlobal } from "./i18n" +import { getOpencodeErrorMessage } from "./opencode-api" afterEach(() => { sdkManager.destroyClientsForInstance("instance-a") @@ -11,6 +12,39 @@ afterEach(() => { }) describe("SDKManager", () => { + it("keeps detailed client failures actionable without replaying a mutation", async () => { + const original = globalThis.fetch + const baseUrl = buildInstanceBaseUrl("/workspaces/first/instance", "https://codenomad.test/tenant") + const client = OpenCode.make({ baseUrl, fetch: createInstanceFetch(baseUrl) }) + const cases = [ + { response: () => new Response("private upstream body", { status: 500 }), reason: "UnexpectedStatus", + message: "UnexpectedStatus: 500", display: "Unexpected status 500" }, + { response: () => new Response("private HTML", { headers: { "content-type": "text/html" } }), reason: "UnsupportedContentType", + message: "UnsupportedContentType: text/html", display: "UnsupportedContentType: text/html" }, + { response: () => { throw new TypeError("Fixture connection refused") }, reason: "Transport", + message: "Transport: Fixture connection refused", display: "Fixture connection refused" }, + ] + try { + for (const fixture of cases) { + let calls = 0 + globalThis.fetch = async (input, init) => { + calls++ + assert.equal(String(input), `${baseUrl}api/session/owned/prompt`) + assert.equal(init?.method, "POST") + assert.equal(init?.credentials, "include") + return fixture.response() + } + await assert.rejects(client.session.prompt({ sessionID: "owned", text: "fixture" }), error => { + assert.ok(error instanceof Error) + assert.equal((error as Error & { reason: string }).reason, fixture.reason) + assert.equal(error.message, fixture.message) + assert.equal(getOpencodeErrorMessage(error, "fallback"), fixture.display) + return true + }) + assert.equal(calls, 1) + } + } finally { globalThis.fetch = original } + }) it("preserves the generated client's proxy prefix exactly once for instructions and declared errors", async () => { const original = globalThis.fetch const requests: string[] = []