dsh-opencode-go 0.1.15 → 0.1.17

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
@@ -19,7 +19,7 @@ The plugin automatically adds the session headers required by OpenCode Go, reads
19
19
 
20
20
  Supports DSH `0.1.5-rc.1` and later, including alpha, rc, and stable releases. Compatibility will be maintained as new host versions are released.
21
21
 
22
- Verified versions: `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`, `0.1.6-alpha.2`, `0.1.7-alpha.1`, `0.1.7-alpha.2`, `0.1.7-rc.1`, and `0.1.7-rc.2`.
22
+ Verified versions: `0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`, `0.1.6-alpha.2`, `0.1.7-alpha.1`, `0.1.7-alpha.2`, `0.1.7-rc.1`, `0.1.7-rc.2`, and `0.2.0-rc.2`.
23
23
 
24
24
  ### Install from DSH (recommended)
25
25
 
@@ -31,12 +31,20 @@ 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)
37
45
 
38
46
  ```sh
39
- dsh plugin --profile web add dsh-opencode-go@0.1.15
47
+ dsh plugin --profile web add dsh-opencode-go
40
48
  ```
41
49
 
42
50
  Start or restart `dsh web`, then:
@@ -50,7 +58,7 @@ Start or restart `dsh web`, then:
50
58
  Install the plugin into the Headless profile:
51
59
 
52
60
  ```sh
53
- dsh plugin --profile headless add dsh-opencode-go@0.1.15
61
+ dsh plugin --profile headless add dsh-opencode-go
54
62
  ```
55
63
 
56
64
  Save the following as `headless.patch.yml` to select a default model:
@@ -58,7 +66,7 @@ Save the following as `headless.patch.yml` to select a default model:
58
66
  ```yaml
59
67
  - id: agent-default-model
60
68
  config:
61
- provider: opencode-go
69
+ provider: dsh-opencode-go
62
70
  model: deepseek-v4.1-flash
63
71
  ```
64
72
 
@@ -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
- dsh plugin --profile web add ./dsh-opencode-go-0.1.15.tgz
89
+ dsh plugin --profile web add ./dsh-opencode-go-0.1.17.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
 
@@ -130,9 +152,11 @@ DSH 0.1.5 offloads only for the current request. DSH 0.1.6 and later record offl
130
152
 
131
153
  ## FAQ
132
154
 
133
- ### The `opencode-go` route is already in use
155
+ ### Coexistence with the host's OpenCode Go and upgrade migration
134
156
 
135
- Only one adapter in a profile can provide the `opencode-go` route. If another plugin or a generic pi-ai configuration already connects OpenCode Go, disable that configuration first. Other providers can continue to run.
157
+ Version 0.1.17 and later use the independent `dsh-opencode-go` provider, shown as **DSH OpenCode Go** in the model picker. The host pi-ai's `opencode-go` provider can run alongside it. Configure the API key in this plugin's settings, then choose a model under **DSH OpenCode Go**. Versions 0.1.16 and earlier use `opencode-go`; their headless configuration should also use that older identity.
158
+
159
+ Sessions, Agent presets and headless defaults previously saved with `provider: opencode-go` need to select **DSH OpenCode Go** again, or change the provider to `dsh-opencode-go`. API keys, model settings and metadata caches are retained. Existing session content remains available; when switching providers, DSH removes the previous adapter's private replay metadata according to its ownership rules.
136
160
 
137
161
  ### An expected model is missing
138
162
 
@@ -150,11 +174,13 @@ A reasoning-capable model without adjustable reasoning levels (for example, `uni
150
174
 
151
175
  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
176
 
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.
177
+ 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.
178
+
179
+ 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
180
 
155
181
  `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
182
 
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.
183
+ 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 and negotiates gzip to reduce transfer size on slow connections. If an HTTP/2 host compatibility issue delivers raw gzip bytes, the plugin asynchronously decompresses them within the same deadline, limiting both the delivered bytes and decoded result to 16 MiB. Model listing and usage requests continue to use uncompressed transfer.
158
184
 
159
185
  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
186
 
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  支持 DSH `0.1.5-rc.1` 及以上版本,包括 alpha、rc 和正式版,并持续跟随宿主版本更新维护兼容性。
21
21
 
22
- 已验证版本:`0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.6-alpha.1`、`0.1.6-alpha.2`、`0.1.7-alpha.1`、`0.1.7-alpha.2`、`0.1.7-rc.1` 和 `0.1.7-rc.2`。
22
+ 已验证版本:`0.1.5-rc.1`、`0.1.5-rc.2`、`0.1.6-alpha.1`、`0.1.6-alpha.2`、`0.1.7-alpha.1`、`0.1.7-alpha.2`、`0.1.7-rc.1`、`0.1.7-rc.2` 和 `0.2.0-rc.2`。
23
23
 
24
24
  ### 在 DSH 中安装(推荐)
25
25
 
@@ -31,12 +31,20 @@
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
  ### 命令行安装(备选)
37
45
 
38
46
  ```sh
39
- dsh plugin --profile web add dsh-opencode-go@0.1.15
47
+ dsh plugin --profile web add dsh-opencode-go
40
48
  ```
41
49
 
42
50
  安装后启动或重启 `dsh web`,然后:
@@ -50,7 +58,7 @@ dsh plugin --profile web add dsh-opencode-go@0.1.15
50
58
  安装到 Headless profile:
51
59
 
52
60
  ```sh
53
- dsh plugin --profile headless add dsh-opencode-go@0.1.15
61
+ dsh plugin --profile headless add dsh-opencode-go
54
62
  ```
55
63
 
56
64
  将以下内容保存为 `headless.patch.yml`,选择默认模型:
@@ -58,7 +66,7 @@ dsh plugin --profile headless add dsh-opencode-go@0.1.15
58
66
  ```yaml
59
67
  - id: agent-default-model
60
68
  config:
61
- provider: opencode-go
69
+ provider: dsh-opencode-go
62
70
  model: deepseek-v4.1-flash
63
71
  ```
64
72
 
@@ -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
- dsh plugin --profile web add ./dsh-opencode-go-0.1.15.tgz
89
+ dsh plugin --profile web add ./dsh-opencode-go-0.1.17.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
 
@@ -130,9 +152,11 @@ DSH 0.1.5 的卸载只影响本次请求;DSH 0.1.6 及以上版本通过宿主
130
152
 
131
153
  ## 常见问题
132
154
 
133
- ### 提示 `opencode-go` 路由已被占用
155
+ ### 与宿主的 OpenCode Go 共存及升级迁移
134
156
 
135
- 同一 profile 中只能有一个适配器提供 `opencode-go` 路由。如果已经通过其他插件或通用 pi-ai 配置接入 OpenCode Go,请先停用那一项配置。其他提供方可以继续使用。
157
+ 从 0.1.17 起使用独立的 `dsh-opencode-go` provider,模型选择器中显示为 **DSH OpenCode Go**;宿主 pi-ai 的 `opencode-go` 可以同时使用。请在插件设置页配置 API Key,然后选择 **DSH OpenCode Go** 下的模型。0.1.16 及以前版本使用 `opencode-go`,这些版本的 headless 配置也应使用旧标识。
158
+
159
+ 升级前保存为 `provider: opencode-go` 的会话、Agent 预设和 headless 默认模型,需要重新选择 **DSH OpenCode Go**,或将 provider 改为 `dsh-opencode-go`。API Key、模型设置和元数据缓存继续沿用。旧会话内容保留;跨 provider 切换时,DSH 会按其规则去除旧适配器的专用回放元数据。
136
160
 
137
161
  ### 没有出现预期的模型
138
162
 
@@ -150,11 +174,13 @@ DSH 0.1.5 的卸载只影响本次请求;DSH 0.1.6 及以上版本通过宿主
150
174
 
151
175
  提供可调节档位的模型,如果其传输协议在未选择档位时会显式关闭思考(`deepseek`、`zai`、`qwen`、`qwen-chat-template`),还会声明一个默认档位(模型支持 `high` 时用 `high`,否则用它提供的最高档位)。用户未选择档位时 DSH 使用该默认值,因此留空也会带上推理档位,而不是关闭思考。由服务商自行决定的协议不声明默认值,行为不变;显式选择的档位始终优先。
152
176
 
153
- 在线配置暂时不可达时,优先复用本次运行中成功获取的配置,以 pi-ai 内置配置作为备用。网关目录刷新失败时,已有请求和设置页均保留上次成功获取的列表。任一来源刷新失败,设置页都会显示警告、失败原因,以及模型名单和模型配置各自上次成功检查的时间。首次读取就失败且没有缓存时,不会凭空生成模型列表。
177
+ 在线配置暂时不可达时,优先复用之前成功获取的配置(包括重启前的磁盘缓存),以 pi-ai 内置配置作为备用。网关目录刷新失败时,已有请求和设置页均保留本次运行中上次成功获取的列表。任一来源刷新失败,设置页都会显示警告、失败原因,以及模型名单和模型配置各自上次成功检查的时间。首次读取网关失败时,不会仅凭元数据缓存生成模型列表。
178
+
179
+ 模型配置自动保存到 `$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
180
 
155
181
  `refreshMinutes` 控制成功刷新后的目录缓存时长。刷新失败后,下一次读取会按 5、10、20、40、60 秒的退避间隔尝试恢复,最长等待间隔为 60 秒;没有读取时不会后台轮询。设置页主动刷新和未知模型的即时发现不受这个间隔限制。取消模型解析或生成时,会立即结束当前调用的目录等待,其他调用仍可继续使用同一次共享刷新。
156
182
 
157
- 已验证的模型配置在缓存到期后的 5 分钟内可直接用于生成,同时触发一次共享后台刷新。这个窗口按两种来源各自上次成功检查的时间计算,失败重试不会延长它;超过窗口后,请求等待下一次到期刷新,刷新仍失败时保留已有回退行为。首次加载、未知模型和手动刷新会等待结果。模型名单请求限时 10 秒,体积更大的模型配置下载单独限时 30 秒。
183
+ 已验证的模型配置在缓存到期后的 5 分钟内可直接用于生成,同时触发一次共享后台刷新。这个窗口按两种来源各自上次成功检查的时间计算,失败重试不会延长它;超过窗口后,请求等待下一次到期刷新,刷新仍失败时保留已有回退行为。没有磁盘缓存的首次加载、未知模型和手动刷新会等待在线结果。模型名单请求限时 10 秒,体积更大的模型配置下载单独限时 30 秒,并协商 gzip 压缩以减少慢速连接的传输量。若宿主因 HTTP/2 兼容问题返回未解压的 gzip 数据,插件会在同一超时预算内异步解压,原始数据和解压结果均受 16 MiB 限制。模型名单和用量接口继续使用非压缩传输。
158
184
 
159
185
  同一会话继续、重试以及恢复后的请求沿用宿主保存的会话 ID。分叉和子会话使用各自的 ID,不复用父会话标识;没有会话 ID 的独立请求每次生成随机标识。
160
186
 
@@ -1,5 +1,5 @@
1
1
  # Third-party notices
2
2
 
3
- The adapter, settings UI, and `src/conversion/{context,stream,replay}.ts` derive from DeepSeek Harness, copyright (c) 2026 DeepSeek, under the MIT license included in LICENSE. The conversion modules are maintained in this package because the published DSH pi-ai package does not export its conversion helpers. Wire protocol implementations remain supplied by `@earendil-works/pi-ai`.
3
+ The adapter, settings UI, and `src/conversion/{context,stream,replay}.ts` derive from DeepSeek Harness, copyright (c) 2026 DeepSeek, under the MIT license included in LICENSE. The conversion modules are maintained in this package because the published DSH pi-ai package does not export its conversion helpers. Wire protocol implementations remain supplied by `@earendil-works/pi-ai`, installed under the plugin's `opencode-go-pi-ai` npm alias.
4
4
 
5
5
  The GitHub icon in the settings footer comes from Simple Icons, distributed under CC0 1.0 Universal.
@@ -0,0 +1,74 @@
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 v020-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 nine host generations, including DSH `0.2.0-rc.2`. 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. The 0.2 fixture also installs its real `dsh-llm-pi-ai` adapter and public pi-ai dependency alongside this plugin.
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. Each host also runs all three wire protocols through prompt and tool declaration checks, a streamed tool call, JSON-restored assistant replay, tool-result continuation, and a one-shot request with no tools.
45
+
46
+ ## Isolated pi-ai dependency
47
+
48
+ The runtime dependency `opencode-go-pi-ai` is an npm alias for `@earendil-works/pi-ai@0.87.1`. All plugin imports, including declaration types and lazy API factories, use the alias. It keeps this plugin's SDK requirement separate from the host's public `@earendil-works/pi-ai` name, so an older plugin that declares pi-ai only as a peer is not redirected by our dependency. The npm and pnpm Git-installation checks include a public pi-ai 0.85.1 and a peer-only probe, and verify that the probe retains 0.85.1 while this plugin resolves its own 0.87.1.
49
+
50
+ DSH requests still convert through `toPiContext`, preserving the existing host input and durable replay contracts. Immediately before calling a provider directly, the adapter applies pi-ai's `normalizeContext`. In 0.87.1, direct providers require `TranscriptContext`; only `Models` streaming entry points normalize legacy Context automatically. Normalizing at this call site preserves the system prompt and tool declarations on both text and image paths without tying the SDK format to the DSH version.
51
+
52
+ This upgrade does not enable developer messages, tool-change blocks, or deferred tool loading. Their existing `UNSUPPORTED_CONTENT` errors remain explicit. The minimum DSH version and peer ranges stay unchanged; new host fixtures verify runtime compatibility rather than expanding the supported content vocabulary.
53
+
54
+ 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.
55
+
56
+ ## Updating a fixture
57
+
58
+ 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.
59
+
60
+ Generate a lockfile in a clean temporary directory containing that fixture's manifest:
61
+
62
+ ```sh
63
+ npm install --package-lock-only --ignore-scripts --strict-peer-deps
64
+ ```
65
+
66
+ 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.
67
+
68
+ After changing the root lockfile, verify a clean `npm ci --strict-peer-deps` and `npm run test:ci` with each supported Node version's bundled npm. Node 22.19.0 ships npm 10.9.3; running Node 22 with a newer npm does not check that install contract. In this dependency tree, npm 10 requires the optional esbuild peer used by Vite under Vitest to be recorded, even when npm 11 can install without that record. Keep its platform packages in the lockfile as well.
69
+
70
+ 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.
71
+
72
+ 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.
73
+
74
+ 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.
@@ -4,10 +4,12 @@ The plugin owns its Host adapter, Web settings entry, build, package manifest, a
4
4
 
5
5
  The Host depends on DSH services through peer dependencies so Cordis service classes and LLM error identities remain shared with the application. The Client resolves React, the store, and UI primitives through the DSH module table. UI type packages are optional peers because headless installations do not mount those services; this project's development dependencies supply the types and test runtime libraries.
6
6
 
7
- The package's bundle inserts `id: opencode-go`. The original fork's `llm-opencode-go` row can be disabled by a profile patch. Plugin registration does not select the user's default model or replace another provider automatically.
7
+ The package's bundle keeps the configuration entry `id: opencode-go` so existing settings remain attached. Its LLM provider route is independently named `dsh-opencode-go`, shown as `DSH OpenCode Go`, and can coexist with the generic adapter's `opencode-go` route. The SDK retains its native `opencode-go` identity for transport behavior; replay metadata records the DSH route and an optional `sdkProvider` to restore the native identity. The original fork's `llm-opencode-go` row can be disabled by a profile patch. Plugin registration does not select the user's default model or replace another provider automatically. Saved session/preset provider selections must be updated explicitly.
8
8
 
9
9
  Published `@deepseek-ai/dsh-llm-pi-ai` versions `0.1.5-rc.2` and `0.1.6-alpha.1` do not export `./conversion`. This package therefore carries the request-history, stream, and replay conversion modules under `src/conversion`, with the upstream MIT license. It does not import unpublished files or bundle the whole adapter. Protocol transport remains implemented by pi-ai. Conversion maintenance is now this project's responsibility; a future public conversion library can replace these modules once published and tested.
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,111 @@
1
1
  # Verification
2
2
 
3
+ ## Release 0.1.17 validation (2026-09-30)
4
+
5
+ After integrating the npm 10 lockfile correction, the 0.1.17 manifest passed Host/Client type checks, all **367 tests in 26 files**, and freshness checks for **32 shipped artifacts** on macOS / Node 24.14.1. Both npm and pnpm 11.7.0 Git installs passed without plugin build approval; the installed package uses private pi-ai 0.87.1 while the independent peer probe keeps public pi-ai 0.85.1. The nine-generation runtime compatibility results below remain applicable: the subsequent integration changes only lockfile metadata, documentation and the package version.
6
+
7
+ ## Independent DSH provider route (2026-09-30)
8
+
9
+ The public LLM route is now `dsh-opencode-go`, shown as **DSH OpenCode Go**, and does not register an `opencode-go` alias. This permits the generic host adapter and the plugin to register in either order. The SDK's native provider remains `opencode-go`, preserving its OpenCode-specific reasoning serialization and models.dev lookup. Host and Client share the route constant; settings discovery and the usage badge follow the new route. The profile entry `opencode-go`, settings namespace, credentials and metadata cache retain their existing identities.
10
+
11
+ Durable replay still uses envelope version 2. Its `provider` matches the DSH assistant source; the optional `sdkProvider` restores the SDK's native identity. JSON-restored requests retain reasoning signatures and tool replay. A saved session or Agent/headless preset selecting the old route must explicitly select `dsh-opencode-go`; DSH's existing cross-adapter ownership rules strip old private replay metadata while keeping durable session content. The plugin does not rewrite user profiles or historical route selections.
12
+
13
+ Before the change, `npx vitest run tests/provider-identity.spec.ts` failed all three regressions: plugin-first registration prevented the host route from mounting, host-first registration left the plugin without its own route, and the independent route had no replay state. These now pass, including real loopback streams for both routes, JSON-restored reasoning continuation, and old-session content after switching. The usage badge also verifies that selecting the host's `opencode-go` starts no plugin polling.
14
+
15
+ Host/Client type checks, production compilation, all **367 tests in 26 files**, and freshness checks for all **32 shipped build files** passed on macOS / Node 24.14.1. `DSH_COMPAT_CONCURRENCY=3 npm run test:compat` passed all **nine isolated installed-host generations**, from 0.1.5-rc.1 through 0.2.0-rc.2, including three-protocol replay, reasoning, images, Client rendering, live settings and applicable Desktop/Web/Headless bundle gates. The run took 884 seconds, dominated by npm installation in the first three consumers. No live inference, installed user profile, GitHub issue or npm publication was changed. This addresses route collisions; the stale protocol dependency reported in Issue #26 remains a separate investigation.
16
+
17
+ ## Issue #22: gzip metadata download on slow connections (2026-09-30)
18
+
19
+ Only the models.dev request now negotiates `accept-encoding: gzip`; listing and usage requests retain `identity`. Requests still use the host's global Fetch and dispatcher. Normally Fetch decodes gzip. If the Issue #7 HTTP/2 mismatch instead delivers raw gzip bytes, the metadata reader recognizes the gzip signature and asynchronously decompresses that one format. Both delivered input and decoded output remain bounded to 16 MiB. Download, connection retry, and recovery decompression share the original 30-second abort signal; corrupt gzip is not retried. No cache format, persistence policy, or proxy configuration changed.
20
+
21
+ Before the change, `vitest run tests/metadata-transport.spec.ts` failed both regressions: a cold catalog timed out transferring approximately 5.23 MB over a local bandwidth-limited HTTP connection, and raw gzip metadata with missing encoding headers failed JSON parsing. Both pass after the change. The bandwidth fixture gives both representations the same 16 KiB per 10 ms transfer budget and scales the deadline to 500 ms. Further regressions cover Fetch-decoded gzip, raw gzip with present or missing headers, input/output limits, corrupt data, cancellation, listing negotiation, and persisted ETag verification.
22
+
23
+ A separate real TLS/HTTP/2 fixture used Node 24.14.1's built-in Fetch with an Undici 8.11.0 global ProxyAgent through a local CONNECT tunnel. The control lost encoding headers and returned raw gzip that native JSON parsing rejected. The shared reader and an actual cold catalog both succeeded through the same proxy; all four reads used HTTP/2. The fixture trusted its own certificate and did not disable TLS verification. This recovers gzip bodies without changing the host's dispatcher; it does not repair the host's underlying header loss.
24
+
25
+ Host/Client type checks, production compilation, all **363 tests in 25 files**, and freshness checks for all **31 shipped build files** passed on macOS / Node 24.14.1. A public read-only probe through the updated reader received HTTP 200 with gzip and parsed 5,264,592 bytes of JSON, including 33 OpenCode Go models, in **6.83 seconds**. This measures decoded data, not wire transfer size; the response supplied no Content-Length. Existing upstream missing-source-map warnings remain. The local fixture and public probe do not guarantee connectivity on the reporter's network.
26
+
27
+ The updated package also passed `npm run test:compat -- v020-rc2` against isolated, real DSH **0.2.0-rc.2** dependencies: Host activation/RPC/streaming/images, all three transcript protocols, reasoning, Client settings/rendering/CSS, live profile settings, and bundle admission. The bundle fixture now explicitly checks **Desktop, Web and Headless** profile directories. The installed macOS Desktop app's manifest reports 0.2.0-rc.2, and a separate gzip-reader probe passed using its actual bundled **Node 24.21.0** executable. These are plugin compatibility and runtime checks, not an end-to-end Desktop GUI session; no installed user profile was updated. The npm latest version remains 0.1.16, which does not yet contain the repository's 0.2 adaptation or this gzip change.
28
+
29
+ ## Node 22 bundled-npm clean installs (2026-09-30)
30
+
31
+ The [Issue #24 main-branch workflow](https://github.com/Duskriver/dsh-opencode-go/actions/runs/36696633511) passed 11 of 13 jobs. Both Linux and Windows core jobs on Node 22.19.0 failed during `npm ci --strict-peer-deps`, before running tests, with `Missing: esbuild@0.28.2 from lock file`. Their bundled npm is 10.9.3. The previous local Node 22 checks used npm 11, so they did not cover that clean-install contract.
32
+
33
+ A fresh fixture containing only the manifest and lockfile reproduced the same error with Node 22.19.0 / npm 10.9.3 in offline dry-run mode, while npm 11 accepted the same files. Vite under Vitest declares an optional esbuild peer whose range excludes the root build tool's esbuild 0.25.12. npm 10 resolves a nested 0.28.2 and checks its lock entries; npm 11 can omit them. The fix records that optional peer and all 26 platform packages. All existing lock entries, package versions, and platform metadata are preserved.
34
+
35
+ The same isolated-fixture command rejects the original lockfile and accepts the corrected one. Clean strict installs and `npm run test:ci` passed on macOS with Node 22.19.0 / npm 10.9.3 and Node 24.14.1 / npm 11.11.0: **354 tests in 24 files** and all **31** shipped artifacts matching source in each environment. The existing Linux/Windows core jobs retain their clean-install check as the regression guard. Development guidance now requires each supported Node version's bundled npm when verifying a changed lockfile.
36
+
37
+ ## Issue #24: DSH 0.2.0 and pi-ai transcript compatibility (2026-09-30)
38
+
39
+ The plugin now uses pi-ai **0.87.1** through the `opencode-go-pi-ai` npm alias. All runtime imports, lazy protocol factories, and declaration imports use that alias, leaving the host's public pi-ai dependency to resolve independently. Before a direct provider call, the adapter applies the official `normalizeContext` to the existing text or image conversion result. This preserves system prompts and tool declarations under the new transcript contract while retaining DSH `0.1.5-rc.1` as the minimum supported host. Tool argument replay uses the SDK's JSON object type; the persisted replay envelope remains version 2.
40
+
41
+ The installed-package matrix now includes **DSH 0.2.0-rc.2**, with all 50 DSH dependency/peer package entries pinned to that generation and its real `dsh-llm-pi-ai` adapter and public pi-ai 0.87.1 installed. Every host exercises all three protocols through leading system prompts, tool declarations and schemas, streamed tool calls, JSON-restored assistant replay, tool-result continuation, and one-shot prompts with no tool leakage. Existing real image preparation, offloading, reasoning, settings, client rendering, and Web/Headless admission checks remain enabled. A separate payload-only control confirmed that each direct API loses the prompt and tools without normalization and preserves both with it; the capture hook stopped execution before any network request.
42
+
43
+ Both npm and pnpm Git-installation fixtures now contain public pi-ai **0.85.1** and a plugin that declares only a broad pi-ai peer. Normal module resolution confirmed that the peer still receives 0.85.1 while this plugin receives its aliased 0.87.1. These checks use prebuilt Git artifacts without source files or plugin build approval. The updated SDK's built-in catalog now includes DeepSeek V4.1 and no longer offers its off effort; tests that need an unknown model use a deliberately fictional ID, and the off-effort transport test uses DeepSeek V4, which still declares that capability.
44
+
45
+ Validation passed on macOS: Host/Client type checks, all **31** shipped build artifacts matching source, **354 tests in 24 files** on Node **24.14.1** and minimum-version **22.19.0**, all **nine installed-host environments** on Node 24, npm and pnpm **11.7.0** Git installs on Node 24, and an additional npm Git install on Node 22.19.0. Existing upstream missing-source-map and peer warnings remain non-failing. Tests use isolated consumers and local fixture endpoints; no user profile, live inference, npm publication, or GitHub issue was changed. Developer messages, tool-change blocks, and deferred loading retain their explicit unsupported-content errors.
46
+
47
+ ## Release 0.1.16 integration (2026-09-29)
48
+
49
+ 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.
50
+
51
+ ## Issue #22: persistent model metadata (2026-09-29)
52
+
53
+ 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.
54
+
55
+ 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.
56
+
57
+ 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.
58
+
59
+ 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.
60
+
61
+ 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.
62
+
63
+ ## Settings page cards with the tuning fields last (2026-09-29)
64
+
65
+ 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.
66
+
67
+ 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`.
68
+
69
+ `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.
70
+
71
+ ## Git installation without plugin build approval (2026-09-28)
72
+
73
+ 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.
74
+
75
+ 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.
76
+
77
+ 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.
78
+
79
+ 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.
80
+
81
+ ## Bounded parallel compatibility checks (2026-09-28)
82
+
83
+ 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.
84
+
85
+ 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.
86
+
87
+ 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.
88
+
89
+ ## Parallel Git-installation CI and cache placement (2026-09-28)
90
+
91
+ 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.
92
+
93
+ 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.
94
+
95
+ 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.
96
+
97
+ 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.
98
+
99
+ ## Issue #19: isolate compatibility fixtures from plugin builds (2026-09-28)
100
+
101
+ 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.
102
+
103
+ `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.
104
+
105
+ 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.
106
+
107
+ 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.
108
+
3
109
  ## Optional request image count cap and usage panel polish (plugin 0.1.15, 2026-09-26)
4
110
 
5
111
  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 +446,15 @@ The derived store preserves its snapshot for transient-only updates, recomputes
340
446
  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
447
 
342
448
  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.
449
+
450
+ ## Model area split, whole-row hover, and the model card's details (2026-09-28)
451
+
452
+ 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.
453
+
454
+ 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.
455
+
456
+ 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.
457
+
458
+ 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.
459
+
460
+ 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.
@@ -1,4 +1,4 @@
1
1
  - id: agent-default-model
2
2
  config:
3
- provider: opencode-go
3
+ provider: dsh-opencode-go
4
4
  model: deepseek-v4.1-flash