dsh-opencode-go 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -31,6 +31,14 @@ Verified versions: `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`, `0.1.6-alpha.2`,
31
31
 
32
32
  Then open **Settings → OpenCode Go**, enter and save your API key, and select an OpenCode Go model in a conversation.
33
33
 
34
+ The plugin can also be installed by entering this Git repository URL:
35
+
36
+ ```text
37
+ https://github.com/Duskriver/dsh-opencode-go
38
+ ```
39
+
40
+ The current default branch includes the compiled plugin. Installation needs neither a local build nor a plugin-specific `allowBuilds` entry. URLs pinned to older commits retain those commits' installation behavior; update them to the current branch.
41
+
34
42
  If your DSH version does not have an **Add plugin** entry, use the command-line method below.
35
43
 
36
44
  ### Command-line installation (alternative)
@@ -75,12 +83,26 @@ The model ID must be available in the current gateway catalog. Web and Headless
75
83
  To build from source and install a local package:
76
84
 
77
85
  ```sh
78
- npm ci --legacy-peer-deps
86
+ npm ci
87
+ npm run compile
79
88
  npm pack
80
89
  dsh plugin --profile web add ./dsh-opencode-go-0.1.15.tgz
81
90
  ```
82
91
 
83
- The development dependencies include real test packages from multiple DSH generations, so installation requires `--legacy-peer-deps`. For Headless, replace `web` with `headless`.
92
+ Source development builds the plugin explicitly with `npm run compile`; `--legacy-peer-deps` is not required. Multi-version DSH compatibility tests use independent environments and are not installed during ordinary installation or builds. For Headless, replace `web` with `headless`.
93
+
94
+ ### Development and verification
95
+
96
+ ```sh
97
+ npm run compile # Regenerate lib/ and commit it with source changes
98
+ npm test # Core tests, rebuilding the plugin first
99
+ npm run check:dist # Verify shipped artifacts match the source
100
+ npm run test:compat # One tarball tested in 8 independent DSH environments
101
+ npm run test:install # npm / pnpm Git installs without plugin build approval
102
+ npm run verify # All of the above
103
+ ```
104
+
105
+ Compatibility and installation checks download dependencies, use system temporary directories, and clean up afterward. To check one host, run `npm run test:compat -- v017-rc2`. See the [development guide](docs/development.md) for maintaining host versions.
84
106
 
85
107
  ## Updating the plugin
86
108
 
@@ -150,11 +172,13 @@ A reasoning-capable model without adjustable reasoning levels (for example, `uni
150
172
 
151
173
  A model that does offer adjustable levels also declares a default effort (`high` when the model offers it, otherwise the highest level it offers) whenever its transport would answer an unset effort with an explicit disable (`deepseek`, `zai`, `qwen`, `qwen-chat-template`). DSH uses that default when no level has been chosen, so leaving the control unset still sends a reasoning level instead of turning thinking off. Transports that leave the choice to the provider declare no default and are unchanged, and an explicitly chosen level always wins.
152
174
 
153
- If the online configuration is temporarily unavailable, the plugin prefers a configuration fetched successfully earlier in the process and falls back to pi-ai's built-in metadata. If a gateway catalog refresh fails, ongoing requests and Settings retain the last successful list. A failure from either source shows a warning, its cause, and the last successful checks of the model listing and model configuration separately. An initial failure without a cache never invents a model list.
175
+ If the online configuration is temporarily unavailable, the plugin prefers previously fetched configuration, including disk data from before a restart, and falls back to pi-ai's built-in metadata. If a gateway catalog refresh fails, ongoing requests and Settings retain the last successful list from the current process. A failure from either source shows a warning, its cause, and the last successful checks of the model listing and model configuration separately. An initial gateway failure never invents a model list from cached metadata.
176
+
177
+ Model configuration is saved to `$DSH_HOME/cache/dsh-opencode-go/models.dev.api.json`, or `~/.dsh/cache/dsh-opencode-go/models.dev.api.json` when `DSH_HOME` is unset. This contains only public metadata, its ETag, and the last successful check time; it excludes API keys and gateway listings. After a restart, ordinary reads verify the gateway listing and use disk configuration while revalidating metadata in the background. The completed refresh updates conversation pickers. Corrupt caches fall back to the network, and write failures do not discard online results. Settings refreshes always wait for online verification, retaining old configuration with a diagnostic on failure; a disk hit is not reported as an online success. To clear the disk cache, exit DSH and delete this file.
154
178
 
155
179
  `refreshMinutes` controls the cache lifetime after a successful refresh. Failed refreshes become eligible for retry on the next read after 5, 10, 20, 40, then at most 60 seconds; there is no background polling when nothing reads the catalog. Explicit Settings refreshes and previously unknown model requests bypass this delay. Cancelling model resolution or generation immediately ends that caller's catalog wait while other callers can continue sharing the same refresh.
156
180
 
157
- Verified model configurations can serve generation immediately for five minutes after their cache lifetime expires, while triggering one shared background refresh. This window is measured from each source's last successful check; failed retries do not extend it. Beyond that window, requests wait for the next due refresh, retaining the existing fallback behavior if it fails. Initial loads, unknown models, and manual refreshes wait for results. Listing requests have a 10-second deadline; the larger model metadata download has its own 30-second deadline.
181
+ Verified model configurations can serve generation immediately for five minutes after their cache lifetime expires, while triggering one shared background refresh. This window is measured from each source's last successful check; failed retries do not extend it. Beyond that window, requests wait for the next due refresh, retaining the existing fallback behavior if it fails. Initial loads without a disk cache, unknown models, and manual refreshes wait for online results. Listing requests have a 10-second deadline; the larger model metadata download has its own 30-second deadline. JSON requests still use uncompressed transfer for compatibility with hosts affected by the HTTP/2 decompression issue.
158
182
 
159
183
  Continuation, retries, and restored sessions reuse the Host's durable session ID. Forks and subagent sessions use their own IDs, separate from the parent. Standalone requests without a session ID receive a fresh random identifier each time.
160
184
 
package/README.md CHANGED
@@ -31,6 +31,14 @@
31
31
 
32
32
  然后打开 **设置 → OpenCode Go**,填入 API Key 并保存,即可在会话中选择 OpenCode Go 模型。
33
33
 
34
+ 也支持在添加插件时直接输入 Git 仓库地址:
35
+
36
+ ```text
37
+ https://github.com/Duskriver/dsh-opencode-go
38
+ ```
39
+
40
+ 当前主分支附带编译好的插件,安装时无需本地构建,也无需为本插件修改 `allowBuilds`。固定到旧提交的 Git 地址仍沿用旧提交的安装方式,请更新到当前主分支。
41
+
34
42
  若当前 DSH 没有「添加插件」入口,可使用下面的命令行方式。
35
43
 
36
44
  ### 命令行安装(备选)
@@ -75,12 +83,26 @@ dsh --profile headless --patch ./headless.patch.yml "你好"
75
83
  如需从源码构建并安装本地包:
76
84
 
77
85
  ```sh
78
- npm ci --legacy-peer-deps
86
+ npm ci
87
+ npm run compile
79
88
  npm pack
80
89
  dsh plugin --profile web add ./dsh-opencode-go-0.1.15.tgz
81
90
  ```
82
91
 
83
- 开发依赖包含多代 DSH 的真实测试包,安装时需要 `--legacy-peer-deps`。Headless 用户将 `web` 换成 `headless`。
92
+ 源码开发时显式运行 `npm run compile`;普通用户直接安装预编译产物,无需 `--legacy-peer-deps`。多版本 DSH 兼容性测试使用独立环境,不参与普通安装或构建。Headless 用户将 `web` 换成 `headless`。
93
+
94
+ ### 开发与验证
95
+
96
+ ```sh
97
+ npm run compile # 更新 lib/,与源码一起提交
98
+ npm test # 基础功能测试(自动重新构建)
99
+ npm run check:dist # 检查已提交产物是否与源码一致
100
+ npm run test:compat # 同一安装包在 8 套独立 DSH 环境中测试
101
+ npm run test:install # npm / pnpm 无额外构建授权的 Git 安装检查
102
+ npm run verify # 执行以上全部检查
103
+ ```
104
+
105
+ 兼容性与安装检查会下载依赖,使用系统临时目录并在结束后清理。只检查一个宿主可运行 `npm run test:compat -- v017-rc2`。维护测试版本的方式见[开发与测试说明](docs/development.md)。
84
106
 
85
107
  ## 升级插件
86
108
 
@@ -150,11 +172,13 @@ DSH 0.1.5 的卸载只影响本次请求;DSH 0.1.6 及以上版本通过宿主
150
172
 
151
173
  提供可调节档位的模型,如果其传输协议在未选择档位时会显式关闭思考(`deepseek`、`zai`、`qwen`、`qwen-chat-template`),还会声明一个默认档位(模型支持 `high` 时用 `high`,否则用它提供的最高档位)。用户未选择档位时 DSH 使用该默认值,因此留空也会带上推理档位,而不是关闭思考。由服务商自行决定的协议不声明默认值,行为不变;显式选择的档位始终优先。
152
174
 
153
- 在线配置暂时不可达时,优先复用本次运行中成功获取的配置,以 pi-ai 内置配置作为备用。网关目录刷新失败时,已有请求和设置页均保留上次成功获取的列表。任一来源刷新失败,设置页都会显示警告、失败原因,以及模型名单和模型配置各自上次成功检查的时间。首次读取就失败且没有缓存时,不会凭空生成模型列表。
175
+ 在线配置暂时不可达时,优先复用之前成功获取的配置(包括重启前的磁盘缓存),以 pi-ai 内置配置作为备用。网关目录刷新失败时,已有请求和设置页均保留本次运行中上次成功获取的列表。任一来源刷新失败,设置页都会显示警告、失败原因,以及模型名单和模型配置各自上次成功检查的时间。首次读取网关失败时,不会仅凭元数据缓存生成模型列表。
176
+
177
+ 模型配置自动保存到 `$DSH_HOME/cache/dsh-opencode-go/models.dev.api.json`,未设置 `DSH_HOME` 时位于 `~/.dsh/cache/dsh-opencode-go/models.dev.api.json`。缓存仅包含公共元数据、ETag 和上次成功检查时间,不包含 API Key 或网关模型名单。重启后的普通读取先确认网关名单,再使用磁盘中的配置,同时后台校验元数据;后台完成后同步模型选择器。缓存损坏时重新联网获取,无法写入时仍可使用在线结果。设置页刷新始终等待在线校验,失败会保留旧配置并显示原因,不会把缓存命中标记为在线刷新成功。需要清除磁盘缓存时,可退出 DSH 后删除该文件。
154
178
 
155
179
  `refreshMinutes` 控制成功刷新后的目录缓存时长。刷新失败后,下一次读取会按 5、10、20、40、60 秒的退避间隔尝试恢复,最长等待间隔为 60 秒;没有读取时不会后台轮询。设置页主动刷新和未知模型的即时发现不受这个间隔限制。取消模型解析或生成时,会立即结束当前调用的目录等待,其他调用仍可继续使用同一次共享刷新。
156
180
 
157
- 已验证的模型配置在缓存到期后的 5 分钟内可直接用于生成,同时触发一次共享后台刷新。这个窗口按两种来源各自上次成功检查的时间计算,失败重试不会延长它;超过窗口后,请求等待下一次到期刷新,刷新仍失败时保留已有回退行为。首次加载、未知模型和手动刷新会等待结果。模型名单请求限时 10 秒,体积更大的模型配置下载单独限时 30 秒。
181
+ 已验证的模型配置在缓存到期后的 5 分钟内可直接用于生成,同时触发一次共享后台刷新。这个窗口按两种来源各自上次成功检查的时间计算,失败重试不会延长它;超过窗口后,请求等待下一次到期刷新,刷新仍失败时保留已有回退行为。没有磁盘缓存的首次加载、未知模型和手动刷新会等待在线结果。模型名单请求限时 10 秒,体积更大的模型配置下载单独限时 30 秒。JSON 请求仍使用非压缩传输,以兼容存在 HTTP/2 解压问题的宿主。
158
182
 
159
183
  同一会话继续、重试以及恢复后的请求沿用宿主保存的会话 ID。分叉和子会话使用各自的 ID,不复用父会话标识;没有会话 ID 的独立请求每次生成随机标识。
160
184
 
@@ -0,0 +1,64 @@
1
+ # Build and compatibility environments
2
+
3
+ The root package contains one coherent DSH 0.1.6-alpha.1 development environment, the plugin's runtime dependencies, build tools, and core test tools. The published runtime peer ranges remain unchanged. `tests/hosts/*` are independent fixture packages, not npm workspaces. Their dependencies and lockfiles are never installed by root `npm install`, `npm ci`, or `npm pack`.
4
+
5
+ ## Commands
6
+
7
+ | Command | Purpose |
8
+ | --- | --- |
9
+ | `npm ci` | Install the locked development dependencies without rebuilding the plugin |
10
+ | `npm run compile` | Type-check and regenerate the committed host, client, and declaration artifacts in `lib/` |
11
+ | `npm run check:dist` | Rebuild in a temporary directory and compare every file against the shipped `lib/` without changing it |
12
+ | `npm run test:ci` | Verify shipped artifacts match source, then run the core suite |
13
+ | `npm test` | Rebuild, then run the core Vitest suite |
14
+ | `npm run test:compat` | Pack once and test the installed artifact across every supported host fixture |
15
+ | `npm run test:compat -- v015-rc1 v017-rc2` | Test only the named fixtures; unknown names fail |
16
+ | `npm run test:install` | Test npm and pnpm 11.7.0 Git installs of prebuilt artifacts without plugin build approval |
17
+ | `npm run test:install -- npm` / `-- pnpm` | Run just one package manager; unknown names fail |
18
+ | `npm run verify` | Run core, compatibility, and Git-installation checks |
19
+
20
+ The checks use loopback gateways and local fixtures, not paid inference or an installed user profile. Compatibility and installation checks need registry access; installation checks also require Git. Temporary consumers are created outside the repository and removed on success or failure. Tests never implicitly skip a missing host environment.
21
+
22
+ ## Build before distribution
23
+
24
+ The default Git branch ships `lib/` so a GitHub URL, an npm release, and an npm tarball all supply ready-to-load code. There are no `prepare`, `prepack`, `prepublish`, or dependency installation hooks. The explicit build command is named `compile` because npm also treats a script named `build` as a reason to prepare a Git dependency and install its development dependencies. `prepublishOnly` checks artifact freshness when a maintainer publishes to npm; it does not execute during dependency installation.
25
+
26
+ After source or build-tool changes, run `npm run compile` and commit the regenerated `lib/` with those changes. `npm run check:dist` checks missing, changed, and extra artifacts against an independent rebuild. CI uses `npm run test:ci` so it checks the committed files before running tests, instead of silently replacing stale files first. CSS module filenames are repository-relative, CSS export keys are sorted, and declaration line endings are fixed to LF so artifacts are independent of the checkout path and operating system.
27
+
28
+ The Git-installation regression creates a fresh repository containing the shipped artifacts but no `src/`, `scripts/`, `tests/`, or `node_modules/`, then installs it with both package managers. Each installed package must expose its host and client entrypoints, declarations, build metadata, and importable host module, and must not declare scripts that trigger Git preparation or dependency installation hooks. pnpm receives no approval for this plugin; only unrelated Google/protobuf scripts are explicitly skipped. The cold Git identity changes for each temporary repository, preventing a previously approved Git build from hiding a missing approval. Consumer profiles and their trust settings are never modified by these tests.
29
+
30
+ ## Installed-package matrix
31
+
32
+ `scripts/compatibility-hosts.mjs` lists the eight host generations. Each `tests/hosts/<id>` has a private manifest and its own lockfile. DSH packages and their required DSH peer/dependency closure are pinned to that host generation, together with compatible Cordis packages. This prevents a broad upstream peer range from silently selecting a later host generation.
33
+
34
+ The runner performs these steps for each host:
35
+
36
+ 1. Copy the manifest and lockfile into a fresh consumer outside the checkout.
37
+ 2. Run `npm ci --strict-peer-deps` to validate that host's dependency contracts.
38
+ 3. Install the same plugin tarball used by every other host.
39
+ 4. Check that the host's pinned packages retained their versions and the plugin is an actual installed directory.
40
+ 5. Copy the fixtures into the consumer and run them in separate Node processes.
41
+
42
+ Local compatibility runs are serial by default. Set `DSH_COMPAT_CONCURRENCY` to a positive integer to run that many isolated hosts at once; CI uses four workers and prefers its restored npm download cache, fetching missing packages as needed. Each host keeps its own dependency installation and fixture processes, and all workers consume the same tarball. A failed host does not skip queued hosts or delete files still used by running checks: the runner collects every result before cleanup. Logs are grouped by host, and the console and GitHub job summary report each result and elapsed time.
43
+
44
+ All host JavaScript imports use ordinary Node resolution. The fixtures do not redirect DSH imports to alternate packages or load the plugin from the source checkout. The client fixture uses the consumer's React, store, and UI primitives, with a CSS loader for published host styles. It verifies the installed browser factory, settings registration, catalog injection, rendering, CSS, and cleanup. Host fixtures retain text streaming, real image processing, history/offload, limits, reasoning, live settings, and profile-bundle checks.
45
+
46
+ There is one explicit npm prerelease exception: DSH evaluates plugin ranges with prereleases included, whereas npm does not generally accept a future prerelease under `>=0.1.5-rc.1`. Only the tarball-install step uses `--legacy-peer-deps` after the host has passed strict installation. This is not a project `.npmrc` policy and does not affect root installs. The post-install version assertions ensure npm did not replace pinned host packages. Changing the plugin's published version policy is a separate decision.
47
+
48
+ ## Updating a fixture
49
+
50
+ Add or update the entry in `scripts/compatibility-hosts.mjs`, its private manifest, and its lockfile together. Pin each DSH package reached through dependencies and required peers to the intended host generation; do not upgrade an old host merely to satisfy a newer package. Keep compatible Cordis versions and the client fixture's React, DOM, CSS, and UI dependencies explicit.
51
+
52
+ Generate a lockfile in a clean temporary directory containing that fixture's manifest:
53
+
54
+ ```sh
55
+ npm install --package-lock-only --ignore-scripts --strict-peer-deps
56
+ ```
57
+
58
+ Copy the resulting lockfile back to its fixture directory. Generating it outside the checkout prevents an existing root `node_modules` from influencing resolution. Then run the selected compatibility check and the full verification before release. Root development dependency changes require the same strict resolution discipline.
59
+
60
+ The GitHub workflow runs core and Git-installation checks on Linux and Windows with Node 22.19.0 and 24. Core tests, npm installs, and pnpm installs run as independent jobs, so one slow installation does not consume another check's time allowance. Installation jobs need no root `npm ci`: their runner uses only Node built-ins, and the fresh Git fixture supplies committed artifacts and must not need build dependencies or plugin build approval. The complete installed-host matrix runs separately on Linux/Node 24.
61
+
62
+ Installation jobs reuse the npm download cache and a separate pnpm store, preferring cached packages while still fetching missing data. They never restore the checkout's `node_modules` or `lib/`. CI keeps the npm cache, isolated consumers, and the cached pnpm store under `RUNNER_TEMP`, on the runner's work volume. `DSH_PNPM_STORE_DIR` selects the pnpm store; local runs use the system temporary directory and the default package caches unless overridden.
63
+
64
+ Every Git-installation run reports fixture preparation, installation, artifact verification, cleanup, and total elapsed time. GitHub job summaries include a phase table. CI also enables npm's internal timing records and uploads the `*-timing.json` files for seven days, for diagnosing dependency resolution and extraction; a timeout may prevent the still-running process from writing its final timing file. The 15-minute job limit remains unchanged.
@@ -10,4 +10,6 @@ Published `@deepseek-ai/dsh-llm-pi-ai` versions `0.1.5-rc.2` and `0.1.6-alpha.1`
10
10
 
11
11
  The build uses esbuild and lightningcss directly. Node imports stay external; the browser factory follows DSH's `window.__ModuleLoader__.load` format, embeds CSS Modules, and allows only the declared platform module imports. A package-level builder is sufficient for this single external plugin; extracting a general DSH build-tool package would require a separate release and is not a prerequisite for installation.
12
12
 
13
+ Multi-version host fixtures have independent manifests and lockfiles, outside the root npm dependency graph. Package installation builds only the plugin. Compatibility verification installs one generated tarball into isolated consumers and uses their ordinary module resolution; it does not substitute selected host modules into the root process. See [development and verification](development.md).
14
+
13
15
  The initial source derives from the user's working copy of DeepSeek Harness on branch `oh-my-dsh`, whose HEAD was `2c8555b323f3b49eac743bafd527f9f3238bf548`, including the uncommitted OpenCode Go settings changes present when this project was created. Runtime conversion modules retain their published behavior. The original checkout is unchanged by this extraction.
@@ -1,5 +1,67 @@
1
1
  # Verification
2
2
 
3
+ ## Release 0.1.16 integration (2026-09-29)
4
+
5
+ The metadata-cache fix was rebased onto the merged settings-page changes from PR #21. The combined release passed Host/Client type checks and the production build, **347 tests in 24 files**, artifact freshness checks, all **eight installed-host generations**, and **npm/pnpm Git installation without plugin build approval**. Validation ran on macOS / Node 24.14.1 using isolated consumers and fixture endpoints. The earlier 336-test result below describes the cache change before integrating the latest main branch.
6
+
7
+ ## Issue #22: persistent model metadata (2026-09-29)
8
+
9
+ Public models.dev metadata now survives process restarts in `DSH_HOME/cache/dsh-opencode-go/models.dev.api.json` (default home: `~/.dsh`). The versioned record contains the source URL, raw document, ETag, and last successful download/revalidation time. Reads validate the envelope, bound both the file size and bytes read, and run the document through the existing metadata converter for the current gateway. Unique temporary files and atomic rename prevent concurrent writers from publishing partial JSON. Cache read/write failures cannot discard a usable online result. Gateway listings and credentials are not persisted.
10
+
11
+ An ordinary cold catalog read with cached metadata waits for the live gateway listing and can then serve configured models while one shared online refresh completes. Manual discovery and unknown-model resolution wait for that refresh. The completed snapshot updates the picker; a late refresh from a replaced catalog cannot notify the current adapter, and unmount clears the registration. Disk reads do not mark metadata live or advance its success time. Failed revalidation preserves cached configuration and its previous timestamp, retains diagnostics, and follows the existing retry backoff. A successful HTTP 304 updates the persisted verification time and validator. The gateway still decides membership, including an empty listing; a cold gateway outage never advertises models solely from disk.
12
+
13
+ Regression coverage includes cross-instance recovery, nonblocking startup with a held metadata response, concurrent manual/unknown reads, cancellation, fast metadata versus delayed listings, background failure/backoff, picker notifications, changed gateways, HTTP 304, corrupt/oversized/unsupported cache records, invalid timestamps/validators, failed writes, concurrent writers, and empty/unreachable listings. Core tests and installed-host fixture processes use isolated DSH homes so they cannot read or modify a user's cache. The new cache tests failed against the original implementation before the fix.
14
+
15
+ Validation: `npm test` passed Host/Client type checks, regenerated the shipped artifacts, and passed **336 tests in 24 files**. The tightened response-ordering regression and all **21 cache tests** also passed. `npm run check:dist` verified the shipped artifacts. The installed tarball passed all **eight host generations** with `DSH_COMPAT_CONCURRENCY=4 npm_config_prefer_offline=true npm run test:compat`. A separate two-process check against `lib/index.js` downloaded fixture metadata in the first process, then recovered the model in a second process with the metadata endpoint offline; `metadataLive` remained false and the successful timestamp was unchanged. Checks ran on macOS / Node 24.14.1 with local or mocked endpoints and no inference calls. Existing upstream missing-source-map warnings remain.
16
+
17
+ Requests retain `accept-encoding: identity` to preserve the Issue #7 host compatibility fix. This change improves startup after a successful download; a first installation without cache still needs the online metadata request. Compression negotiation is a separate transport change.
18
+
19
+ ## Settings page cards with the tuning fields last (2026-09-29)
20
+
21
+ The settings page now reads top to bottom as the values a user has to supply. The first card carries the connection: the enable switch and the API key together, because the switch decides whether the provider is served at all and the key is what it authenticates with. The second card carries the gateway listing and its capacity editor. The adapter tuning fields, the credential reference among them, sit in a collapsed card at the foot of the page instead of a disclosure in the title row, and the model card takes whatever height is left instead of a height guessed from the window.
22
+
23
+ Measured on the shipped client artifact rendered at the section's slot: the model area is **650 × 334 px**, where the previous layout held it at **378 px** in the same window, and that height is now the parameter card's own content rather than a constant subtracted from the container. The page content measures **887 px** against the reference window's **828 px** viewport, so the page scrolls about **59 px** to reach the tuning card; a taller window removes that scroll and gives the extra height to the model area, capped at 560 px. The tuning grid reports two equal **315.8 px** columns when opened. The collapsed card keeps one row whose `aria-controls` target the client compatibility fixture still holds to `display: flex` and `cursor: pointer`.
24
+
25
+ `npm test` passes **325 tests in 23 files** — the section suite grew by two, one holding the page order (switch, key, model list, tuning) and one holding the switch and the key control inside a single card — `npm run typecheck` passes, and `npm run check:dist` reports all **30** shipped build files matching the source.
26
+
27
+ ## Git installation without plugin build approval (2026-09-28)
28
+
29
+ DSH Desktop's pnpm 11.7.0 rejected commit `68e5840` with `ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED`: the Git source declared `prepare`, while the desktop profile had no build approval for this plugin. The earlier CI explicitly approved its Git fixture, so it established installation after approval rather than the default user experience. Removing that approval from `npm run test:install -- pnpm` reproduced the same error in 1.4 seconds. This entry supersedes the earlier policy of building the plugin during Git installation.
30
+
31
+ The default branch now includes the 30 prebuilt Host/Client/declaration files under `lib/` and declares no Git-preparation or dependency-installation hooks. The explicit maintainer build is named `compile`, also avoiding npm's special treatment of a script named `build`. CI independently rebuilds into a temporary directory and compares every file before running the core suite; the npm publish hook performs the same check. CSS module paths and export ordering, plus declaration line endings, are deterministic across checkouts and operating systems. Controlled checks rejected changed content, a missing declaration, and an extra empty artifact without overwriting them.
32
+
33
+ Local validation passed **314 tests in 23 files**, all **eight installed-host environments**, and both npm and pnpm Git installations from a fixture with no source, build scripts, test fixtures, or plugin build approval. The [branch workflow](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36445611175) passed **13/13 jobs** in **89 seconds**, including artifact freshness and core checks on Linux/Windows with Node 22.19.0/24, plus the unchanged installation and compatibility matrix.
34
+
35
+ A separate cold-store test used Desktop's bundled Node 24.21.0 and pnpm 11.7.0 to install the real GitHub URL at commit `22c6d38`. Its temporary consumer copied the desktop profile's linker, peer-installation, and build-trust settings, with no `dsh-opencode-go` approval. Installation passed in **8.7 seconds**, and its Host/Client entrypoints, declarations, and build metadata matched the verified artifact byte for byte. The user's desktop profile was not changed. This fixes future default-branch Git installs; URLs pinned to older source-only commits retain the older behavior. No new npm version was published.
36
+
37
+ ## Bounded parallel compatibility checks (2026-09-28)
38
+
39
+ The installed-host runner now tests up to four independent consumers at once in CI, using one shared package tarball. Each consumer still performs strict host installation, artifact installation, pinned-version checks, and every existing Host/Client/profile/bundle fixture. Local runs default to one worker; `DSH_COMPAT_CONCURRENCY` selects a positive worker count. The runner reports per-host results and timings, groups each host's logs, and waits for active and queued work to settle before removing temporary consumers, including after failures. CI prefers restored npm downloads and still fetches missing packages.
40
+
41
+ The [serial baseline](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36438860747) spent **113 seconds** in `npm run test:compat`. The [four-worker run](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36440959685) took **74 seconds** in that step; [adding cache preference](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36441411147) reduced the observed step to **66 seconds**, including build/pack, all eight hosts, and cleanup. That is a **42% reduction** from the baseline. The latter workflow passed **13/13 jobs** in **89 seconds** from creation to its final update, compared with **171 seconds** for the baseline. The baseline also had a longer runner-provisioning wait, so the full-workflow difference is not entirely a code speedup. These are individual hosted-runner observations, not guaranteed timings.
42
+
43
+ The Linux/Windows and Node 22.19.0/24 coverage, eight package-manager installation jobs, and eight compatibility hosts remain intact; no macOS CI job was added. Local validation passed the build and **314 tests in 23 files**, the full four-worker installed-host matrix, and a selected-host run with the default serial setting. Three scheduler regressions verify bounded concurrency with immediate slot reuse, draining active/queued work while preserving multiple failures, and rejecting invalid concurrency before work starts. CLI validation also rejected unknown hosts and invalid worker counts before package construction. Both measured branch workflows passed all 13 jobs.
44
+
45
+ ## Parallel Git-installation CI and cache placement (2026-09-28)
46
+
47
+ The first [main-branch workflow](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36411675701) passed all core tests, the eight-host compatibility matrix, and both Git installs on Linux and Windows/Node 24. Windows/Node 22.19.0 passed its npm Git install after about ten minutes, then exceeded the shared 15-minute job limit during pnpm preparation. No installation assertion failed before cancellation.
48
+
49
+ Core tests, npm installs, and pnpm installs now run in independent jobs across the same Linux/Windows and Node 22.19.0/24 matrix. The installation runner accepts an optional package manager, uses no checkout dependencies, and reports preparation, installation, artifact validation, and cleanup times in job summaries. CI restores package downloads, adds a pnpm-store cache, prefers cached downloads, and uploads npm's internal timing JSON. The fresh Git source still excludes `tests/`, `node_modules/`, and `lib/`; all installed-artifact and ESM checks remain enabled.
50
+
51
+ The [first instrumented run](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36437774394) passed all 13 jobs. Its Windows/Node 22.19.0 npm preparation spent **129.1 seconds** unpacking dependencies in the system-drive cache, while builds took about six seconds. Moving only the installation jobs' npm cache to `RUNNER_TEMP` also moved npm's internal Git preparation onto the work volume. In the [subsequent run](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36438357442), that unpack phase took **9.5 seconds**, the complete npm install phase took **38.4 seconds**, and the npm installation/validation/cleanup script took **40.1 seconds**. Its npm cache was a miss, so this reduction did not depend on an already-restored npm cache. The Windows/Node 22.19.0 pnpm check completed in **34.3 seconds**. These are individual hosted-runner observations; VM and cache conditions vary.
52
+
53
+ The subsequent run passed **13/13 jobs** in about **91 seconds** from the first job start to the last completion, including **311 core tests in 22 files** on all four OS/Node combinations, all eight installed-host environments, and all eight OS/Node/package-manager installation checks. The 15-minute per-job limit is unchanged. Local validation also passed both package managers with a new pnpm store, produced phase summaries and nested npm timing files, and rejected an unsupported package-manager argument before creating a fixture.
54
+
55
+ ## Issue #19: isolate compatibility fixtures from plugin builds (2026-09-28)
56
+
57
+ The root package now has one coherent development dependency tree and installs with `npm ci --strict-peer-deps`. The eight published-Host fixtures have independent manifests and lockfiles outside npm workspaces. Git installation builds the plugin through `prepare`; it does not install the compatibility fixtures. This supersedes the workspace, dependency-alias, JavaScript resolution-hook, and root `--legacy-peer-deps` instructions in earlier entries.
58
+
59
+ `npm run test:compat` builds one tarball and installs that same artifact into eight temporary consumers outside the checkout. Each host dependency tree is first installed with strict peer validation. Only the subsequent plugin artifact installation uses `--legacy-peer-deps` to accommodate the difference between npm's prerelease range semantics and DSH's `includePrerelease` admission policy; the runner then checks that the pinned DSH versions have not changed. These checks establish runtime compatibility with those hosts, not strict npm peer-range acceptance for every prerelease host.
60
+
61
+ Validation passed on macOS with Node.js **22.19.0** and **24.14.1**: Host/Client type checks and build, **311 core tests in 22 files**, all **eight installed-package Host/Client compatibility environments**, and clean Git installations with **npm 11.11.0** and **pnpm 11.7.0**. The minimum-version Node 22.19.0 run passed the complete `npm run verify` command. Both installation regressions use a source repository with the entire `tests/` directory removed, then check the installed entrypoints, declarations, build metadata, and ESM loading. Existing compatibility assertions were moved into the isolated runners, and distributed-client coverage increased from five host versions to eight.
62
+
63
+ Published UI dependencies still emit missing-source-map warnings, and pnpm reports upstream peer warnings while installation and artifact checks pass. CI now covers Linux and Windows on Node 22.19.0 and 24, plus the eight-host matrix on Linux. Windows and the new GitHub workflow have not been executed locally. Tests use local fixtures; no live model request or package publication was performed.
64
+
3
65
  ## Optional request image count cap and usage panel polish (plugin 0.1.15, 2026-09-26)
4
66
 
5
67
  The OpenCode Go settings page now exposes `maxImages` as an optional positive integer. It has no default count limit, so existing image capability is unchanged until a user configures the field after an upstream image-count error. Clearing the field restores the inherited base value, or no count limit when the base has none. Counts include repeated image occurrences and images nested in tool results. The existing byte, pixel, and per-image budgets continue to apply independently.
@@ -340,3 +402,15 @@ The derived store preserves its snapshot for transient-only updates, recomputes
340
402
  Lifecycle tests use the real `@deepseek-ai/dsh-session` 0.1.6-alpha.1 Session/SessionStore and a local HTTP gateway. Continuation, a 503 followed by a caller retry, serialized Host header/log restoration, and adapter recreation retain the same `x-opencode-session`. Real forks and subagent-marked child sessions have distinct identities during concurrent calls and keep their own IDs after restoration. These are persistence-boundary tests, not a full installed agent-loop process restart. Existing protocol and published-host compatibility fixtures remain part of the full suite.
341
403
 
342
404
  Validation: `npm test` rebuilt both artifacts and passed **320 tests in 24 files**, including both TypeScript checks. An isolated browser fixture loaded the actual built `lib/client.js` usage slot with local Host-service data. Chinese and English panels displayed the expected 70% ratio and 7,000 / 10,000 input-token counts; a 320px viewport had no horizontal overflow, and account errors did not hide session statistics. Escape closed the dialog; captured browser errors were empty. This validates the shipped client artifact with fixture services, not a live installed Host or upstream cache-hit rate. No paid inference or installed-profile update was required.
405
+
406
+ ## Model area split, whole-row hover, and the model card's details (2026-09-28)
407
+
408
+ The selection list and the parameter card now split 45 : 55 instead of 3 : 1, and the card is taller (`clamp(260px, calc(100cqh - 380px), 560px)`). The former 3 : 1 rule never rendered as 3 : 1 at the reporting window: the right column's 180px floor bound first, so a 549 CSS px model area gave the card 180px while its content needed 464px inside a 286px viewport — the input-modality row started at 258px and its chips ended at 328px, entirely below the fold. The card now shows the model id, the release date on the id's line, both capacities and the modality chips without an inner scroll.
409
+
410
+ Hover and selection paint the same whole row. Previously `.modelChoice:hover` painted only the name button, so a hovered bar stopped where the switch began (measured on the reporter's screenshot: hover 25→392 device px against a full-row selection of 25→455); only the selected state was a whole-row bar. Hover is now `.modelRow:hover:not(.modelRowSelected)`, which covers the name, badges, override mark and switch, keeps the selection's 6px radius and hidden top separator, and mixes the surface one step lighter (`color-mix(in srgb, var(--dsw-alias-bg-layer-3) 92%, var(--dsw-alias-label-primary))`) so the two states stay distinguishable; a selected row keeps its own surface while hovered. The release date also moved onto the model-id line and the capacity hint was shortened, so the card spends its height on the details it exists for.
411
+
412
+ Geometry was measured in headless Microsoft Edge 154 (`--headless --remote-debugging-port`) rendering the shipped stylesheet extracted from the built `lib/client.js` at the reporting window's geometry (553 × 726 CSS px, the 125% display scaling their device pixels divide by). After the change: grid `241.6px 295.4px` (45 : 55), card 539 × 348, pane content 346px inside a 346px viewport, modality chips and the trailing note fully visible, and the deprecation hint's extra two lines still scroll 32px. With the pointer over a row's switch the hovered bar spans x 23→251, identical to the selected row's 23→251, at `rgb(69, 70, 72)` against the selection's `rgb(53, 54, 56)`; hovering the selected row keeps `rgb(53, 54, 56)`. These checks used a local headless browser and the repository's own fixtures; no paid inference was performed.
413
+
414
+ The stylesheet contract moved with the client fixture: `tests/fixtures/client-compatibility.mjs` now holds the distributed CSS to the whole-row hover rule and the 45 : 55 split, replacing the assertions that lived in the removed `tests/artifact.client.spec.ts`. The component test pins the model id and release date to one line with the date's provenance in its title.
415
+
416
+ One operational note came out of the installed-profile follow-up: a desktop Harness loads plugin code into its host process at startup, so installing a new package, reloading the browser, or reinstalling the plugin never reloads it. A build predating `inputModalities` therefore kept serving release dates and deprecation flags while every model still read "not declared", until Harness itself was restarted. Model membership, capacities, lifecycle data and modalities all ride that one process.