dsh-opencode-go 0.1.24 → 0.2.1

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.
Files changed (102) hide show
  1. package/README.en.md +66 -12
  2. package/README.md +172 -79
  3. package/THIRD_PARTY_NOTICES.md +6 -2
  4. package/assets/icon.png +0 -0
  5. package/docs/advanced.en.md +32 -35
  6. package/docs/advanced.md +33 -35
  7. package/docs/assets/logo-dark.png +0 -0
  8. package/docs/assets/logo-light.png +0 -0
  9. package/docs/assets/usage-history-desktop.jpg +0 -0
  10. package/docs/assets/usage-history-mobile.jpg +0 -0
  11. package/docs/assets/usage-history-models.jpg +0 -0
  12. package/docs/development.md +61 -119
  13. package/docs/independent-package.md +4 -2
  14. package/docs/usage-analytics.en.md +57 -0
  15. package/docs/usage-analytics.md +57 -0
  16. package/docs/verification.md +51 -917
  17. package/lib/build-info.json +1 -1
  18. package/lib/client.js +3429 -2618
  19. package/lib/free-http-worker.js +32 -0
  20. package/lib/index.js +5135 -3746
  21. package/lib/types/accounts.d.ts +96 -31
  22. package/lib/types/adapter.d.ts +63 -51
  23. package/lib/types/analytics.d.ts +35 -0
  24. package/lib/types/catalog.d.ts +109 -44
  25. package/lib/types/client/AnalyticsDialog.d.ts +18 -0
  26. package/lib/types/client/Section.d.ts +41 -7
  27. package/lib/types/client/UsagePill.d.ts +15 -6
  28. package/lib/types/client/controller.d.ts +178 -0
  29. package/lib/types/client/dashboard-copy.d.ts +58 -0
  30. package/lib/types/client/index.d.ts +18 -12
  31. package/lib/types/client/{locales.d.ts → presentation.d.ts} +97 -9
  32. package/lib/types/config.d.ts +14 -15
  33. package/lib/types/contracts.d.ts +338 -0
  34. package/lib/types/conversion.d.ts +155 -0
  35. package/lib/types/free-channel.d.ts +21 -0
  36. package/lib/types/free-http-worker.d.ts +1 -0
  37. package/lib/types/free-policy.d.ts +4 -0
  38. package/lib/types/free-protocol.d.ts +16 -0
  39. package/lib/types/free-runtime.d.ts +2 -0
  40. package/lib/types/free-socks-tunnel.d.ts +4 -0
  41. package/lib/types/free-transport.d.ts +19 -0
  42. package/lib/types/index.d.ts +8 -40
  43. package/lib/types/pricing.d.ts +29 -0
  44. package/lib/types/sdk-types.d.ts +1540 -2902
  45. package/lib/types/transport.d.ts +71 -0
  46. package/lib/vendor-licenses.txt +55 -31
  47. package/package.json +77 -78
  48. package/docs/assets/usage-panel.png +0 -0
  49. package/docs/fork-review-2026-09-25.md +0 -191
  50. package/docs/native-call-comparison-v020-rc2.md +0 -116
  51. package/lib/types/account-manager.d.ts +0 -25
  52. package/lib/types/account-operations.d.ts +0 -31
  53. package/lib/types/account-policy.d.ts +0 -16
  54. package/lib/types/account-recovery.d.ts +0 -2
  55. package/lib/types/account-selection.d.ts +0 -30
  56. package/lib/types/account-service.d.ts +0 -12
  57. package/lib/types/accounts-contract.d.ts +0 -38
  58. package/lib/types/call-trace.d.ts +0 -54
  59. package/lib/types/catalog-manager.d.ts +0 -20
  60. package/lib/types/client/AccountsCard.d.ts +0 -19
  61. package/lib/types/client/ModelEditor.d.ts +0 -20
  62. package/lib/types/client/accounts-controller.d.ts +0 -71
  63. package/lib/types/client/hint-mark.d.ts +0 -17
  64. package/lib/types/client/host-compat.d.ts +0 -7
  65. package/lib/types/client/host-primitives.d.ts +0 -11
  66. package/lib/types/client/section-controller.d.ts +0 -202
  67. package/lib/types/client/settings.d.ts +0 -18
  68. package/lib/types/client/staged-form.d.ts +0 -201
  69. package/lib/types/client/usage.d.ts +0 -4
  70. package/lib/types/config-contract.d.ts +0 -70
  71. package/lib/types/conversion/config.d.ts +0 -4
  72. package/lib/types/conversion/context.d.ts +0 -56
  73. package/lib/types/conversion/image-offload.d.ts +0 -13
  74. package/lib/types/conversion/image-pool.d.ts +0 -24
  75. package/lib/types/conversion/index.d.ts +0 -5
  76. package/lib/types/conversion/replay.d.ts +0 -66
  77. package/lib/types/conversion/stream.d.ts +0 -44
  78. package/lib/types/gateway-diagnostics.d.ts +0 -23
  79. package/lib/types/gateway-error.d.ts +0 -2
  80. package/lib/types/gateway-url.d.ts +0 -6
  81. package/lib/types/google-fetch.d.ts +0 -6
  82. package/lib/types/host-settings.d.ts +0 -11
  83. package/lib/types/json-response.d.ts +0 -14
  84. package/lib/types/metadata-cache.d.ts +0 -12
  85. package/lib/types/model-metadata.d.ts +0 -21
  86. package/lib/types/models-contract.d.ts +0 -66
  87. package/lib/types/models.d.ts +0 -13
  88. package/lib/types/protocol-contract.d.ts +0 -5
  89. package/lib/types/protocol-policy.d.ts +0 -4
  90. package/lib/types/provider-identity.d.ts +0 -4
  91. package/lib/types/proxy-url.d.ts +0 -2
  92. package/lib/types/proxy.d.ts +0 -11
  93. package/lib/types/reasoning.d.ts +0 -40
  94. package/lib/types/remote-contract.d.ts +0 -3
  95. package/lib/types/remotes.d.ts +0 -3
  96. package/lib/types/request-budget.d.ts +0 -12
  97. package/lib/types/request-control.d.ts +0 -2
  98. package/lib/types/settings-bridge.d.ts +0 -22
  99. package/lib/types/usage-contract.d.ts +0 -35
  100. package/lib/types/usage-display.d.ts +0 -4
  101. package/lib/types/usage.d.ts +0 -27
  102. /package/lib/types/{google-generative-ai.d.ts → google.d.ts} +0 -0
@@ -1,943 +1,77 @@
1
1
  # Verification
2
2
 
3
- ## Issue #54: 64k output default and failed-request occupancy (2026-10-15)
3
+ The recorded baseline on 2026-11-06 used macOS, Node `24.22.1`, npm `12.2.1`, development peers DSH `0.2.1-rc.5`, Cordis `4.0.5` and Schemastery `3.18.5`. The installed DSH `0.2.2-rc.1` CLI was also exercised with an isolated headless profile and the rebuilt plugin. Those results precede the official-history dashboard migration and do not validate its new implementation.
4
4
 
5
- Only requests to the exact model ID `deepseek-v4.1-flash` default to 65,536 output tokens, bounded by that model's capacity. Both model-resolution paths report the default as `defaultMaxTokens` for host compaction budgeting. Other models keep their SDK output defaults and existing explicit caller/setting behavior. Regression tests compare the Host's prepared configuration with the serialized Chat Completions allowance, cover smaller DeepSeek capacities and settings changes during preparation, and verify that other model IDs (including similarly named DeepSeek variants) receive no injected default. SDK context clamping can reduce the wire value; Anthropic token-budget reasoning retains its existing behavior.
5
+ The minimum supported Host is DSH `>=0.2.1-rc.5`, with Cordis `~4.0.5`. The package retains its `.` and `./client` export contracts. The current architecture and command reference are in [development](development.md).
6
6
 
7
- HTTP 400/413/422 diagnostics include the serialized output allowance, configured window and `/compact`/Max output guidance. Explicit context-overflow codes or text map to `CONTEXT_WINDOW_EXCEEDED`, while opaque 400 and request-body limits remain `INVALID_REQUEST`. Failed attempts without measured tokens omit usage; positive input, output, cache or total counts remain available for accounting.
7
+ ## Recorded baseline before the dashboard migration
8
8
 
9
- A pure projection probe using the published `dsh-token-meter@0.2.0-rc.2` confirmed that an attempt with the old zero usage changes a 670,387-token pressure sample to zero, while the new stream preserves 670,387. This exercised the projection and compact attempt stream, not a real desktop conversation or live gateway.
9
+ | Command | Result |
10
+ | --- | --- |
11
+ | `npm test` | 17 test files, 913 tests, zero failures or skipped tests |
12
+ | `npm run typecheck` | Host and browser TypeScript projects pass |
13
+ | `npm run check:dist` | All 33 shipped build artifacts match an independent rebuild |
14
+ | `npm run verify` | Types, artifact comparison, full Vitest suite and installed-package smoke pass |
10
15
 
11
- Validation: both TypeScript projects pass; `npm run test:ci` passes **755 tests in 46 files** and freshness checks for **66 shipped artifacts**. All **ten installed host generations** pass (**116.3 seconds** including cleanup), with assertions for failure usage omission, capacity diagnostics, prepared defaults and preserving other models' SDK defaults. npm and fresh pnpm Git/tarball installation checks pass (**33.4 seconds**), including public declarations and loading without dependency build approvals. Checks used isolated consumers and local fixtures; no installed user profile, credential, live inference request or publication was changed.
16
+ `verify` includes `typecheck`, `test:ci` and `verify:installed`; `test:ci` includes `check:dist` and `npm test`. CI runs this same acceptance path after `npm ci --strict-peer-deps` on Linux and Windows with Node 22.19 and 24. Those CI jobs are configured; the baseline results above are from macOS.
12
17
 
13
- ## Issue #59: forwarded settings schema (2026-10-15)
18
+ ## Official-history source check
14
19
 
15
- DSH rebuilds volatile settings fields and serializes the assembled form again before sending it to the desktop client. The second serialization drops the rehydrated `baseURL` transform callback, causing validation to fail and leaving the form in `loading`. The field now uses a plain string schema. Host reads, profile writes and revision-checked saved migrations retain the legacy official gateway migration.
20
+ A manual read-only check on 2026-11-06 used the current desktop key without Cookie or organization headers. Four official Console history endpoints returned HTTP 200. Their 30-day totals agreed:
16
21
 
17
- Regression coverage validates the forwarded schema, migrates a saved legacy profile address at startup without remounting, resets to the default gateway and preserves custom addresses. Existing gateway tests cover discovery, all four inference protocols and usage while the legacy gateway is unavailable.
22
+ | Metric | Recorded value |
23
+ | --- | --- |
24
+ | Requests | 7,064 |
25
+ | Total tokens | 1,105,009,939 |
26
+ | `cost_micro_cents` | 3,152,640,453 |
27
+ | Official usage amount (`1 USD = 100,000,000` units) | $31.52640453 |
18
28
 
19
- Validation of the isolated change: `npm run test:ci` passes **714 tests in 45 files**, both TypeScript projects and all **65 shipped build artifacts**. All **ten installed host generations** pass, including DSH 0.2.0-rc.2 (**40.4 seconds** including cleanup).
29
+ The daily CSV at `https://opencode.ai/console/api/v2/usage/export?range=30d` supplied the fields needed for totals, daily charts and provider/model detail. The implementation uses that single snapshot rather than combining multiple endpoint snapshots. The check establishes access and consistency for this account at that time; it does not establish every account's permissions, immediate publication of recent usage or complete anonymous Free coverage. The official usage amount does not establish additional Go subscription charges.
20
30
 
21
- ## Live direct-gateway versus installed-plugin latency (2026-10-15)
31
+ ## This dashboard migration
22
32
 
23
- The user's recorded DeepSeek V4.1 Flash request reached its first reasoning delta at **19,449 ms**, first text at **19,627 ms**, and completion at **20,634 ms**. The Host recorded request context after **9 ms**. A same-model title request started after **10 ms** and completed **20,465 ms** later; that coincidence alone does not establish a dependency.
33
+ | Check | Current result |
34
+ | --- | --- |
35
+ | Official CSV parsing, exact totals, UTC date filling and today filtering | Pass: strict CSV fixtures, large integers, unit conversion, provider/model grouping and exact reconciliation |
36
+ | Credential/account/network/date cache isolation, force refresh, proxy path, cancellation, timeout and response limits | Pass: Host service and RPC tests, including independent cancellation and connection-change races |
37
+ | Key/member-field boundary, third-party gateway rejection and explicit upstream failures | Pass: report excludes member fields and keys; explicit permission, credential, gateway and malformed-response failures |
38
+ | Dashboard account/range/refresh, selected-account quotas and responsive light/dark UI | Pass: browser interaction tests plus real-browser 1280×1000 dark / 390×844 light checks, Chinese and English |
39
+ | `npm test` | Pass: 17 files, 886 tests; zero failures or skipped tests |
40
+ | `npm run typecheck` / `npm run compile` | Pass: Host and browser types and distribution build |
41
+ | `npm run check:dist` | Pass: all 31 shipped artifacts match an independent rebuild |
42
+ | `npm run verify` | Pass: types, build comparison, all tests and installed-package smoke |
24
43
 
25
- With explicit authorization for live measurements, `scripts/benchmark-latency.mjs` used the selected desktop credential, the installed v0.1.23 adapter and Desktop 0.2.0-rc.2's actual Host packages under Electron's Node 24.18.1. Each paired comparison asserted byte-identical serialized bodies and all SDK-visible headers, including the session header. The recorded text messages and 33 tool definitions were reconstructed in memory, with a shared **1,024-token output cap** and the original default reasoning policy. Sequential AB/BA ordering reduced order bias. Neither prompts nor raw credential-bearing headers were saved.
44
+ The user-facing Free status card, its copy/styles, status/probe RPCs and manual inference probe have been removed. Normal requests retain Free error classification and passive Retry-After/backoff, including account isolation, cancellation and coexistence with Go.
26
45
 
27
- Median first-text times from arm entry:
46
+ Backend migration checks use synthetic CSVs, synthetic keys and loopback services, never a real user's key. The manual Console source check above is separate from automated tests.
28
47
 
29
- | Scenario | Pairs | Direct gateway | Installed plugin |
30
- | --- | ---: | ---: | ---: |
31
- | Recorded request replay | 4 | 3.063 s | 2.895 s |
32
- | New inert context prefix per pair | 4 | 4.180 s | 4.167 s |
33
- | Recorded automatic title concurrently | 2 | 4.915 s | 4.509 s |
48
+ The rewritten `GoAnalyticsService` was also checked directly against Console with the current desktop key on 2026-11-06: both the 30-day report and CSV export succeeded. The report contained 30 calendar days and 13 provider/model rows, with no member fields or key; the explicitly requested CSV retained its 14 official columns and contained no key. This was a read-only check, with no inference or profile changes.
34
49
 
35
- All **20 main paired requests** produced text successfully. The fresh-prefix run observed two zero-cache main requests and two cache-hit main requests in each arm. Gateway headers identified both `luminal-dsv4.1flash` and `orcarouter-dsv4.1flash` under the same public model id; pairs stayed on the same reported endpoint. The combined plugin median before HTTP dispatch was **3.233 ms** (range **1.802–5.172 ms**); first wire text reached the plugin consumer after a median **0.203 ms** (range **0.099–0.291 ms**). Credential resolution through the actual local credential provider had median **0.413 ms**. Observed new socket connection setup took **1.214–2.838 s**; upload completion to response headers took **1.394–5.171 s**. These intervals overlap differently across samples and their medians must not be summed.
50
+ Real-browser layout checks used synthetic preview data. The narrow view fills 390×844 without horizontal page overflow; charts and detail tables scroll within their own regions. Account/range changes and model detail were exercised. Previews: [desktop](assets/usage-history-desktop.jpg), [narrow light](assets/usage-history-mobile.jpg), [models](assets/usage-history-models.jpg).
36
51
 
37
- The additional minimal-prompt control had three HTTP-successful requests and one direct-fetch transport failure before headers. One successful minimal request exhausted its output cap entirely on reasoning and produced no text; these controls are excluded from the main paired table. Three of four concurrent title calls similarly exhausted their recorded 64-token cap on reasoning. In total there were **28 inference attempts**, with **27 successful HTTP responses**. No tool calls were executed. All profile, credential, manifest and dependency-build-trust hashes stayed unchanged. The offline incremental SSE/CRLF observer check and distribution verification passed.
52
+ ## Regression scope
38
53
 
39
- The historical 19.449-second outlier was **not reproduced**, including with title concurrency and observed cache misses. The measured plugin overhead is milliseconds; observed seconds were spent establishing connections and waiting for gateway/upstream data. Historical socket/header timestamps are unavailable, so these results cannot distinguish the old request's network wait from gateway queueing or provider prefill. They also do not measure desktop UI/IPC. JSON samples, gateway request IDs, joined statistics and a readable report are retained in the local ignored `opencode-protocol-benchmark-latency-*` directories.
54
+ The test files import current modules directly, without resolver aliases or temporary path substitutions. Support helpers provide current host/account services, ConfigForm/React fixtures, offline model data, loopback gateways and proxies. Tests for removed journals, recovery state machines, diagnostic capture, global image pools and legacy host APIs are removed.
40
55
 
41
- ## Desktop installation: forwarded settings schema (2026-10-15)
56
+ The pre-migration baseline covered:
42
57
 
43
- Installing the local v0.1.23 fixes into DeepSeek Harness Desktop 0.2.0-rc.2 exposed another regression: the Host rebuilds volatile fields as plain schemas and serializes the assembled form a second time. Schemastery drops the rehydrated base-URL transform callback during that second serialization, so the client rejects the namespace and shows the unavailable message. A regression reproduced `TypeError: callback is not a function` before the fix. Configuration schemas now keep `baseURL` as a plain string; Host reads, profile writes and revision-fenced saved migrations retain the legacy-gateway migration. The installed profile fixture also validates the actual Host's forwarded schema envelope.
58
+ - Real Cordis/Loader lifecycle, native SettingsForms and owner-scoped RPC, hot profile changes without adapter remounts, coexistence with the native provider and durable signed continuations.
59
+ - Account CRUD, generated-key ownership, the 20-account limit, queue serialization across reconnects, failed-write cleanup, read-only keys, stale operations and primary promotion. Real HTTP 401/403/429 and quota responses verify fallback before output, cooldown/expiry, cancellation and prevention of replay after output or charged usage.
60
+ - Model catalog discovery, missing/invalid metadata, online refresh and ETag/disk caching, endpoint isolation, lifecycle visibility, typed capacities, request snapshots, reasoning choices and OpenAI/Anthropic/Google wire behavior.
61
+ - Stable session and attribution headers, stream outcomes/deadlines, independent tool-role conversion, replay signatures, image offload projection, exact encoded-byte/count budgets and request-local preparation concurrency.
62
+ - Official quota/error codecs. Official-history CSV behavior is covered by the migration checks above.
63
+ - Bounded JSON/gzip responses, safe gateway classification/redaction, actual HTTP CONNECT and SOCKS5 proxy paths, concurrent Google fetch binding and version-guarded SDK patch matching.
64
+ - Free-specific quota/client/access/rate-limit classification, Retry-After seconds and HTTP dates including clock skew, bounded exponential fallback, endpoint/proxy isolation, concurrent completion ordering and zero inference during cooldown. A normal Host request test limits Free and then completes Go streaming with its saved fixture key in the same mount.
65
+ - Settings Save/Reset/Discard and rejected/concurrent writes; AccountsCard additions, quota states, row actions and drag/keyboard sorting; model editor/search/visibility and refresh races; quota pill polling, account switching and retained readings; installed locale resolution and live Chinese/English changes. Dashboard and palette migration checks are listed separately above.
44
66
 
45
- Validation: **728 tests across 46 files**, both TypeScript projects, and all **65 shipped build artifacts** passed. The updated fixtures passed all **ten installed Host generations** in **131.2 seconds**, including cleanup. npm/pnpm Git and tarball installs and public types passed in **37.2 seconds**. Separate loopback profile and transcript fixtures passed with the installed plugin and the desktop application's actual Host packages under Electron's Node 24.18.1.
67
+ ## Isolation and limits
46
68
 
47
- The package was installed through Desktop's plugin manager and loaded after a normal application restart. The real OpenCode Go settings page rendered the configured account, usage and **37 gateway entries**. An invalid proxy draft was cleared by **Reset All**, then the staged reset was discarded; Save and Discard returned to disabled. Existing profile configuration, credentials and dependency-build trust were unchanged by hash comparison. All installed `lib` files matched the tested archive, and a backup of the prior plugin/profile was retained outside the repository. No completion request was submitted during this installation check.
69
+ Vitest gives each test a temporary `DSH_HOME`, serves complete synthetic `/models` declarations, uses loopback gateways/proxies and rejects other network requests. The installed-package smoke also uses a temporary home, synthetic credentials and metadata, and loopback inference. It resolves the built package through the consumer's ordinary Cordis resolver and verifies dual route discovery, streamed text, session headers, attribution, authorization and unload.
48
70
 
49
- ## v0.1.23 review fixes: invalid-draft reset and Google signed Part replay (2026-10-15)
71
+ The pinned rc.5 UI primitives package references an unpublished `index.js.map`; Vite reports a non-fatal source-map warning. Undici also reports its experimental SOCKS5 notice during the real proxy test. Neither creates a failed test or nonzero command exit. The suite does not filter these messages or patch installed packages.
50
72
 
51
- The advanced card now enables Reset for invalid drafts and resets them alongside saved overrides. An invalid proxy address or image count can return to its inherited value even when every other row inherits. Saving the reset clears existing advanced overrides through the same staged settings batch; the action remains scoped to `ADVANCED_FIELDS`.
73
+ JSDOM verifies DOM interactions and accessible state. Its static ResizeObserver fixture does not establish browser layout geometry or production latency; the real-browser preview checks above cover the recorded layouts. Loopback inference does not establish live gateway availability or paid model behavior. `npm run verify:headless -- /path/to/consumer` remains available for a consumer containing the official CLI; it is separate from the default acceptance path.
52
74
 
53
- The guarded pi-ai 1.2.0 Google stream patch keeps separate Parts within a chunk and starts a new block when a later chunk supplies a different signature. Text, thinking and signature-only empty text retain their own signatures through Host block assembly, JSON restoration and the next request. Ordinary text deltas still accumulate, including signatures supplied on the first or final delta. esbuild and Vitest use the same version-checked, single-match substitutions.
75
+ The installed Harness CLI loaded the rebuilt package from an isolated `DSH_HOME`: Go completed one loopback request with the expected synthetic credential and session header (exit 0). A single public `beam-free` request to the real Free gateway returned HTTP 429 / `FreeUsageLimitError`, now surfaced as `FREE_QUOTA` (exit 1, expected upstream rejection). This validates error propagation, not live Free availability; no paid upstream inference was used. The 53,305-second backoff and Go coexistence were verified with deterministic HTTP/RPC fixtures.
54
76
 
55
- Validation on macOS / Node 24.14.1: both TypeScript projects compiled, **728 tests in 46 files** passed, and all **65 shipped artifacts** matched an independent rebuild. Four new client regressions and ten Google replay cases cover the fixes and continuing-delta behavior. The original seven audit probes also pass against source and the rebuilt bundle. The updated installed client and transcript fixtures passed across **all ten host generations** in **101.4 seconds**, including cleanup; npm Git installation and fresh pnpm 11.7.0 Git/tarball installation plus public type checks passed in **54.6 seconds**. Model requests used loopback fixtures, and installed user profiles were untouched.
56
-
57
- ## Second review round: an invalid draft keeps its error as its description, and the blank cap says what it inherits (2026-10-13)
58
-
59
- Codex reviewed the merged revision and found two more P2s, both caused by the shortening pass.
60
-
61
- The first is a real regression: `aria-describedby` pointed at the hint node alone, while the error moved to a separate `role="alert"` paragraph with no id. The old markup had put `invalidLabel` *inside* the described node, so an invalid proxy or numeric draft used to describe itself with its error; after the rewrite the alert announced the failure once and a later focus announced only the hint. The error node now carries `<id>-error`, and the control lists both ids while invalid — error first, hint behind it — dropping back to the hint alone once the draft is valid. A regression covers both states.
62
-
63
- The second is a wording error in the shortened copy. `numberField` parses an empty draft as `{ kind: 'clear' }`, so clearing the count cap re-inherits the composition default rather than removing the cap — the sentence the shortening dropped ("Clearing inherits the base configuration") was the correct one, and its replacement claimed an empty value means no cap at all. Both locales now say that blank inherits the configured default, uncapped only when that default sets no cap.
64
-
65
- ## PR review follow-up: the two switch hints became the switches' own descriptions (2026-10-13)
66
-
67
- Codex reviewed the pull request and found one P2: moving the provider and fallback explanations into `HintText` hid them behind a mark that only a pointer can open, and neither `Switch` referenced the hidden node, so focusing a switch announced its label alone. The finding is right, and the cause is the primitive: `Switch` accepts `{ checked, onChange, label, disabled, title, className }` on 0.1.5-rc.1, 0.1.6-alpha.1 and 0.2.0-rc.2 alike, and forwards no `aria-describedby`, so a hidden node can never be that control's description. Both switches now carry the sentence as `title` — the one channel the primitive forwards, and the pattern the drag handle already uses — while the mark keeps opening the same sentence in the Host's bubble. The hidden nodes are gone, so the sentence is no longer an unassociated paragraph in the reading order. The affected regressions now assert the switch's description instead of a hidden paragraph.
68
-
69
- ## Advanced rows: one card reset, no per-row badges, two more hints behind the mark (2026-10-12)
70
-
71
- Each advanced row carried its own `Customized` tag and `Reset to default` button. In the reporter's 538px card the label column is about 196px wide, so `DeepSeek V4.1 Flash 协议` (148px) plus a 64px tag, a 48px reset button and the hint mark needed 298px: the label lost its tail to an ellipsis while the button was squeezed to 30.8px and wrapped one character per line. The rows now carry no badges at all. The card owns a single `全部恢复默认` button beside its intro line; it stages a reset for exactly the rows that hold a user value (`ADVANCED_FIELDS`, through the same `resetField` the rows used) and stays disabled while every row inherits. Measured in headless Microsoft Edge at the reporting width against the built stylesheet, every label now fits — `DeepSeek V4.1 Flash 协议` is 147.9px in a 196px column — and the one label longer than its column, `请求图片数量上限(maxImages)` at 195px plus the mark, wraps to a second line instead of truncating, capped at two lines so one long name cannot turn a row into a paragraph.
72
-
73
- The divider between two rows of the same group is gone; spacing carries the grouping (`padding: 10px 0`) while the group heading keeps its hairline. The provider switch's paragraph and the automatic-fallback switch's paragraph became marks too — the same `HintMark`/`HintText` pair the rows use, so the sentence sits behind the question glyph, stays in the document for assistive technology, and both texts were shortened to one line of meaning.
74
-
75
- Validation: `npm run test:ci` passed a byte-identical `check:dist` for all 62 shipped files with **669 tests in 42 files** and both TypeScript projects clean. Four regressions cover the card reset (only the customized rows, and disabled while nothing is customized) and the two description nodes. The geometry above comes from a headless render of the built stylesheet at the reporting width, and the installed-host client fixture is re-run against a real DSH 0.2.1-alpha.1 consumer for this client-facing change.
76
-
77
- ## Advanced card blanked the settings page on DSH 0.2: the icon alias that no longer exists (2026-10-12)
78
-
79
- Expanding the advanced card emptied the whole settings surface, and every later visit stayed empty until the Harness process restarted. The hint mark resolved `primitives.IconQuestionOutline14`, the pixel-size name DSH 0.1.6 and older ship. 0.1.7 renamed icons by stroke weight, and 0.2.0-rc.2 — the generation the reporter runs — exports only `IconQuestionOutlineRegular` / `...Medium`, so the atom was `undefined`; React throws on an undefined element type, the Host's error boundary catches it, and that boundary then renders the same empty page for the rest of the process. Only the disclosure's first open triggered it, because the rows — and the mark — render for the first time there.
80
-
81
- Every Host atom lookup now goes through `hostAtom(host, current, previous)` in `src/client/host-primitives.ts`: the stroke-weight name first, the pixel-size alias behind it, the same order `ChevronDown` and the accounts card already used. The mark falls back to a text `?` when neither name exists, and `HintTooltip` falls back to a visible hint line, so a future rename costs a glyph instead of the page.
82
-
83
- The core suite and the client fixture both run against the checkout's `@deepseek-ai/dsh-client-ui-primitives` 0.1.6-alpha.1, which still ships the `...14` alias, so neither could see this. The installed-host matrix does open the advanced card — `tests/fixtures/client-compatibility.mjs` clicks the disclosure and asserts the proxy input — but it was not run for that change. `tests/host-primitives.spec.ts` now pins the resolution order for the current name, the older alias, and neither.
84
-
85
- Validation: `npm run test:ci` passed a byte-identical `check:dist` for all 61 shipped files with **665 tests in 42 files** and both TypeScript projects clean. The failure and the fix were both reproduced against a real DSH 0.2.1-alpha.1 consumer built from `tests/hosts/v021-alpha1`: the pre-fix tarball dies in `ValueField` with `Element type is invalid … but got: undefined`, and the same client fixture passes once the lookup resolves the stroke-weight name. The runner's 30-second per-fixture ceiling SIGTERMs that fixture on a cold Windows scan (it needs 35s here), so it was run directly against the installed consumer; CI's Linux job keeps it inside the ceiling.
86
-
87
- ## Advanced card: one setting per line, hints behind the mark, segmented enums (2026-10-12)
88
-
89
- The advanced card's eleven settings were a two-column grid whose hints ran from 16 to 123 characters, so the two columns never lined up and the card read as a wall of prose. Each setting is now one line under four headings — 用量与协议 / 连接与凭证 / 缓存与超时 / 图片处理 — with the label column taking the slack and every control in a fixed 300px column. Measured in headless Microsoft Edge at device scale 2 against the stylesheet extracted from the built `lib/client.js`: all eleven controls start at **x 417.00** and are 300px wide, row heights run 48.75–51.00px, and the segmented thumb lands on its segment within **0.02px** (three options at index 1: thumb 517.98–615.97 against the option's 518–616; two options at index 0: 420–567 against 420–567).
90
-
91
- Hints no longer take a line of their own. Each label carries the Host's own question glyph, and the Host's `Tooltip` primitive opens the sentence on hover; the same text stays in the document as the control's `aria-describedby`, so a screen reader still reads it without a tooltip. Both copy keys were rewritten to one sentence — the Chinese hints drop from about 500 characters to about 300 — and `advancedHint` keeps a single line under the card title.
92
-
93
- The two small enums (usage display, protocol) became segmented controls, which also answers the two defects the reporter measured on the native select: it drew its arrow about 4px from the border and its text about 2px below the box centre, and its focus ring doubled against the border. The segmented control keeps every option visible, slides one thumb between equal-width segments with a transform-only 160ms transition (removed under `prefers-reduced-motion`), and behaves as a `radiogroup` with arrow-key movement and a roving tab stop. Input focus now matches the Host's own Input — border colour alone — while an invalid field answers focus with an error-coloured halo instead of the brand border, so neither signal hides the other.
94
-
95
- Validation: `npm run compile` regenerated the artifacts, `npm run test:ci` passed a byte-identical `check:dist` for all 60 shipped files with **664 tests in 41 files** and both TypeScript projects clean. Four regressions cover the segmented control (its options, its staged selection, its read-only state, arrow-key movement), the hint wiring (the description node, one copy in the document), and the four headings in order. The focus rules were verified as built CSS and with a same-declaration render, because `:focus-visible` cannot be triggered from a headless dump (script focus does not set the keyboard-focus flag); the geometry above comes from a local headless render, not an installed profile.
96
-
97
- ## Usage pill switch alignment and the advanced card's summary (2026-10-12)
98
-
99
- The account row's `Switch ›` read high beside its label, and the label was the outlier, not the arrow. Measured in headless Microsoft Edge at device scale 2 against the stylesheet extracted from the built `lib/client.js` (320px panel, `-apple-system, "Segoe UI", "Microsoft YaHei"`), the button's box, the label's line box and the 14px chevron's box all centre on one line — so `align-items: center` was working — while the label's glyph ink sits **+0.50px** below that line: the line box is strutted by the Latin font while the CJK glyphs come from the fallback one, and the chevron's ink is centred on the box. The arrow therefore read **0.50px** high for 切换 and for `Switch` alike (the reporter's screenshot shows the same direction, 76.5 against 75.0 image px on a resampled capture). `.switch svg` now carries `display: block; transform: translateY(.5px)`, which moves the chevron's ink to **+0.50px** — level with the label — in both locales; a `usage-pill-material.spec.ts` guard pins the rule so a build that drops it fails, the way the panel's frosted material is guarded.
100
-
101
- The advanced card's header no longer repeats what the card says once it is open: the `额度显示 · 协议 · 凭证引用名 · 网关地址 · 目录刷新 · 流空闲超时 · 图片 4 项` / `Usage display · Protocol · …` summary and both `advancedSummary` copy keys are gone, leaving the title, the `Customized` tag and the disclosure chevron. The hint inside the opened card (`advancedHint`) is unchanged, and `Section.module.css`'s `.cardSub` stays because the accounts card still uses it. A `section.client.spec.tsx` regression holds the header to its title and one span.
102
-
103
- Validation: `npm run compile` regenerated the artifacts, and `npm run test:ci` passed a byte-identical `check:dist` for all 60 shipped build files with **661 tests in 41 files** and both TypeScript projects clean. The two new regressions cover the header row (title plus one span) and the chevron rule. One full-suite run lost `tests/proxy.spec.ts` and `tests/usage.spec.ts` to two crashed Vitest workers under Windows load; both pass alone (26 tests) and the repeat run passed every file. The geometry above was measured in headless Microsoft Edge against the stylesheet extracted from the built `lib/client.js`, not in an installed profile, so the alignment still needs the maintainer's eye on a real Host.
104
-
105
- ## Shared architecture and experimental Responses (2026-10-10)
106
-
107
- Settings row selection and write acknowledgements share one host/client contract. A mount-owned catalog manager supplies discovery to both the adapter and settings. Thrown and terminal inference failures share one pure fallback policy. Account commands and durable recovery run through one serialized host service, with revision checks, cancellation, deadlines, and idempotent command IDs. Client controllers retain staged edits and delegate account mutations to that service.
108
-
109
- The opt-in `deepseek-v4.1-flash` Responses override leaves Automatic as the default, resolves protocol compatibility on each captured request configuration, and does not mutate catalog entries or retry through another protocol. Eighteen focused regressions exercise real SDK wire payloads, tools, images, restored histories in both protocol directions, protocol errors, and captured configuration changes. Installed transcript and settings fixtures cover the override and clearing it through legacy and modern host services.
110
-
111
- Both TypeScript projects, all 655 tests in 41 files, freshness checks for 60 shipped artifacts, all ten installed DSH generations, npm/pnpm Git installations, and pnpm tarball installation passed. The desktop-installed build also passed offline benchmark observer/statistics checks.
112
-
113
- Twenty authorized synthetic live requests succeeded with one account, interleaved protocols, stable per-block sessions, and the same 4,096-token cap. Median first body-text latency changed from 3,340 to 3,298 ms under Default (1.3% faster) and from 3,161 to 3,085 ms under High (2.4% faster). Median total time changed from 3,920 to 3,695 ms (5.7%) and from 4,501 to 3,495 ms (22.3%). High's repeated samples instead showed 8.5% slower first text and 0.8% slower total time. Route identifiers differed between protocols; these identifiers alone do not establish the physical provider. Output lengths, reasoning tokens, caching, and small sample sizes limit conclusions: this experiment does not establish a stable latency benefit.
114
-
115
- Three preliminary live calls were excluded from those 20 samples; the third reached its earlier 1,024-token cap without body text, prompting the shared 4,096-token cap for the formal run. Desktop settings and credential-file hashes were unchanged after the formal benchmark. No private conversation was used.
116
-
117
- ## Token budget controls (2026-10-09)
118
-
119
- Budget-based models now display numeric token presets. The settings model card can stage, save, discard, and remove additional budgets through `modelLimits[modelId].thinkingBudgets`. Standard presets retain the old minimal/low/medium/high selection IDs; added values use `budget:N`. Models declaring both effort and budget keep their existing efforts and gain custom numeric choices. Adaptive Haiku continues using effort and exposes no legacy budget control.
120
-
121
- Both TypeScript projects, all 556 tests in 34 files, and freshness checks for 43 shipped artifacts passed. All ten installed DSH generations passed, including 19 reasoning streams per host with standard and custom budgets. The extended regression also verifies live settings snapshots, saved selection restoration, metadata outages, models supporting both control types, completion-format budget fields, budget bounds, and SDK reduction of a budget to preserve answer room. The first installed fixture run rejected Anthropic's query-bearing endpoint because the fixture compared the raw URL; comparing its pathname fixed the harness, and the final matrix passed.
122
-
123
- Twelve authorized, bounded synthetic requests through the rebuilt adapter passed: Qwen 3.7 Plus Default, Off, four standard budgets, and custom 4,096/6,000 budgets; Qwen 3.8 Max Low and a custom 4,096 budget; and a Qwen 3.7 Plus tool call followed by a continuation after JSON restoration of signed assistant/tool history. Captured requests carried the selected `budget_tokens` values, Default omitted thinking controls, and the restored continuation returned `CHECK-739`. Credentials stayed in memory and the user's installed profile was unchanged.
124
-
125
- ## Unified reasoning intent (2026-10-09)
126
-
127
- Default now leaves thinking and effort controls to the service for all three protocols. Model information no longer declares a fallback `defaultEffort`. Switch-only models display On / Off, retain the previous `high` selection ID, and send the SDK's native switch/budget without an invented effort. Adjustable models retain their advertised levels. A shared request boundary removes automatic controls for Default while preserving other SDK output, rendering, and history configuration.
128
-
129
- `DSH_COMPAT_CONCURRENCY=4 npm run verify` passed all 549 tests in 34 files, freshness checks for 43 shipped artifacts, all ten installed DSH generations, and npm/pnpm installation checks. Each installed host also passed 14 reasoning streams, including Default, explicit Low/Off, native On/Off, and MiMo mappings. Type checking passed for both projects. Regressions cover Haiku adaptive efforts with live metadata and an outage, all four native completion switch formats, and nonmutating payload edits.
130
-
131
- With the user's authorized credential held in memory, 20 bounded synthetic requests through the rebuilt adapter passed:
132
-
133
- - Haiku 5.5, DeepSeek V4 Flash, GPT-6 Luna, MiniMax M3, and MiMo V2.6 Flash: 14 Default and explicit-choice requests. Every Default wire payload omitted thinking switches and effort settings. MiniMax's picker showed On / Off, and the saved `high` ID enabled its native thinking budget without an effort field.
134
- - Haiku, DeepSeek, and GPT: six Default requests covering tool calls and continuations after JSON restoration of assistant/tool history. All three returned the synthetic tool result `CHECK-739`.
135
-
136
- The service decided whether to reason under Default; observed reasoning blocks differed by model. The earlier pi-ai upgrade records below describe the behavior before this change, including its previous Default-to-disabled/native-High policy.
137
-
138
-
139
- ## pi-ai 1.1.0 and Haiku 5.5 adaptive thinking (2026-10-09)
140
-
141
- Upgraded the private bundled pi-ai alias from 0.87.1 to 1.1.0, with its matching external transports: OpenAI 7.19.0 and Anthropic 0.129.0. The upstream OpenCode Go catalog now supplies Haiku 5.5's `forceAdaptiveThinking` compatibility flag; the existing metadata merge retains it without a model-specific production patch. Refreshed the bundled implementation, public declarations, and upstream v1.1.0 license.
142
-
143
- The Haiku regression captures real SDK requests against a loopback gateway for low, medium, high, xhigh, max, and off, with both live metadata and metadata failure. Enabled efforts use adaptive thinking and `output_config.effort`, never `budget_tokens`; off disables thinking. Native-toggle fixtures now use DeepSeek V4 Pro because the new upstream catalog removed Qwen 3.6 Plus.
144
-
145
- On macOS, `DSH_COMPAT_CONCURRENCY=4 npm run verify` passed 539 tests in 34 files, freshness checks for 43 shipped artifacts, all ten installed DSH generations (0.1.5-rc.1 through 0.2.1-alpha.1), and npm/pnpm installation checks. Existing three-protocol fixtures cover prompts, tools, restored replay, and continuations. Installation assertions additionally verify that a consumer keeps public pi-ai 0.85.1, OpenAI 6.40.0, and Anthropic 0.124.0 while the plugin resolves OpenAI 7.19.0 and Anthropic 0.129.0. Fresh pnpm Git and packed installs require no build approvals.
146
-
147
- Clean `npm ci --strict-peer-deps` and `npm run test:ci` passed with Node 22.19.0 / npm 10.9.3 and Node 24.14.1 / npm 11.11.0. Regenerated the lockfile with npm 10 to retain Vite's optional esbuild peer records. The initial automated run used local fixtures; live validation is recorded below. The user's installed profile was not changed, and nothing was published.
148
-
149
-
150
- ### Expanded live validation (2026-10-09)
151
-
152
- With the user's authorization, used the existing default OpenCode Go credential in memory through the rebuilt plugin adapter. Requests used synthetic prompts, synthetic tool results, temporary DSH homes, no account switching, no retries, and bounded output. Credentials, headers, and private conversation data were not saved in the report.
153
-
154
- - Haiku 5.5: default, off, low, medium, high, xhigh, and max all completed. Wire capture confirmed adaptive thinking with the requested effort for every enabled level; max produced a reasoning block. Default/off send disabled thinking. Adaptive thinking can legitimately skip a reasoning block on an easy question.
155
- - Three real protocols: Haiku 5.5, DeepSeek V4 Flash, and GPT-6 Luna each called a synthetic `lookup_code` tool, then returned `CHECK-739` after JSON-restored assistant history and a tool-result continuation.
156
- - Haiku max reasoning solved a probability question as 7/22. Restoring that signed reasoning history and switching to off succeeded. Cancelling after the first text delta returned ABORTED, and a subsequent request on the same adapter succeeded. A supplied temperature was omitted for Haiku as expected.
157
- - MiniMax M3 high, Qwen 3.8 Max low, MiMo V2.6 Flash off, and DeepSeek V4 Flash low/off all completed with correct short answers. The 23 recorded live scenarios include one intentional cancellation; an additional initial tool call was repeated after fixing the test harness's attempt to mutate a frozen user message.
158
- - An offline old/new request-body comparison covered Haiku plus six current models (DeepSeek V4 Flash, GPT-6 Luna, MiniMax M3, Qwen 3.8 Max, MiMo V2.6 Flash, Kimi K3), including system prompts, tools, effort, temperature, and output caps. Only Haiku changed: adaptive thinking and output_config replace the old budget mode. MiniMax/Qwen's budget additions to max_tokens are unchanged existing behavior.
159
-
160
- The new upstream catalog removes Kimi K2.6, GLM 5.1, Qwen 3.7 Max, and Qwen 3.6 Plus from its built-ins even though the gateway still lists them. A Qwen 3.6 Plus Off request was rejected locally after its legacy compatibility hints disappeared. Per the user's instruction to discard obsolete models, no legacy catalog snapshots or compatibility exceptions are retained. Live metadata may still make some such models discoverable, but their old built-in fallback/controls are no longer guaranteed.
161
-
162
- Added Haiku to the durable signed-thinking/alias/replay regression alongside MiniMax; the final `npm run test:ci` passed all 540 tests in 34 files and all 43 artifact freshness checks. No further failures were observed in the current-model scenarios; live probes do not establish exhaustive compatibility or long-running reliability.
163
-
164
- ## Issue #40 follow-up: upstream routing evidence and misleading occupancy (2026-10-09)
165
-
166
- The October 8 comments report the same failure on DSH's built-in provider, with other models still working in the same conversation. This weakens a plugin-only explanation but does not identify the failed request's upstream cause. OpenCode users independently report the same DeepSeek model-only HTTP 400 body: [#51990](https://github.com/anomalyco/opencode/issues/51990) and [#51201](https://github.com/anomalyco/opencode/issues/51201). The latter reports byte-identical 200/400/200 replays; [#50761](https://github.com/anomalyco/opencode/issues/50761) instead investigates route-dependent capacity limits. [#48180](https://github.com/anomalyco/opencode/issues/48180) reports a separate history/reasoning-parameter trigger. These are reporter observations, not a confirmed diagnosis of this issue, and they show why the opaque error alone cannot justify treating every such 400 as transient.
167
-
168
- The public legacy gateway [selects a weighted provider from the session ID suffix](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/console/app/src/routes/zen/util/handler.ts#L641-L648); its [sticky record is keyed by model and session](https://github.com/anomalyco/opencode/blob/388406238bd5ca15564a762840a2362c3a45bd9c/packages/console/app/src/routes/zen/util/stickyProviderTracker.ts#L11). This supports investigating a session/model route that repeatedly fails. It does not prove the online failing request used this implementation: the current handler can first proxy migrated workspaces to a separate inference service. Live provider configuration and the reporter's response headers remain unknown.
169
-
170
- A disposable consumer of the actual published `@deepseek-ai/dsh-token-meter@0.2.0-rc.2` reproduced another key distinction. The context-pressure projection went from 200,000 tokens / 20% to zero / 0% when an `assistant/attempt` carried the SDK's zero usage followed by `INVALID_REQUEST`; no compaction or surface replacement event was involved. Independently, 100 samples of 1,000,000 cached input tokens accumulated 100,000,000 tokens while latest context pressure stayed 1,000,000. The [last-sample pressure update](https://github.com/deepseek-ai/deepseek-harness/blob/dsh-v0.2.0-rc.2/packages/llm/token-meter/src/usage-projection.ts#L192-L196) explains why a displayed 0% does not establish empty request history, and a cumulative nine-digit counter does not establish a nine-digit prompt. These were projection probes, not a reproduction of the reporter's real UI/session.
171
-
172
- The current [models.dev entry](https://models.dev/api.json) still advertises a 1,000,000-token context and 384,000-token output capacity, with low/high/max reasoning and `reasoning_content` interleaving. Capacity is not a measurement of this failed request. An output-cap comparison, history-shape comparison and an identical-body/session-ID comparison are useful separate experiments; changing all of them together would not isolate the cause. No real inference request or user credential was used in this investigation.
173
-
174
- The plugin's diagnostic allowlist previously omitted Go's route/log fields. It now retains `x-opencode-log-id`, `x-opencode-endpoint-id`, `x-opencode-upstream-model-id`, `x-zen-model` and `cf-placement` when present, with the existing redaction and length bounds. The model-only JSON regression confirms that the nonempty body remains visible while 400 stays `INVALID_REQUEST`, with one attempt and the same session ID. **538 tests in 34 files**, freshness checks for **43 shipped artifacts**, and the installed **DSH 0.2.0-rc.2** fixture passed (61.3 seconds including cleanup). This improves the next capture; it does not fix or reproduce the upstream 400. The follow-up is included with the user-facing README rewrite. After integration with pi-ai 1.1.0, all 541 tests and 43 artifact freshness checks passed; the changes have not been published.
175
-
176
- ## Issue #43: first installation without dependency script approvals (2026-10-09, unreleased)
177
-
178
- The issue attachments report that DSH 0.2.0-rc.2 / pnpm 11.7.0 can leave the plugin dependency in `package.json` after script approval interrupts the first install. A successful retry can then miss bundle registration. Inspection of the published DSH plugin-manager source confirms that its reconciliation skips dependencies already present in the pre-install snapshot. This package change avoids the dependency-script interruption that triggers that path; it does not repair an already incomplete DSH profile or change DSH's installer.
179
-
180
- The plugin now bundles the required pi-ai 0.87.1 code and OpenCode Go model table before distribution. The private SDK alias is a development dependency, so consumers no longer install its unrelated Google SDK and protobufjs dependencies. OpenAI 6.40.0, Anthropic 0.124.0, Undici and DSH service imports remain external. The three protocol entrypoints are imported eagerly: the initial lazy-entrypoint bundle failed the installed reasoning fixture before the first stream because a shared constant had not initialized. The final eager bundle passes that existing regression. SDK declarations are bundled too, with the original third-party licenses included.
181
-
182
- On macOS / Node 24.14.1, the final artifacts passed **537 tests in 34 files**, both TypeScript projects, and freshness checks for **43 shipped artifacts**. All **ten installed-host generations** passed (31.7 seconds including cleanup), including DSH 0.2.0-rc.2 and 0.2.1-alpha.1; fixtures exercise model information before streaming, all three protocols, tool calls, restored replay, real images, settings, client rendering and bundle admission.
183
-
184
- The final installation checks passed in 11.5 seconds. Fresh pnpm 11.7.0 consumers installed both Git and packed npm artifacts with explicit `strictDepBuilds=true`, separate empty stores, isolated configuration and **no build approvals or script skips**. Both installed 32 packages and passed ESM loading and public type checks; assertions confirm pi-ai, Google SDK and protobufjs are absent. An npm Git consumer independently retained another plugin's public pi-ai 0.85.1 peer resolution. Earlier tests had preconfigured Google/protobuf skips; those skips are removed. Verification used disposable consumers, not the user's installed profile. The reporter's Windows installation and real Plugin Hub UI were not reproduced here. The changes have not been published to npm.
185
-
186
- ## Issue #40: HTTP 400 investigation and diagnostic capture (2026-10-07)
187
-
188
- The reported intermittent rejection has not been reproduced against the real gateway. Two distinct loopback HTTP 400 responses — an empty body and `{"message":"edge rejected the request"}` — both produced the exact SDK message `400 status code (no body)` before this change. OpenAI's SDK parses the latter as JSON, extracts only its top-level `error`, and discards the other fields before pi-ai normalizes the error. Thus `(no body)` does not establish an empty wire response, a gateway routing failure, or failure to reach the model. pi-ai also initializes usage to zero and the plugin emits that usage on error; zero tokens do not establish where a request failed. The plugin itself maps 400 text to `INVALID_REQUEST`, and the real DSH 0.2.0-rc.2 provider policy excludes that code from default retries.
189
-
190
- The adapter now captures non-success HTTP responses through a per-attempt fetch wrapper before SDK normalization. It returns the original response, retains the existing direct/proxy transport and session header, and leaves success streams alone. Error messages identify the provider/model and observed body state; when the SDK discarded a nonstandard JSON body, a bounded excerpt restores its content. Request rejection retains its prior failure classification and does not gain hidden automatic retries.
191
-
192
- Opt-in `DSH_OPENCODE_GO_DEBUG_DIR` writes separate error records with the endpoint without authentication/query parameters, serialized request byte size, message/tool counts, final output cap and reasoning settings, a hashed session ID, HTTP status, selected response headers and the first 16 KiB of error body. Capture reads a response clone concurrently with the SDK, stops after one second or cancellation, and marks incomplete/truncated reads. API keys and configured proxy authentication, including JSON-escaped and partially truncated echoes, are redacted. Request content, cookie/authentication headers and unknown response headers are omitted. File failures add a diagnostic notice without replacing the original HTTP failure. Both READMEs describe enabling the capture and reviewing upstream error content before sharing.
193
-
194
- A public, read-only `https://models.dev/api.json` check on 2026-10-07 advertised this model with a 1,000,000-token context and 384,000-token output limit. A mounted-plugin loopback probe using those metadata values, a two-character prompt and no explicit generation cap actually sent `max_tokens=384000`, `reasoning_effort=high` and `thinking.type=enabled`; its whole request was 205 bytes. The eventual cap and reasoning parameters deserve comparison between failing and successful requests. This is a possible parameter difference, not a demonstrated cause of the real rejection. No paid inference request was made.
195
-
196
- Validation on macOS / Node 24.14.1: Host/Client type checks, **500 tests in 33 files**, and all **40 shipped artifacts** passed. The 13 added regressions cover empty/nonstandard/standard error bodies, successful streaming, file failures, bounded and exact-limit reads, stalled clones, concurrent session correlation, caller cancellation and secret redaction. The rebuilt package passed **all ten installed-host generations** in 38.1 seconds including cleanup. Each host exercises the nonstandard 400 through the mounted route and reads the saved request ID; 0.2.0-rc.2 and 0.2.1-alpha.1 additionally assert the actual retry policy. The earlier baseline matrix also passed before the final capture-boundary/redaction refinements. User credentials and installed DSH profiles were not changed. A real failed request's captured headers/body, plus a successful request's parameters, are still needed to determine the upstream cause.
197
-
198
- ## Release 0.1.21: include the frosted usage panel (2026-10-07)
199
-
200
- The release combines the network proxy settings and DSH 0.2.1-alpha.1 compatibility with PR #39's dedicated usage-panel material and inner scroll container. The release includes the final merged implementation of both changes.
201
-
202
- An independent PR checkout passed strict `npm ci`, **487 tests in 32 files**, and freshness checks for **39 shipped artifacts** on macOS / Node 24.14.1. A direct Lightning CSS probe reproduced the declaration merge: placing the prefixed spelling last emitted only that spelling; an unprefixed declaration retained `backdrop-filter`. The PR's [13 GitHub checks](https://github.com/Duskriver/dsh-opencode-go/actions/runs/37408981047) and the [merged main checks](https://github.com/Duskriver/dsh-opencode-go/actions/runs/37493340359) passed, including the ten installed-host generations and npm/pnpm Git installs. The complete local verification passed 487 tests, 39 shipped artifacts, all ten installed-host generations and both npm/pnpm Git installation checks.
203
-
204
- ## Usage panel frosted glass: the build dropped the working blur (2026-10-06)
205
-
206
- The panel's frosted attempts shipped no blur at all, and the hunt misread why. `scripts/build.mjs` minifies every CSS module with lightningcss, which treats `backdrop-filter` and `-webkit-backdrop-filter` as one logical property and ships only the LAST spelling in the file. The source listed the prefixed spelling last, so `lib/client.js` carried only `-webkit-backdrop-filter` — and current Chromium ignores that alias entirely. Replayed over identical text in headless Edge (standard deviation of the panel interior; **58.06** is the unblurred baseline): the prefix-only rule measured **58.06** and produced a byte-identical screenshot to no filter at all, the unprefixed rule measured **1.60**, and the shipped `var(--dsw-menu-backdrop-filter, blur(40px) saturate(150%))` form measured **1.60** with the same bytes. The failure was silent in the other direction too: with only the prefixed declaration left, `@supports not (backdrop-filter: blur(1px))` still evaluates as supported, so the opaque fallback never engaged — the panel stayed translucent over unblurred chat, which is exactly what every earlier attempt looked like. The structural theories chased around it (composer ancestors as backdrop roots, element-level `isolation`, portaling to `document.body`) were orthogonal: lightningcss stripped the blur from every build, including the portal build whose marker was proven live. The plugin the report compared against blurs because its stylesheet is a raw string that never passes through lightningcss.
207
-
208
- The fix paints the material the way the host's own `MenuSurface` does: a dedicated `.material` child (absolute, `inset: 0`, `z-index: -1`, `pointer-events: none`) carries `var(--dsw-menu-surface-fill)` under `var(--dsw-menu-backdrop-filter)`; the panel keeps `isolation: isolate` so the negative-index layer cannot slip behind the composer card; scrolling moved from the panel to an inner `.body`, so the material never rides a scroll container; the hairline became the host's elevation stroke. Only the unprefixed `backdrop-filter` appears in the source, with a comment naming the trap, and `tests/usage-pill-material.spec.ts` pins all three invariants: no prefixed spelling in the stylesheet, the blur on `.material` rather than on the panel or its scroller, and the `@supports` arm that falls back to an opaque layer-2 surface.
209
-
210
- Validation: `npm run typecheck` and the production build passed; `npm run test:ci` passed **487 tests in 32 files** and the byte-for-byte freshness check for all **39 shipped artifacts** on Windows / Node 24.15.0. `DSH_COMPAT_CONCURRENCY=4 npm run test:compat` passed **all ten installed-host generations** in **256 seconds** including cleanup once the local per-fixture ceiling was raised to 180 seconds: the client fixture needs **30.3–31.6 seconds** here, just over the committed 30-second limit, which is why the default ceiling SIGTERMs it on this machine. The built stylesheet was replayed in headless Edge over a dense text wall (interior std **1.60** against a **58.06** text baseline), and an A/B twin with the filter disabled kept the ghost text. The same stylesheet and panel markup were installed in a Desktop host before this branch, where the blur appeared after restarting the Host; the merged build was not re-installed here, and the Host serves plugin code only at startup.
211
-
212
- ## Network proxy validation and DSH 0.2.1-alpha.1 (2026-10-06)
213
-
214
- Settings now accept HTTP, HTTPS and SOCKS5 proxy URLs, including URL-encoded authentication. The per-mount Undici transport routes model inference, gateway model discovery, models.dev metadata and account usage through the saved proxy. Blank explicitly disables an inherited proxy; Reset removes the override. Settings changes apply to later requests while active response bodies finish on their original connection, and proxy failure never falls back to a direct connection.
215
-
216
- DSH 0.2.1-alpha.1 has an independent installed-host fixture with its complete published DSH dependency generation, Cordis 4.0.5-alpha.1 and Schemastery 3.18.5-alpha.1. It omits the removed invariant package. The plugin does not use invariant exports or replace the retired composer statistics row; the quota pill's `conversation.input.right` slot remains declared in the published composer. Client fixtures now verify that declaration and the expanded proxy field. Real profile fixtures exercise saving all three proxy schemes, rejecting invalid values, clearing and resetting without remounting the plugin.
217
-
218
- On macOS / Node 24.14.1, `DSH_COMPAT_CONCURRENCY=4 npm run verify` passed **484 tests in 31 files**, freshness checks for **39 shipped artifacts**, **all ten installed-host generations** (38.2 seconds including cleanup), and both **npm and pnpm Git installation checks** (9.4 seconds). Proxy regressions use authenticated loopback HTTP CONNECT and SOCKS5 servers across all three model protocols, verify metadata and usage routing, preserve HTTPS certificate and hostname validation, and cover failed connections, captured requests, live changes and cancellation. Clean copies also passed strict `npm ci` and the core checks with Node 22.19.0 / bundled npm 10.9.3 and Node 24.14.1 / bundled npm 11.11.0. Automated inference uses local fixtures. The earlier Desktop installation loaded the proxy input on DSH 0.2.0-rc.2; latest-host validation uses an isolated installed consumer.
219
-
220
- ## Account card owns credential editing (2026-10-06)
221
-
222
- The settings page no longer shows the standalone preferred-account Key field when account management is available. The first card contains provider activation; adding or replacing credentials happens in the account card. Callers without account actions retain the standalone credential control. The production build, Host/Client type checks, all **466 tests in 30 files**, and the freshness check for **37 shipped artifacts** passed. Existing model-switch and locale-change draft checks now exercise the staged refresh interval, while account credential behavior remains covered by the account suite. The user tested the installed Desktop package and accepted the changes before requesting the merge.
223
-
224
- ## PR #38 account regressions (2026-10-06)
225
-
226
- Adding an account now persists a configured legacy account, or one whose credential status is unknown, so selecting the new account cannot remove the old one from the list or fallback candidates. Only a confirmed empty legacy placeholder is discarded. Removing a managed account now retains its metadata and key when credential description fails or omits that reference, allowing the same operation to be retried. Gateway key rejections are cached by normalized endpoint and credential reference; switching endpoints rechecks the preferred account, returning to an endpoint retains its own rejection, and replacing a credential clears its rejections across endpoints.
227
-
228
- On macOS / Node 24.14.1, the production build passed both Host and Client type checks. `npm run test:ci` passed **466 tests in 30 files** and the byte-for-byte freshness check for all **37 shipped artifacts**. Seven added regressions cover legacy default/custom references, rejected/thrown/missing credential descriptions with successful retries, endpoint isolation, equivalent trailing-slash URLs, and credential invalidation across endpoints; the existing unknown-credential test now verifies preservation through repeated additions and selection changes. `npm run test:compat -- v020-rc2` passed the installed Host, transcript, reasoning, client, profile and bundle fixtures for **DSH 0.2.0-rc.2** in **63.5 seconds** including cleanup. Automated checks used local gateway fixtures; the user subsequently accepted the installed Desktop package.
229
-
230
- ## Usage pill collapses under a narrow composer instead of wrapping or overflowing
231
-
232
- The account usage pill in the composer's trailing control group rendered its whole label — brand prefix, both window readings with unit words, the optional stale suffix, and (with more than one account configured) the selected account name — as one unbreakable string. That group is `flex: none` by design, so below the Host's own ~460px composer-row breakpoint it already wraps onto its own line; the pill's own untruncated width then forced a *second* wrap inside that line. Narrower still, the pill's width exceeded the row's remaining content box and pushed the send/model controls past the composer card's right edge. The multi-account work made the worst case worse: the account name is the longest of the four segments and appears exactly where space is scarcest.
233
-
234
- The fix keeps the label as one string for accessibility — `aria-label` and the newly added `title` carry the full text, and `textContent` still concatenates to exactly the old string — but renders it as addressable `<span>`s (a `brand` prefix, a `reading` group with a `data-stale` flag, `unit` words inside the reading, a `stale` suffix, and an `account` segment). The account segment is capped at `12ch` with an ellipsis to keep valid names of up to 80 characters from making the pill arbitrarily wide. Two anonymous `@container` tiers use the same composer row already used by `PermissionSelect.module.css`:
235
-
236
- * **≤ 460px** — hide the `brand` prefix, `stale` suffix and account name. A bare `::after` asterisk on `[data-stale]` replaces the stale wording, so the signal survives without adding text. The popup's own account selector restates the account name in full.
237
- * **≤ 360px** — additionally hide the `unit` words, leaving only the bare percentages.
238
-
239
- Both tiers are CSS-only (`display: none`), so `textContent` — and every existing assertion built on it — is unaffected; jsdom does not evaluate container queries, so the unit tests exercise the unsegmented string in both states. The non-usage state (`···` / unavailable) gets the same treatment for the account segment and keeps the brand span, so a failure while a second account is selected does not reintroduce the overflow.
240
-
241
- Validation: `npm ci --strict-peer-deps`, both TypeScript faces, and the production build passed on Linux / Node 22.23.3 / npm 10.9.9. `npm run test:ci` passed **431 tests in 30 files** and the freshness check for all **37 shipped build files**, including twelve added segment assertions in `tests/usage-pill.client.spec.tsx` (the `brand`/`unit`/`reading`/`stale` shape, the `title` attribute, the unsegmented non-usage state) and three new cases for the account segment — present and hideable with more than one account, present in the non-usage state, and absent with at most one. One pre-existing `tests/localization.client.spec.tsx` assertion moved from `getByText` to the trigger's `textContent`, because the label is no longer a single text node. `DSH_COMPAT_CONCURRENCY=4 npm run test:compat` passed **all nine installed-host generations** in **46.9 seconds** including cleanup, and `npm run test:install` passed for both **npm (4.7 s)** and **pnpm (2.5 s)** Git installations, loading the shipped artifacts without source builds or plugin build approval. Every check used loopback fixtures; the 460px and 360px cut points were not re-measured in a browser on this branch, and no installed user profile, GitHub issue or npm publication was changed.
242
-
243
- ## Release 0.1.20: usage pill and settings draft fixes (2026-10-02)
244
-
245
- The release includes the Host scope receiver fix and shared no-op draft predicate from PR #34. The installed-client compatibility fixture now reads instance state and exercises both SSR snapshot reads and client subscription cleanup. Desktop follow-up confirmed that saving Always works after restarting a Host process that predated the installed plugin update; the upgrade instructions now explicitly require quitting and reopening desktop Harness.
246
-
247
- On macOS / Node 24.14.1, `DSH_COMPAT_CONCURRENCY=4 npm run verify` passed **401 tests in 28 files**, freshness checks for **34 shipped build files**, **all nine installed-host generations**, and both **npm and pnpm Git installation checks**. The compatibility run took **87.9 seconds** including cleanup; installation checks took **11.8 seconds**. Both installation paths loaded the shipped artifacts without source builds or plugin build approval and retained the private pi-ai 0.87.1 alongside the independent peer's 0.85.1. The test suite used local gateways and isolated consumers.
248
-
249
- ## Usage pill rendering on a real Host and the staged override badge (2026-10-02)
250
-
251
- The browser half no longer hands the Host settings scope's methods over as plain values. `UsagePill` subscribed with `useSyncExternalStore(settings.subscribe, settings.getSnapshot, ...)`, and a Host scope is a class instance whose methods read their own state (`ConfigFormController` behind `ctx.configForms.get(entryId)`, `SettingsScopeController` behind `ctx.settingsScope.bind(spec)`); both providers return that instance as-is. The detached reference lost its receiver, so the pill threw `TypeError: Cannot read properties of undefined (reading 'store')` on its first render, the slot's per-entry error boundary removed it, and no usage mode could ever show a pill — including Always, which is why a saved `usageDisplay: always` had no visible effect. React now reaches the scope through `useCallback` wrappers, the same shape the Host's own slot outlet uses.
252
-
253
- The staged form's override badge now answers for the same state its save plan does. `plan` skipped a draft equal to the field's effective value while `field` reported it as an override, so a value typed back to its original (`60 → 59 → 60`) disabled Save and Discard yet kept the "overridden" badge, its reset button, and the advanced card's badge. Both paths now share one `unchangedDraft` predicate: a no-op draft reports whatever the user layer already holds, a clear still previews as not overridden, and an invalid draft still blocks the save.
254
-
255
- Before the fix, `npx vitest run tests/usage-pill.client.spec.tsx tests/stores.client.spec.ts` failed 25 of 54 cases: all **24** usage-pill cases (including the new receiver case) with the detached-call `TypeError`, and the new no-op-draft case on `overridden`. The test double now keeps its scope methods on the prototype (`tests/support/settings-scope.ts`) and the installed-host fixture builds the same class shape, so a method passed as a value fails in tests exactly as it failed in a browser; before this change every double used arrow-function literals, which is what hid the regression from the suite and from the `v020-rc2` compatibility row.
256
-
257
- Host/Client type checks and production compilation passed; `npm run test:ci` passed **401 tests in 28 files** and freshness checks for all **34 shipped artifacts** on Windows / Node 24.15.0. `DSH_COMPAT_CONCURRENCY=1 npm run test:compat -- v020-rc2 v017-rc2` passed both installed generations — 0.2.0-rc.2 and 0.1.7-rc.2 (both use the `configForms` path) — including the client fixture's module table, settings binding, rendering, CSS and cleanup. On this machine the client fixture also exceeded the runner's 30-second per-fixture limit before this change, so the two local rows ran with that limit temporarily raised to 180 seconds; the committed limit is unchanged. The other seven host generations, macOS and Linux, and a live Desktop session were not exercised. Every check used loopback fixtures; no installed user profile, GitHub issue or npm publication was changed.
258
-
259
- Follow-up on macOS / Node 24.14.1: the installed-host fixture now reads `this.snapshot` and `this.listeners`; prototype methods that only closed over local variables still allowed detached calls. Its SSR checks exercise the snapshot reader, and a client mount/unmount now checks the scope subscription and cleanup while Auto keeps the pill hidden. The receiver unit case also checks a live display change and subscription disposal. Two temporary negative-control bundles failed as expected: the original detached snapshot reader threw on `snapshot`, and a client with only the subscription detached threw on `listeners`; the fixed bundle passed. Both type checks, **401 tests in 28 files**, all **34 shipped artifacts**, and **all nine installed-host generations** passed. The compatibility run took **88.2 seconds** including cleanup, with the committed 30-second fixture limit unchanged. The matrix exercises legacy `settingsScope` on 0.1.5/0.1.6 and `configForms` on 0.1.7/0.2. No live Desktop UI or installed user profile was exercised.
260
-
261
- ## Release 0.1.19: consolidate pending features (2026-10-02)
262
-
263
- The prepared-call configuration, Anthropic alias replay and usage-display changes are now integrated with 0.1.18's MiMo controls. Prepared calls retain the captured endpoint, credential reference and nested capacities; later calls use updated settings. Anthropic replay preserves signed thinking against the requested model ID. Settings now offer Auto / Always / Off usage visibility, with Off and a disabled plugin stopping the poller. The existing default remains Auto.
264
-
265
- Host/Client type checks, production compilation, **398 tests in 28 files**, and freshness checks for **34 shipped artifacts** passed on macOS / Node 24.14.1. All **nine installed-host generations** passed, including DSH 0.2.0-rc.2, prepared-call dispatch, signed alias replay, MiMo Default/Off controls and usage-display settings. The complete compatibility run took 73.8 seconds including cleanup. Both npm and pnpm 11.7.0 Git installations passed without plugin build approval, retaining the plugin's private pi-ai 0.87.1 and the independent peer's 0.85.1. This integration uses isolated local gateway fixtures; the earlier live MiMo observations are recorded in the 0.1.18 entry.
266
-
267
- ## Release 0.1.18 validation (2026-10-02)
268
-
269
- The 0.1.18 manifest passed Host/Client type checks, production compilation, all **381 tests in 27 files**, and freshness checks for **33 shipped artifacts** on macOS / Node 24.14.1. Both npm and pnpm 11.7.0 Git installations passed with the new version, without plugin build approval; the plugin retains private pi-ai 0.87.1 and the independent peer retains public pi-ai 0.85.1. The nine-host compatibility checks and live OpenCode Go observations for this reasoning fix are recorded below.
270
-
271
- ## Issue #28: MiMo reasoning controls (2026-10-02)
272
-
273
- The metadata reader no longer derives an `off` effort spelling from intrinsic reasoning or creates generic OpenAI effort levels from toggle/budget declarations. Established DeepSeek/Qwen native switches remain supported. An exact `mimo-v2.6-flash` / OpenAI Completions rule supplies **Off, Low, Medium, High** in both online metadata and built-in outage fallbacks. Off maps to `none`; an unset effort suppresses the SDK's implicit Off mapping on a request-local model copy, preserving gateway-default reasoning and the shared picker map. Other MiMo models do not inherit this rule.
274
-
275
- Direct streaming requests to **OpenCode Go**, using the same TypeScript interval-merging prompt and a 4,096-token output cap, produced the following observations. Counts measure individual responses, not comparative reasoning quality or guaranteed effort ordering.
276
-
277
- | Wire `reasoning_effort` | HTTP | Reasoning tokens | Finish |
278
- | --- | ---: | ---: | --- |
279
- | Omitted | 200 | 122 | stop |
280
- | `none` | 200 | 0 | stop |
281
- | `low` | 200 | 626 | stop |
282
- | `medium` | 200 | 383 | stop |
283
- | `high` | 200 | 71 | stop |
284
- | `off` | 400 | — | Invalid request parameters |
285
-
286
- Two further live calls went through the rebuilt plugin, DSH LLM runtime and actual SDK, using an isolated temporary metadata cache. Default omitted the wire field and returned **14 reasoning tokens**, 74 reasoning characters and 1,605 answer characters. Explicit Off sent `none` and returned **0 reasoning tokens**, no reasoning characters and 902 answer characters. Both returned HTTP 200 and finished with `stop`. This confirms Default remains distinct from Off through the implemented request path; the installed user profile was not changed.
287
-
288
- Host/Client type checks and production compilation passed. `npm run test:ci` passed **381 tests in 27 files** and freshness checks for all **33 shipped artifacts**. New regressions cover online and outage paths, all four supported choices, rejection of unsupported choices before inference, Default/Off alternation without shared-map mutation, exact-model/protocol boundaries, future advertised `none`, and native controls. `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**, with 13 reasoning streams per generation plus the existing Host/Client/transcript/profile/bundle checks; total time including cleanup was 88.2 seconds. Other operating systems and a Desktop GUI session were not exercised for this change.
289
-
290
- ## Prepared-call configuration and Anthropic alias replay (2026-09-30)
291
-
292
- The adapter now copies its configuration, including nested model limits, before catalog discovery. Its `prepareCall` binds the resolved model, catalog provider and that configuration to the eventual stream. Credential resolution receives the captured configuration instead of re-reading the current key reference. This keeps an already prepared request on its original endpoint and credential reference while later requests use changed settings. Direct streams use the same capture path. Existing credential values still resolve through the Host service at dispatch; the reference is the frozen fact.
293
-
294
- Anthropic replay now reconstructs the assistant with the requested model ID. The provider-reported alias remains in `responseModel` as informational metadata, preserving envelope version 2 and previously saved history. pi-ai therefore retains signed thinking when continuing the same requested model, while an actual switch to another model still strips those signatures.
295
-
296
- Before the fix, `npx vitest run tests/call-snapshot.spec.ts tests/provider-identity.spec.ts` failed all four new regressions: nested limits changed during discovery, a prepared request went to the new endpoint, a direct stream combined the old endpoint with the new key reference, and JSON-restored Anthropic alias history lost its thinking signature. The same tests pass after the fix, also checking that subsequent requests use the new endpoint/key/capacities and that actual model changes still remove incompatible signatures.
297
-
298
- `npm test` passed Host/Client type checks, rebuilt the distributed artifacts and passed **371 tests in 27 files**. `npm run check:dist` confirmed all **32** shipped files match source. The installed-package matrix passed all **nine** host generations from `0.1.5-rc.1` through `0.2.0-rc.2`; each now checks prepared endpoint/key/limit retention and signed Anthropic alias replay through a streamed tool call, JSON restoration and tool-result continuation.
299
-
300
- The original differential probe was rerun with the rebuilt tarball and real `0.2.0-rc.2` dependencies. Both native and plugin prepared requests stayed on endpoint A, the plugin paired endpoint A with fixture key A during a held catalog refresh, and both retained Anthropic thinking signatures. Existing reasoning defaults and forced output-cap behavior remained unchanged. All requests used isolated temporary caches, loopback gateways and dummy credentials; no paid inference or installed user profile was changed during this fix.
301
-
302
- ## Release 0.1.17 validation (2026-09-30)
303
-
304
- 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.
305
-
306
- ## Independent DSH provider route (2026-09-30)
307
-
308
- 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.
309
-
310
- 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.
311
-
312
- 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.
313
-
314
- 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.
315
-
316
- ## Issue #22: gzip metadata download on slow connections (2026-09-30)
317
-
318
- 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.
319
-
320
- 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.
321
-
322
- 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.
323
-
324
- 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.
325
-
326
- 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.
327
-
328
- ## Node 22 bundled-npm clean installs (2026-09-30)
329
-
330
- 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.
331
-
332
- 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.
333
-
334
- 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.
335
-
336
- ## Issue #24: DSH 0.2.0 and pi-ai transcript compatibility (2026-09-30)
337
-
338
- 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.
339
-
340
- 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.
341
-
342
- 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.
343
-
344
- 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.
345
-
346
- ## Release 0.1.16 integration (2026-09-29)
347
-
348
- 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.
349
-
350
- ## Issue #22: persistent model metadata (2026-09-29)
351
-
352
- 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.
353
-
354
- 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.
355
-
356
- 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.
357
-
358
- 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.
359
-
360
- 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.
361
-
362
- ## Settings page cards with the tuning fields last (2026-09-29)
363
-
364
- 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.
365
-
366
- 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`.
367
-
368
- `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.
369
-
370
- ## Git installation without plugin build approval (2026-09-28)
371
-
372
- 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.
373
-
374
- 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.
375
-
376
- 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.
377
-
378
- 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.
379
-
380
- ## Bounded parallel compatibility checks (2026-09-28)
381
-
382
- 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.
383
-
384
- 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.
385
-
386
- 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.
387
-
388
- ## Parallel Git-installation CI and cache placement (2026-09-28)
389
-
390
- 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.
391
-
392
- 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.
393
-
394
- 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.
395
-
396
- 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.
397
-
398
- ## Issue #19: isolate compatibility fixtures from plugin builds (2026-09-28)
399
-
400
- 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.
401
-
402
- `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.
403
-
404
- 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.
405
-
406
- 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.
407
-
408
- ## Optional request image count cap and usage panel polish (plugin 0.1.15, 2026-09-26)
409
-
410
- 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.
411
-
412
- On DSH 0.1.5, excess oldest occurrences become request-local text placeholders. On newer hosts, the adapter requests durable offloading through the Host's `IMAGE_OFFLOAD_REQUIRED` protocol; raising the limit later does not restore occurrences already marked offloaded. Compatibility fixtures exercise the count boundary, nested oldest image, combined count and byte budgets, live settings updates, clearing, and both legacy and modern Host paths.
413
-
414
- The usage panel also includes PR #17's opaque background and severity colors for high and rate-limited usage windows. Its component test checks that normal, high, and limited windows receive distinct bar styles.
415
-
416
- Validation: `npm test` passed the Host/Client type checks, production build, and **338 tests in 24 files**, including the published-Host compatibility matrix. The published UI dependencies still emit missing-source-map warnings. Tests use local HTTP and attachment fixtures; no live model request was made.
417
-
418
- ## Account usage panel and unsupported-history guards (plugin 0.1.14, 2026-09-26)
419
-
420
- The OpenCode Go usage panel again shows only the account's rolling, weekly, and monthly limits. Session cache statistics introduced in 0.1.13 have been removed, including their event subscription, component, styles, and translations; token statistics remain available in DSH's own interface. The original account polling, refresh failure handling, and retry behavior are retained. This supersedes the session-cache panel behavior described in the earlier verification entry below.
421
-
422
- PR #11 adds explicit `UNSUPPORTED_CONTENT` errors for developer-role history, tool-addition/tool-removal blocks, and tools with `deferLoading: true`, on both text-only and image conversion paths. Existing image-role validation and normal tool conversion are retained. These guards are preventive hardening for inputs the adapter cannot represent; no known production source for those inputs was reproduced. Dependencies and the minimum Host version are unchanged.
423
-
424
- Validation: `npm test` on the combined release changes passed both TypeScript checks, the production build, and **323 tests in 23 files**, including the existing Host compatibility and distributed client fixtures. Additional in-memory checks of PR #11 passed 15 scenarios on both conversion paths, covering tool-removal, empty history with deferred tools, ordinary tools, and `deferLoading: false`. The published UI dependencies still emit their existing missing-source-map warnings. These checks use local fixtures; no installed-profile update or live inference was performed.
425
-
426
- ## Issue #14: distinguish unavailable metadata from missing model configuration (2026-09-26)
427
-
428
- The existing source diagnostics now drive a specific Settings warning when model configuration fails while the gateway listing succeeds, including an empty successful listing. Unconfigured models show “Configuration unavailable” during a metadata-source failure and explain how to check connectivity from the machine running DSH. A retained model list keeps its warning and source details visible while retrying. Successful metadata recovery clears the warning; models still absent from usable metadata return to “Configuration missing”, and models with newly usable configuration become selectable.
429
-
430
- Two controller/component regressions failed before the UI changes and pass afterward. Four additional cases exercise the real Host RPC with cold-start HTTP 503, timeout, invalid JSON, and invalid configuration-document responses. They assert source-specific failures, continued availability of built-in models, and successful recovery of DeepSeek V4.1 Flash without restarting or changing credentials. Existing cached-data and legacy-response checks remain in place.
431
-
432
- `npm test` passed the Host/Client type checks, production build, and **326 tests in 24 files**, including the existing host compatibility and distributed client fixtures. The installed upstream UI packages still emit their existing missing-source-map warnings. This verifies local fixture failures and recovery, not the issue reporter's network environment.
433
-
434
- ## Minimum-host compatibility policy (2026-09-25)
435
-
436
- The compatibility policy is DSH `0.1.5-rc.1` and later, including alpha, rc, and stable releases, with continued maintenance as the host evolves. `engines.dsh` and all 19 DSH peers now use `>=0.1.5-rc.1`; the Cordis peer uses `>=4.0.2`. The eight existing host fixtures are verified versions, not an exhaustive allowlist. This supersedes the release-specific declaration limits recorded below.
437
-
438
- DSH's published bundle gate and the market compare host ranges with `includePrerelease: true`. Declaration tests now follow that policy, rejecting hosts below the minimum and accepting later prereleases and stable releases. These checks do not use npm's default peer-range semantics. The published `0.1.7-rc.2` gate also accepted 16 current/future version strings and rejected three below the minimum; future strings check admission policy only. `npm test -- tests/package-compatibility.spec.ts tests/host-compatibility.spec.ts` passed the Host/Client build checks and all **31 tests**, including activation and streaming across the eight existing hosts and Web/Headless bundle admission on rc.1 and rc.2.
439
-
440
- ## Issue #9: usage progress bar painting (plugin 0.1.12, 2026-09-24)
441
-
442
- The progress elements retained `appearance: auto`, preventing the custom WebKit fill from taking effect ([MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Selectors/::-webkit-progress-value)). The fix sets `appearance: none`, removes the native border, and gives the progress element the rounded track background. The WebKit track stays transparent to avoid applying the translucent track color twice; the existing WebKit and Firefox fill rules remain green. Percentage values and accessible progress semantics are unchanged.
443
-
444
- An isolated browser fixture mounted the actual `lib/client.js` usage slot with local usage data. On macOS / Chrome 153, the original artifact drew a thin native line with a faint fill rather than the intended 5px custom bar; this is not a reproduction of the reporter's completely gray Windows rendering. A screenshot pixel check failed before the fix (zero pixels of the intended fill color) and passed afterward, measuring 59, 101, and 84 filled pixels for 24%, 41%, and 34% of a 246px track, with the expected fill height.
445
-
446
- Browser checks also passed for light/dark themes, 0%, 50%, 100%, and a 120% usage reading clamped to a full bar, including a 320px viewport without horizontal overflow. The dialog opened and closed with Escape, and no browser errors were recorded. These checks used the production client artifact with fixture host services, not a full DSH installation; Windows, Firefox, and Safari were not exercised. The complete `npm test` run passed the Host/Client type checks, production build, and **295 tests in 22 files**.
447
-
448
- ## Issue #8: DSH 0.1.7-rc.1 / rc.2 bundle admission (plugin 0.1.12, 2026-09-24)
449
-
450
- The published `@deepseek-ai/dsh-app-boot@0.1.7-rc.1` reproduced the reported `skipping profile bundle` diagnostic with the 0.1.11 manifest: all 19 DSH peer ranges exclude rc.1, so `loadProfileDirectory` removes the bundle before the provider or settings can load. The engine declaration also excluded rc.1. The subsequently published [rc.2](https://github.com/deepseek-ai/deepseek-harness/releases/tag/dsh-v0.1.7-rc.2) still rejects a manifest that only declares through rc.1. Adding both exact versions to the engine and DSH peer ranges fixes admission without profile version exemptions; unverified rc.3 and 0.1.7 stable remain outside the declared range.
451
-
452
- `node --expose-internals tests/fixtures/bundle-compatibility.mjs v017-rc1` and the corresponding `v017-rc2` command failed before their manifest changes and pass afterward. They use the real startup gates against temporary Web and Headless profiles, asserting that the bundle and provider insertion survive. The rc.2 gate now returns skipped bundles for later reporting; the fixture also checks that this list is empty. Independently pinned rc.1 and rc.2 workspaces extend the existing built Host/Client tests for provider activation, catalog and usage RPC, streaming, image resizing and tool results, default/explicit reasoning, live profile settings, and client settings registration/rendering. The rc.2 UI primitives additionally require the host's new `dsh-util-code-language` module, which is supplied by the rc.2 test workspace.
453
-
454
- `npm test` passed Host/Client type checks, the production build, and **301 tests in 22 files**, covering all eight supported host fixtures on macOS / Node.js 24.14.1. The existing upstream UI source-map warnings remain. Tests use local gateways and fake credentials; they do not exercise a full running DSH Web/Desktop shell or make paid model requests.
455
-
456
- ## Individual model switches and cached Settings discovery (2026-09-23)
457
-
458
- `modelVisibility` stores explicit booleans by model ID. An absent override enables an ordinary model and disables a model marked deprecated. A `true` override can enable a deprecated model without a second global condition; `false` can hide an ordinary model. The Host and Client share `isModelEnabled`, including own-property checks for IDs that match JavaScript prototype keys. Older `showDeprecatedModels` and `visibleModelIds` fields remain loadable as unknown configuration but no longer impose a visibility condition.
459
-
460
- Each model has one switch that writes immediately and refreshes conversation pickers. Capacity and credential edits retain Save/Discard behavior. Hidden models stay available in Settings and existing conversations can continue requesting models the gateway serves. Newly discovered models follow their lifecycle default unless an override already exists for their ID.
461
-
462
- Picker reads now reuse the catalog for `refreshMinutes`, so visibility changes do not trigger another network refresh while that snapshot is cached. `GoModelsService.read()` still forces discovery, then notifies any registered picker on both successful and failed refreshes. The picker consumes the same committed or retained snapshot; explicit Settings refreshes discover new and removed models, and direct requests for unknown IDs retain their immediate discovery behavior.
463
-
464
- Settings discovery now returns `GoModelCatalog` with `models`, `stale`, and an optional safe error message. If a refresh fails after a successful listing, Settings keeps the cached entries and shows a warning with the failure cause. Client transport failures also retain the last successful view. A failure without any cached listing shows an error; a successful empty listing stays empty. Explicit generic discovery retains its strict failure behavior.
465
-
466
- The final `npm test` run passed Host/Client type checks, the production build, and **246 tests in 20 files**. Coverage includes lifecycle defaults, individual overrides, returning and newly discovered models, non-boolean configuration rejection, unchanged existing-model requests, TTL expiry, and real Settings RPC notifications without redundant picker requests, including failed refreshes. The 0.1.7 profile fixtures verify the new switches and retained legacy fields without remounting; client tests verify immediate switches and cached-list warnings. The earlier browser result below predates this UI revision and does not establish its visual behavior in a live DSH installation.
467
-
468
- ## Issue #7: transport verification, usage, diagnostics and model selection (2026-09-23)
469
-
470
- An isolated local HTTPS/HTTP2 reproduction used the reporter's exact Node 24.19.0 (bundled Undici 7.29.0) and npm Undici 8.11.0 global dispatcher. The server supplied correctly labeled compressed JSON. HTTP/1.1 worked; HTTP/2 lost all response headers and native `response.json()` failed on compressed bytes. The same failure occurred on Node 24.14.1 (bundled Undici 7.24.4). These tests ran on macOS, not WSL2, and used local fixtures without real credentials or paid requests.
471
-
472
- Undici 8's HTTP/2 path passes a header object through the legacy dispatcher bridge where the older fetch expects an alternating header array ([request.js](https://github.com/nodejs/undici/blob/v8.11.0/lib/core/request.js#L328), [dispatcher1-wrapper.js](https://github.com/nodejs/undici/blob/v8.11.0/lib/dispatcher/dispatcher1-wrapper.js#L24)). The 0.1.10 reader was verified to recover this payload, but the final implementation adopts the issue reporter's simpler recommendation: `/models`, models.dev and `/usage` explicitly request `accept-encoding: identity`. The manual Brotli/gzip/deflate guessing loop has been removed. The reader only bounds delivered bytes and parses JSON; normally labeled compressed responses remain Fetch's responsibility. Requests keep using the host's global fetch and dispatcher.
473
-
474
- The final identity implementation passed actual catalog and usage RPC reads through one unchanged Undici 8.11.0 global ProxyAgent and a CONNECT tunnel to a real TLS/HTTP2 server, under Node 24.19.0. The control request without identity failed; identity returned plain JSON and all three code paths succeeded. Public read-only identity requests also succeeded: `/models` returned HTTP 200, 3,309 bytes and 40 model IDs; models.dev returned HTTP 200, 4,884,187 bytes and valid metadata. Neither response was compressed. The tradeoff is the larger metadata transfer; identity does not fix the underlying loss of ETag headers. No observed endpoint required manually decoding compressed responses despite identity, so an artificial server ignoring the header is not used to justify that fallback.
475
-
476
- Usage uses the shared reader with a 1 MiB limit on delivered bytes; listing and metadata limits remain 1 MiB and 16 MiB. Real HTTP regressions model an upstream that honors identity and a host that loses encoding headers: removing identity breaks the actual catalog/usage read, while identity succeeds. Correctly labeled Fetch-decoded compression and oversized responses remain covered. Model discovery retains the failure cause and gives both discovery entry points a URL plus HTTP status, network error code, JSON decoding failure or invalid listing shape. Public messages omit response bodies; a successful refresh clears the previous failure.
477
-
478
- The initial picker-selection implementation used a saved allowlist and a separate deprecated-model option. It has been replaced by the individual switches described above.
479
-
480
- Before the individual-switch and cached-discovery revision, the identity implementation passed `npm test`: Host/Client type checks, the production build and **240 tests in 19 files**, including all six supported host fixtures. Obsolete manual-decompression tests were replaced by request-negotiation and bounded JSON parsing coverage. Browser verification at that checkpoint used the actual settings component and controller with a local in-memory settings scope: selecting one model, saving, restoring all, and discarding behaved correctly; the 390 px viewport had no horizontal overflow and no browser errors were reported. This preview was not a live DSH Web deployment. The existing upstream missing-source-map warnings remain.
481
-
482
- ## Unknown profile fields during activation (2026-09-23)
483
-
484
- A real Loader composition reproduced `TypeError: value.get is not a function` when the plugin entry contained an additional configuration field. Schemastery preserves fields outside the schema as plain values; the plugin incorrectly called `.get()` on every entry. Configuration reads now dereference only declared schema fields, preserving their live references and leaving stored profile data intact.
485
-
486
- The regression failed before the fix and passes afterward, covering extra scalar, null, array, and object values alongside a working endpoint and model-capacity override. The built artifact also mounts with an extra field across all six supported host fixtures; both 0.1.7 fixtures verify live settings changes still work without remounting and retain the extra profile field. The build's Host/Client type checks and all **234 tests in 18 files** pass on macOS / Node.js 24.14.1. Upstream UI primitives still emit missing-source-map warnings. No live model requests were made during those tests.
487
-
488
- The reported failure was subsequently reproduced by disabling and enabling the component in the user's running DSH Web process. A debugger breakpoint identified `showDeprecatedModels` as the failing field: its value was a boolean and its `get` property was undefined. The process's loaded plugin source matched the local `dsh-opencode-go-0.1.7.tgz` artifact byte for byte (SHA-256 `07422ad73df69896e2c3eafa1a07481cedd2945ed53dc59f09657abf1c8faa27`), including stack lines 1007 and 1305; that schema predates this field. The installed files were already 0.1.10 and recognized it. The process started at 18:30:41, before the replacement package directory was created at 18:31:45. Component toggles reimport the same cached module; the host's package manager requires a restart after replacing an installed dependency. Thus the actual trigger was a newer saved setting reaching an older module still loaded in memory, not a manually added invalid setting. A complete DSH restart loaded the existing 0.1.10 files with the same configuration: the component changed from failed to running, and the selected model and subscription usage became available. No package replacement or settings removal was needed, and no paid generation was sent.
489
-
490
- ## Default reasoning effort (plugin 0.1.10, 2026-09-23)
491
-
492
- PR #6 declares a default effort for the pi-ai transports that explicitly disable thinking when the effort is unset: `deepseek`, `zai`, `qwen`, and `qwen-chat-template`. Models offering `high` default to it; otherwise the highest supported non-`off` effort is selected. Explicit choices, including `off`, still take precedence. Other transports and models without adjustable efforts retain their existing behavior.
493
-
494
- Review found that the original fallback selected the first non-`off` effort from pi-ai's ascending list. End-to-end regressions failed with `low` in the outgoing request for models offering `low/medium` or `low/max`, then passed after selecting the last supported effort. The same cases cover `low` alone and the preference for `high` over `max` when both are offered.
495
-
496
- Host/Client type checks, the build, and all **233 tests in 18 files** pass on macOS / Node.js 24.14.1 using the existing installed dependencies. Permanent compatibility coverage adds 42 local HTTP requests through the built plugin and all six supported DSH runtimes, verifying default effort resolution, explicit `low` and `off`, Qwen's thinking switch, provider-decides models, and reasoning/text stream conversion. Published UI primitives still emit missing-source-map warnings; all assertions pass. Completion requests use local fixtures and test credentials, with no paid model calls or manual Web/Desktop verification.
497
-
498
- This release also includes PR #5's bounded compressed-response recovery described below, which was merged after the 0.1.9 release.
499
-
500
- ## Bounded model discovery responses (0.1.10; superseded by identity requests above)
501
-
502
- Model discovery accepts ordinary JSON and Brotli, gzip, or deflate JSON whose `Content-Encoding` header is missing. Correctly labeled responses continue through Fetch's automatic decoding. The shared reader counts actual streamed bytes and cancels oversized bodies; fallback decoders run asynchronously with `maxOutputLength`. Both delivered bytes and fallback output are limited to 1 MiB for the gateway listing and 16 MiB for models.dev metadata. These are payload limits, not total process-memory or CPU-time budgets. Failed recovery preserves the original JSON error and each decoder's cause.
503
-
504
- Host/Client type checks and the build passed, followed by all **222 tests in 18 files**, on macOS / Node.js 24.14.1 using the existing installed dependencies. Ten new resource-limit and diagnostic tests failed against the original parser before passing with the bounded reader. Real local HTTP tests cover both discovery sources, correctly labeled and unlabeled compression, corruption, cached fallback, and each source's limit exceeded by exactly one byte. The upstream UI primitives packages still emit missing-source-map warnings; all assertions pass.
505
-
506
- The missing-header failure is reproduced with controlled HTTP fixtures. A read-only direct check of the two public endpoints returned HTTP 200 with correct `Content-Encoding: br` headers and valid JSON after Node's automatic decoding; models.dev was approximately 4.6 MiB decoded. This does not establish why the original reporter's proxy environment failed: response headers, a raw response sample, and the relevant proxy/runtime configuration are still needed. Curl's automatic decoding also relies on `Content-Encoding`, so successful curl access alone does not establish the reported root cause. Usage fetching is outside this change.
507
-
508
- ## DSH 0.1.7-alpha.2 compatibility (plugin 0.1.9, 2026-09-23)
509
-
510
- The package now declares DSH `0.1.7-alpha.2` and Cordis `4.0.4` support while retaining the five previously supported hosts. Before the change, the SemVer regression rejected the new host in `engines.dsh`; the DSH and Cordis peer ranges also excluded its published versions. `engines.dsh` is declarative in the current upstream installer, so it must not be confused with an enforced loader check. The peer ranges affect dependency resolution. Unverified future prereleases remain outside the declared range.
511
-
512
- Review of the [upstream release diff](https://github.com/deepseek-ai/deepseek-harness/compare/dsh-v0.1.7-alpha.1...dsh-v0.1.7-alpha.2) and tests with the published packages found no required change to the adapter or settings implementation. The new `tests/hosts/v017-alpha2` workspace pins the DSH services, Cordis `4.0.4`, Loader `1.0.5`, Include `1.0.9`, and Schemastery `3.18.4` separately from the older host fixtures.
513
-
514
- Validation on macOS / Node.js 24.14.1:
515
-
516
- - Host/client type checks, build, and **191 tests in 17 files** pass. The six-host matrix covers artifact loading, Loader activation, catalog and usage RPCs on modern hosts, streaming, capacities, real image resizing, offload limits, and immutable history. Modern hosts also verify mixed tool-result text and image delivery.
517
- - Both `0.1.7` prereleases pass the real profile settings fixture, including live updates without remounting, visibility changes, capacity reset, route toggling, and validation.
518
- - The distributed browser factory renders against the legacy UI packages and both `0.1.7` prereleases' actual store and primitives packages. Model loading, settings registration, CSS composition, and cleanup pass. Published UI primitives still emit missing-source-map warnings.
519
- - The tarball installs into a separate official `@deepseek-ai/dsh@0.1.7-alpha.2` npm consumer without `--force` or `--legacy-peer-deps`. Its resolved Cordis, LLM, settings, and attachment packages match the target versions. `verify:installed` passes package resolution, catalog, streaming, session headers, authorization, and unload checks.
520
- - `verify:headless` passes through the official launcher with the installed bundle selected in an isolated profile. The first `dsh plugin add` stopped on pnpm's build-policy decisions for `@google/genai` and `protobufjs`. Both were explicitly disabled in that temporary profile's `allowBuilds`, then installation was retried. Because the interrupted operation had already written the dependency, the retry retained its disabled state; adding `dsh-opencode-go` to that profile's existing `dsh.profile.bundles` enabled it before the successful smoke test.
521
-
522
- Development dependencies are reproducible with `npm ci --legacy-peer-deps --ignore-scripts`. Completion requests use a loopback gateway and fake credentials. This verification does not cover the full Web/Desktop UI, other operating systems, or live paid model calls.
523
-
524
- ## Release 0.1.8 (2026-09-22)
525
-
526
- Host/client type checks, the build, and all **181 tests in 16 files** pass on macOS / Node.js 24.14.1. Coverage includes the five supported host versions, deprecated-model visibility, model remote injection, and CSS composition in the distributed client. The upstream UI primitives packages still emit missing-source-map warnings; all assertions pass. This release also synchronizes the English README with the simplified Chinese guide and updates installation examples to 0.1.8.
527
-
528
- ## Model settings layout and deprecated visibility (2026-09-22; superseded by individual switches above)
529
-
530
- The settings page uses the selected list/detail layout, real gateway membership, models.dev release dates and deprecation flags, and a default-off `showDeprecatedModels` switch. Deprecated models stay configurable in settings; only conversation picker membership is filtered. Existing conversations can still call a hidden model that the gateway serves. Models found only in metadata or saved overrides are not displayed. Before any successful gateway response, a network failure does not advertise built-in models.
531
-
532
- Host/client type checks, the build, and all **181 tests in 16 files** passed. Added checks cover lifecycle date boundaries and ordering, gateway removals, deprecation during metadata outages, real model RPC serialization, immediate visibility changes and picker notifications on legacy settings and the 0.1.7 profile service, and failed toggle writes without discarding other edits. All five published host targets pass the compatibility fixture. Model and usage endpoints share one package contribution so 0.1.7's registry retains both.
533
-
534
- The actual React settings component was also rendered in a separate local preview with public live API data. Preview settings remain in memory, and credentials are not read or changed. This is a component/browser check, not a manual five-version host UI matrix. No paid completion or publication was performed.
535
-
536
- ## Per-model capacities on five hosts (2026-09-22)
537
-
538
- PR #3's capacity editor is integrated on main commit `b093842`, preserving both legacy settings and the `0.1.7` profile configuration bridge. Each operation applies its captured overrides to original catalog metadata. The catalog cache remains available during outages, discovery shows the original reference values, and configured output caps also constrain explicit request budgets. Null model entries or fields explicitly restore catalog values over inherited configuration.
539
-
540
- The built Host artifact passes the existing compatibility fixture with real published LLM/attachment packages for all five targets:
541
-
542
- | DSH target | Capacities, output caps, hot update and reset | Text, image resizing and history checks |
543
- | --- | --- | --- |
544
- | 0.1.5-rc.1 | Pass | Pass |
545
- | 0.1.5-rc.2 | Pass | Pass |
546
- | 0.1.6-alpha.1 | Pass | Pass |
547
- | 0.1.6-alpha.2 | Pass | Pass |
548
- | 0.1.7-alpha.1 | Pass | Pass |
549
-
550
- The `0.1.7` fixture uses its actual Loader and settings service to edit and reset capacities without replacing the plugin fiber. Legacy settings tests verify null overrides of inherited capacities. Regressions also cover catalog references, metadata outages, concurrent configuration changes, and explicit output caps across Chat Completions, Responses and Anthropic Messages. Client tests cover Save/Discard, reset followed by another edit, customized counts, and offline reset; the distributed client mounts against both UI primitive generations.
551
-
552
- Validation: Host/Client type checks, all **175 tests in 15 files**, build and `npm pack` pass on macOS / Node.js 24.14.1. Dependencies install with `npm ci --legacy-peer-deps` using main's unchanged lock. The older host matrix selects matching LLM/attachment packages through resolution hooks, with other services from the development tree; the `0.1.7` host uses its separately pinned workspace. UI validation is automated component/factory testing, not a manual five-version browser or Desktop matrix. All completions use a loopback fixture and fake credentials; no paid generation or npm publication was performed.
553
-
554
- ## DSH 0.1.7 compatibility (plugin 0.1.7)
555
-
556
- Verified on macOS / Node.js 24.14.1 against published DSH `0.1.7-alpha.1`, while retaining the four previously tested hosts (`0.1.5-rc.1`, `0.1.5-rc.2`, `0.1.6-alpha.1`, `0.1.6-alpha.2`). The independent npm workspace at `tests/hosts/v017` keeps the new Cordis/Loader and DSH service identities separate from the legacy test dependencies. Install development dependencies with `npm ci --legacy-peer-deps`; the aliases deliberately contain incompatible peer ranges from different host generations.
557
-
558
- The failures reproduced before their fixes were:
559
-
560
- - `engines.dsh` rejected `0.1.7-alpha.1`.
561
- - The real new settings service did not expose OpenCode Go because `installSection` was removed and the profile form requires volatile Config fields.
562
- - New `role: 'tool'` history was sent as user text, losing its tool-call identity; tool-result images were rejected.
563
- - The real Web settings page crashed with React error 130 because the host removed `IconChevronDownOutline14`.
564
-
565
- Validation: Host/Client type checks and all **151 tests in 14 files** pass. The fresh-process host matrix exercises the built artifact, real LLM/attachment implementations, text streaming, tool history, image resizing and offload limits. The modern fixture additionally exercises the real usage RPC gateway. The profile-settings fixture uses the real Loader and settings service with an in-memory editor, and verifies live updates without remounting, toggling the route, and invalid-URL rejection. The browser artifact test now renders the actual page against both published UI primitive generations; merely checking registration had missed the removed icon.
566
-
567
- An isolated npm consumer with the official `0.1.7-alpha.1` CLI passed `verify:installed` and `verify:headless` against a local fixture gateway. The final browser factory was also checked in that consumer's Web profile: the settings page renders, advanced fields are editable, saving `refreshMinutes: 30` and switching `enabled: false` persist in the real profile patch and survive a full page reload, with no new console errors. No real API key or paid model generation was used. Older custom settings may require the explicit migration described in README because the upstream importer does not map this plugin's legacy namespace to its bundle entry id.
568
-
569
- The verification history below describes earlier releases; their live-provider results do not imply live-provider testing of this release.
570
-
571
- Verified on macOS with Node.js 24.14.1 and npm-installed DSH 0.1.6-alpha.1. The plugin's dependencies came from npm, with no workspace aliases, symlinks into a checkout, or unpublished DSH exports.
572
-
573
- ## Local checks
574
-
575
- - `npm run typecheck`: strict Host and Client programs against installed declarations.
576
- - `npm test`: builds both artifacts, then runs the adapter, catalog, configuration, settings, loader, image/history conversion, and browser-factory suites.
577
- - `npm pack`: builds a distributable tarball containing the bundle patch, Host ESM, browser module factory, type declarations, license, and documentation.
578
-
579
- The published UI primitives bundle references a missing source map. Vitest prints an upstream missing-map warning; it does not affect execution or assertions.
580
-
581
- ## Isolated installed checks
582
-
583
- Create an empty temporary consumer directory and install the compatible CLI plus the tarball there:
584
-
585
- ```sh
586
- npm init -y
587
- npm install --ignore-scripts @deepseek-ai/dsh@0.1.6-alpha.1 /absolute/path/to/dsh-opencode-go-0.1.0.tgz
588
- ```
589
-
590
- Set `DSH_HOME` to that consumer's `home` directory before invoking its CLI. Install the bundle into the headless profile:
591
-
592
- ```sh
593
- DSH_HOME="$PWD/home" ./node_modules/.bin/dsh plugin --profile headless add /absolute/path/to/dsh-opencode-go-0.1.0.tgz
594
- DSH_HOME="$PWD/home" ./node_modules/.bin/dsh --profile headless --dump-config
595
- ```
596
-
597
- The tested pnpm installation initially stopped for dependency build-policy decisions. In the temporary profile's `pnpm-workspace.yaml`, set `allowBuilds` entries for `@google/genai` and `protobufjs` to `false`, preserving other generated fields, then repeat installation. This keeps those scripts disabled. The second installation completed and added the bundle to the profile manifest.
598
-
599
- From this plugin project, run:
600
-
601
- ```sh
602
- npm run verify:installed -- /absolute/path/to/consumer
603
- npm run verify:headless -- /absolute/path/to/consumer
604
- ```
605
-
606
- The installed smoke resolves the tarball's module and its shared DSH services from the consumer, mounts them through the actual Cordis Loader without an import mock, queries the catalog, streams three requests, checks session-header stability and isolation, checks authorization and User-Agent, and disposes the adapter to verify route removal. The fixture gateway binds an OS-allocated loopback port and closes it after the test.
607
-
608
- The headless smoke invokes the consumer's official `dsh --profile headless` launcher with a temporary profile overlay and a local gateway. It selects the OpenCode Go provider, completes a task with `standalone-ok`, checks the outgoing session header, and removes the temporary overlay. The test has a bounded child-process timeout and waits for teardown.
609
-
610
- ## Browser verification
611
-
612
- The tarball was installed into a separate Web profile in the same temporary Harness home. The official `dsh web` server started on an OS-allocated port. Browser inspection confirmed the OpenCode Go settings entry, API-key field, advanced configuration, and a successful model-list response. Toggling the provider off and on updated the page through the Host settings service. No real API key was entered, and no real paid model completion was requested.
613
-
614
- The browser-factory test additionally evaluates the distributed `lib/client.js` against the published React/store/UI module table, checks the package ID, mounts the settings section, and verifies CSS insertion.
615
-
616
- ## Subscription usage verification (0.1.1)
617
-
618
- The usage tests invoke the actual published Typert Host gateway against a local HTTP fixture, checking the Bearer credential, credential changes, all three windows, and failure handling. Browser component tests check provider switching, polling cleanup, monthly details, and failed-refresh behavior. Adapter tests cover non-zero cached input in OpenAI, DeepSeek, and Kimi usage fields.
619
-
620
- The local Web profile was also checked against the real read-only OpenCode Go usage endpoint. The button renders immediately before the model selector, and its details show rolling, weekly, and monthly percentages and local reset times. This check sends no model-generation request. The manual Remote codec supports both the published DSH `schema` shape and the current source build's `create()` shape.
621
-
622
- ## Dynamic model catalog verification
623
-
624
- The dynamic catalog change passes all 140 tests plus the Host/Client build. Its offline regression fixtures cover Union Alpha and an arbitrary future id absent from pi-ai, immediate discovery and picker refresh, metadata arriving after a model id, HTTP ETag revalidation, concurrent refreshes, metadata outages, gateway outages, removal of retired models, malformed metadata, and supported reasoning controls. Real SDK streams against a local HTTP gateway verify Chat Completions, Responses, and Anthropic Messages paths for models with no built-in entry, including the Anthropic SDK's `/v1/messages` suffix.
625
-
626
- A read-only check against the live OpenCode Go listing and models.dev resolved 36 of 38 advertised ids, including `union-alpha`; `deepseek-flash` and `hy3-preview` had no metadata. Those ids remain visible with a configuration diagnostic. This check did not send a generation request or establish that every advertised id is currently callable for an account.
627
-
628
- ## Remaining limits
629
-
630
- Desktop, non-macOS platforms, and DSH releases outside the versions documented here are not verified. Live OpenCode Go verification is limited to the four image requests and four text-only code-generation requests documented below. The automated gateway tests preserve the adapter's request and replay semantics but cannot establish account validity or live provider availability.
631
-
632
- ## DSH 0.1.5 compatibility (plugin 0.1.5)
633
-
634
- Issue #1 reproduced as an ESM import failure before plugin activation: DSH 0.1.5 lacks `IMAGE_OFFLOAD_REQUIRED_CODE`, `requiredImageOffload`, and `projectOffloadedImages`. The regression test failed against both actual published `dsh-llm` 0.1.5 release candidates before the fix.
635
-
636
- `npm test` builds the distributed artifact and runs `tests/host-compatibility.spec.ts` in fresh Node processes against the published LLM packages `0.1.5-rc.1`, `0.1.5-rc.2`, and `0.1.6-alpha.1`. The two older versions are pinned npm aliases in development dependencies. A resolution hook selects the LLM package for both the plugin and Cordis Loader; it does not mock that package's exports. This focused matrix does not replace full CLI/profile checks.
637
-
638
- The original fixture verifies native ESM loading, Loader activation, model listing, streamed text, image transport with a mock attachment store, repeated-image byte accounting, nested oldest-image offloading, immutable history, and a second check against encoded image sizes. It did not validate the real attachment service's request policy (see Issue #2 below). All external model metadata is replaced by an offline fixture, and completions go to a loopback gateway with a fake credential.
639
-
640
- The compatibility bridge uses the host's own functions. On 0.1.5 it preserves the stock adapter's two-pass transient projection (estimated bytes before reading images, exact encoded bytes afterward). On 0.1.6 it preserves durable offload marks and the `IMAGE_OFFLOAD_REQUIRED` retry signal; it never silently substitutes legacy offloading on a modern host. The existing conversion suite additionally checks surface-marked images, path descriptions, and unsupported image roles.
641
-
642
- Validation: 146 tests passed, Host and Client type checks passed, and the package built successfully on Node.js 24.14.1.
643
-
644
- Full installed checks also passed against separate npm CLI installations whose entire DSH dependency closures were pinned to `0.1.5-rc.1` and `0.1.5-rc.2` respectively (not just the CLI version). The `0.1.5` plugin tarball was installed through `dsh plugin` into each installation's Web and Headless profiles under isolated `DSH_HOME` directories. Both versions passed `verify:installed` for package resolution, catalog, streaming, request headers, and unload; both passed `verify:headless` through the official launcher and loopback gateway. Browser checks confirmed the Web shell and OpenCode Go settings section load, including the API-key field, advanced settings, and enable switch, with no page errors.
645
-
646
- ## Image policy compatibility (plugin 0.1.6, Issue #2)
647
-
648
- The real published `dsh-attachment-local` 0.1.5-rc.2 reproduced `Image request maxPixels must be a positive integer.` with an 800×600 PNG before the fix. The adapter passed `{ width, height, maxBytes }`, but DSH 0.1.5 requires `{ maxPixels, maxBytes }`. DSH 0.1.6 requires explicit dimensions, so replacing dimensions with only the old policy would break the newer hosts. The adapter now supplies both the computed dimensions and the original pixel/byte budgets; each host consumes its own contract.
649
-
650
- The automated matrix now covers four matching sets of published `dsh-llm`, `dsh-attachment`, and `dsh-attachment-local` packages:
651
-
652
- | Host packages | Real image requests | Existing loading, text, and offload checks |
653
- | --- | --- | --- |
654
- | 0.1.5-rc.1 | Pass | Pass |
655
- | 0.1.5-rc.2 | Pass | Pass |
656
- | 0.1.6-alpha.1 | Pass | Pass |
657
- | 0.1.6-alpha.2 | Pass | Pass |
658
-
659
- `tests/fixtures/host-compatibility.mjs` saves actual PNGs in a temporary local attachment store, streams them through the built adapter to a loopback gateway, and decodes the transmitted bytes. It checks an unchanged 800×600 image, a 3000×2000 image downscaled under the default pixel budget, and a custom pixel/byte budget. Storage admission keeps the original dimensions so resizing is exercised in the request path. Temporary storage is removed after each run. The mock attachment checks remain for exact byte-bound and offload scenarios.
660
-
661
- Run `npm test -- tests/host-compatibility.spec.ts` for the focused matrix. The aliases intentionally install multiple host generations; use `npm ci --force` to retain the pinned development dependency tree despite their conflicting peer ranges. Resolution hooks select the matching LLM and attachment API at runtime. Other services use the development dependency tree, so this is not a full four-version CLI/Desktop or browser matrix, and no live paid completion is sent.
662
-
663
- Additional isolated-install checks passed on macOS/Node.js 24.14.1 for all four versions. The built tarball was npm-installed into four temporary consumers with all DSH packages in each dependency closure pinned to the target version (38, 38, 39, and 40 DSH packages respectively; no version mismatches). A copy of the fixture loaded the installed plugin and packages directly, without resolution hooks, and passed ESM loading, Cordis Loader activation, model listing, streamed text, real image resizing, custom budgets, and image-offload/history assertions. These checks exercise the installed dependency combinations, not the official CLI launcher, Desktop, or browser UI.
664
-
665
- Validation: 147 tests passed, Host/Client type checks and builds passed, and the focused four-version matrix passed again after adding custom-budget checks. Windows Desktop remains unverified.
666
-
667
- ## Live image verification (2026-09-21)
668
-
669
- With the user's authorization, the fixed tarball was tested against the real OpenCode Go endpoint `https://opencode.ai/zen/go/v1` using model `deepseek-v4.1-flash`. Each of the four isolated consumers above used its actual Cordis Loader, LLM service, local attachment service, and installed plugin, with no mocked metadata or gateway response. SHA-256 comparisons confirmed all four installed Host artifacts matched the current built fix.
670
-
671
- Each consumer sent exactly one completion request containing an 800×600 solid red PNG and a 3000×2000 solid blue PNG. Admission preserved the original dimensions, so the larger image exercised the adapter's request-size preparation. The prompt asked for the two dominant colors in order without naming them. Every outgoing completion contained both image parts, received HTTP 200, returned `red, blue`, and ended with `finish.kind: stop`.
672
-
673
- | DSH dependency version | Result | Elapsed time including setup/discovery | Reported input/output tokens |
674
- | --- | --- | --- | --- |
675
- | 0.1.5-rc.1 | Pass | 2.586 s | 1501 / 26 |
676
- | 0.1.5-rc.2 | Pass | 2.503 s | 1501 / 70 |
677
- | 0.1.6-alpha.1 | Pass | 2.993 s | 1501 / 128 |
678
- | 0.1.6-alpha.2 | Pass | 2.686 s | 1501 / 53 |
679
-
680
- Total: four live completion requests and 6,281 reported tokens (including reasoning output). The credential was supplied through hidden stdin, passed to child processes through their environment, and never saved in repository files or test logs. Temporary image storage was removed. These checks verify the real model/attachment request path on macOS; they do not exercise Windows Desktop's UI or establish compatibility for every other model.
681
-
682
- ## Live text-only code verification (2026-09-21)
683
-
684
- The same four isolated consumers and `deepseek-v4.1-flash` model were then tested with one ordinary text-only code-generation request each. No attachment service was mounted for these requests. The task was to implement a JavaScript `mergeIntervals(intervals)` function that handles unsorted closed intervals, merges overlaps/shared endpoints, and returns new arrays without mutating its input. Each outgoing completion contained zero image parts and used the actual installed Loader, LLM service, and plugin against the live gateway.
685
-
686
- All four requests returned HTTP 200 and `finish.kind: stop`. After inspecting each generated function, it was executed in a time-bounded VM context without process, filesystem, module-loading, or credential bindings. Eight cases covered empty input, overlaps, chains sharing endpoints, negative bounds, nested intervals, duplicate points, unsorted disjoint intervals, and distinct adjacent integer points. Inputs were frozen; every case checked the expected result, unchanged inputs, and independent output arrays.
687
-
688
- | DSH dependency version | Code checks | Elapsed time including setup/discovery | Reported input/output tokens |
689
- | --- | --- | --- | --- |
690
- | 0.1.5-rc.1 | 8/8 passed | 4.278 s | 133 / 599 |
691
- | 0.1.5-rc.2 | 8/8 passed | 4.487 s | 133 / 670 |
692
- | 0.1.6-alpha.1 | 8/8 passed | 3.882 s | 133 / 589 |
693
- | 0.1.6-alpha.2 | 8/8 passed | 4.759 s | 133 / 702 |
694
-
695
- Total: four additional live completion requests and 3,092 reported tokens, including reasoning output. No code corrections or retries were needed. This verifies ordinary text-only code generation and executable output; it does not exercise an agent's multi-turn tool-call workflow. Credential handling was identical to the live image checks above.
696
-
697
- The settings build was also loaded in the user's running local `0.1.7-alpha.1` Web profile. This exposed a missing `remote.opencodeGoModels` injection in the browser settings scope; both legacy and modern settings bindings now declare it, and the distributed-client regression checks exercise model loading under that injection requirement. After rebuilding and restarting the Host, the real settings page loaded 40 gateway models, including 3 recent releases and 8 deprecated entries, with the deprecated-model switch off. No completion request was sent during this check.
698
-
699
- Local Web layout checks also cover the settings panel's independent scroll body and reserved save/discard footer. The model panel now uses the settings container's available height and stacks at narrow container widths. At a 600×780 viewport, browser geometry confirmed no footer overlap or horizontal overflow and a visible action row. The client artifact test checks inherited disclosure styles because the CSS-module build previously omitted `composes` entries.
700
-
701
-
702
- ## Per-model switches in the installed Web profile (2026-09-23)
703
-
704
- The revised local package was installed into the user's Web profile and the source Harness restarted. Installed host and client JavaScript matched the local build byte for byte. In the authenticated page, settings displayed 40 gateway entries, including 8 deprecated entries whose switches were off by default. Disabling MiMo-V2.6-Pro removed it from both the live session model catalog and the actual conversation picker; enabling the deprecated MiniMax-M2.5 added it to both without pressing Save. The temporary overrides were then removed through the revision-fenced settings API, restoring normal-model visibility and deprecated-model defaults. No completion was submitted. Browser errors were empty, and the settings layout was visually checked at the user's 1077×1324 viewport.
705
-
706
- Timeout retention was verified deterministically through the real Host RPC and component/controller tests: success, timeout with retained entries and an explicit warning, then recovery. The installed live check does not establish that upstream network timeouts can no longer occur.
707
-
708
-
709
- ## Missing model configuration (2026-09-23)
710
-
711
- Settings now carries `configurationMissing` as structured model state. Models without usable Go configuration retain their raw names, show “配置缺失”, and have an unchecked, disabled switch even if an earlier visibility override is true. A real Host RPC regression covers missing configuration followed by metadata recovery, and the component regression covers the disabled switch and its recovery. The complete suite passed 248 tests in 20 files, along with the host and client type checks.
712
-
713
- After installing and restarting the user's Web profile, the actual `deepseek-flash` and `hy3-preview` rows both displayed “配置缺失”, `aria-checked=false`, and a disabled switch. Neither name contained the previous English diagnostic. No user settings or model generations were changed during this check.
714
-
715
- ## Intermittent network resets and usage recovery (2026-09-23)
716
-
717
- The subsequent `ECONNRESET` report is distinct from Issue #7's compressed JSON failure. Public `/models` probes with `accept-encoding: identity` reproduced resets before response headers, including in Node 24.14.1's unmodified built-in fetch without Harness or an Undici 8 dispatcher. Fifteen sequential native requests produced ten successes, one reset and four 10-second timeouts. A separate five-request probe through the configured local proxy with HTTP/2 disabled produced two successes, one reset and two timeouts. Successful responses were HTTP/1.1, HTTP 200 and 40 models. This rules out JSON parsing and HTTP/2 as necessary causes of this failure; it does not identify whether the local forwarding layer or the upstream path caused each reset. No proxy setting was changed and no completion was sent.
718
-
719
- Read-only JSON requests now retry connection reset/socket closure/temporary DNS errors at most once after 150 ms, including failures during body reading. Both attempts and the delay share the original timeout signal. HTTP failures, malformed or oversized JSON, and TLS certificate errors are not automatically retried. Loopback HTTP tests exercise a dropped first connection followed by recovery, persistent failures, body truncation, HTTP failures, and cancellation during backoff. The real catalog and usage RPC paths also exercise recovery.
720
-
721
- Usage failures now carry safe Host diagnostics through the Remote error contract. The UI retains a prior reading only when its opaque account/endpoint source matches the failed request, labels it as old data, and shows its original timestamp and a manual retry. Authentication failures and different/missing source identities clear old data. A source is a Host-generated random identifier, never a credential or credential hash. Polling and manual retries coalesce. These changes improve recovery and diagnostics; they cannot guarantee availability during a network outage.
722
-
723
- Validation: 289 tests in 22 files and both Host/Client type checks passed, including the published-host compatibility fixtures and the real usage RPC codec.
724
-
725
- ## Catalog failure recovery, cancellation, and source diagnostics (2026-09-25)
726
-
727
- The catalog previously cached an initial failed listing for the normal successful refresh lifetime (60 minutes by default), ignored caller cancellation during model discovery, and reported fresh Settings data when the gateway listing succeeded but models.dev failed. Five new regressions reproduced these behaviors before the fix: two retry/recovery cases, one metadata diagnostic case, and independent cancellation of model resolution and generation while a second caller shared the pending refresh.
728
-
729
- Failed catalog refreshes now use demand-driven backoff of 5, 10, 20, 40, then at most 60 seconds. Successful refreshes reset the failure count and restore `refreshMinutes`. Manual discovery bypasses backoff; existing immediate discovery for unknown models is preserved. Repeated failures retain usable models without resurrecting retired entries. No timer polls in the absence of catalog reads.
730
-
731
- Cancellation ends the individual model-resolution or generation wait before metadata completes. Shared HTTP reads retain their own bounded timeout so other callers still complete and populate the cache. A generation cancelled before discovery starts sends no catalog or inference request. The regression holds the metadata response pending, observes cancellation, then releases it and verifies that the other caller succeeds using the same single listing fetch.
732
-
733
- Settings RPC responses now optionally carry separate listing/configuration diagnostics and timestamps of their last successful checks, including 304 revalidation. A metadata-only failure marks the catalog stale, retains usable configuration, and shows the failed source and its previous timestamp. The controller/component integration test verifies the warning, enabled model switches, clearing source status after a failed Host RPC, and recovery. Older responses without source fields remain accepted. Malformed source timestamps are rejected by the codec.
734
-
735
- Validation: `npm test` rebuilt the Host and Client (including both TypeScript checks) and passed **309 tests in 22 files**. The existing missing-source-map warnings from the published Harness UI dependencies remain. `git diff --check` passed. These checks use local fixtures and mock HTTP gateways; no paid inference, installed-profile update, or package publication was performed.
736
-
737
- ## Background catalog refresh, session cache diagnostics, and session identity (2026-09-26)
738
-
739
- Known, verified models can now use the cached configuration for five minutes after its normal lifetime while one shared refresh runs. A held metadata response regression confirms that generation lookup returns the known configuration before the response arrives, concurrent lookups coalesce, and manual/unknown-model reads still wait. Once the refresh completes, retired models are no longer routed from the old snapshot. Another regression checks that failed attempts do not extend the grace window. The model listing retains its 10-second deadline; metadata downloads have a separate 30-second deadline. The nonblocking lookup and independent-deadline regressions failed before the change and passed afterward.
740
-
741
- The usage panel derives session cache statistics from the Host's existing loaded event window, with no new RPC or persistent counters. Cached input share is `cacheReadTokens / (inputTokens + cacheReadTokens + cacheWriteTokens)`; output tokens do not contribute. Durable Go responses are counted once by sequence. Transient chunks, attempts without a settled message, other providers, malformed usage, and inherited fork responses are excluded. Missing attribution/accounting and incomplete history are indicated. A zero denominator shows no ratio. Statistics can be reconstructed from restored history and remain available when account-usage reads fail. Hosts without the optional event feed retain the existing account panel.
742
-
743
- The derived store preserves its snapshot for transient-only updates, recomputes on settlement/history replacement, and subscribes only while the details panel is open. Unit/component tests cover token weighting, cache writes, omitted cache counts, malformed and missing usage, duplicate events, fork boundaries, serialized restoration, history replacement, account failure, switching sessions, and subscription cleanup. Session statistics describe the loaded, settled responses, not all billable gateway attempts or an account-wide cache metric.
744
-
745
- 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.
746
-
747
- 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.
748
-
749
- ## Model area split, whole-row hover, and the model card's details (2026-09-28)
750
-
751
- 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.
752
-
753
- 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.
754
-
755
- 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.
756
-
757
- 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.
758
-
759
- 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.
760
-
761
- ## Multi-account hardening: add/remove transactions, fallback notice, rejected-key cache (2026-10-05)
762
-
763
- An adversarial review of the multi-account chain (dd0e4b4 plus the fold/reorder branch) confirmed the storage, fallback, and credential-isolation design, and found four client-side defects plus two host-side gaps. Every defect was reproduced by a regression written before its fix.
764
-
765
- Adding the first account while the page's credential describe had not answered — or kept failing — materialized the legacy placeholder row into the stored accounts and left the new account unselected. The add now asks the credential service directly for any reference the loaded rows do not know: the legacy placeholder is never written (it stays a read-side view while the selection still names the legacy reference), and the new account becomes preferred only when every listed account is known to hold no key; an unanswered describe keeps the selection conservative.
766
-
767
- Removing an account deleted its settings entry before its stored key, so any unset failure stranded an orphan plaintext credential with no retry path. The remove now unsets first (after a describe confirms the reference is configured) and keeps the row on refusal behind a new "key could not be deleted" notice; a refused settings write leaves a keyless, visible, retryable row instead of a dangling secret. An add whose revision was moved by a concurrent writer re-reads and re-applies the same account (same credential reference, up to three attempts) instead of stranding the key it just stored. A row editor whose row left the list (remove accepted, key cleanup failed) used to hide its own Cancel together with the Add button; the card now drops the stranded editor as soon as its row disappears.
768
-
769
- On the Host side, the fallback notice's fromRef was pinned to the preferred reference rather than the first account that actually failed (visible with three or more accounts); it now names the real source, or the first reference skipped for a remembered rejection. A gateway-rejected key (401/invalid-key) is remembered for five minutes and skipped by later requests instead of re-sending a full transcript to a dead key; a locally missing credential is not remembered (it is re-checked for free), an all-rejected list still attempts in order so the gateway's own error surfaces, and any `credentials/reference-updated` event clears the entry. The collapsed usage pill now carries a fallback mark (and a bare `↳` marker under the 460px container tier) whenever a switch notice is active; a failed describe marks rows "Status unknown" instead of an eternal loading state; renaming an account no longer refetches every account's quota.
770
-
771
- Validation: `npm test` rebuilt both artifacts and passed **450 tests in 30 files** with both TypeScript checks, including regressions for each fix: the placeholder race with and without a pending describe, the conservative unknown state, the conflict retry reusing one credential, key-first removal and its retry, stranded-editor recovery, multi-hop notice attribution, rejected-key skipping and credential-change clearing, and the collapsed-pill mark. These checks use local fixtures and mock gateways; no paid inference, installed-profile update, or package publication was performed. Known limits carried forward: concurrent settings writes from two Host processes can still lose updates inside the host watcher's observation window (a dsh-settings-level boundary), and the anthropic-messages error-body classification remains dormant until such models appear in the catalog.
772
-
773
- ## Accounts card: reorder-only rows, one refresh stamp, a wide quota bar (2026-10-05)
774
-
775
- Three pieces of the card read as noise or as too small, and a rendered preset comparison (`mockups/row-quota/overview.png`, `presets.png`) settled the shape before any code moved. The two hint paragraphs under the card header are gone, and the per-row "Move up" / "Move down" buttons went with them: the drag handle already reorders the list. The deleted paragraph was the only place that explained the drag, so the handle now carries that sentence as its `title` as well as its accessible name, while ↑ / ↓ keeps the keyboard path; a regression test pins the tooltip.
776
-
777
- The refresh stamp left every row's detail and moved into the card header. One pass reads every account, so the card was repeating N identical lines; it now renders one stamp, visible while the card is folded, showing the clock time on the day it happened and the date otherwise, with the complete local timestamp in its `title`. The detail keeps the weekly and monthly windows and the three row actions.
778
-
779
- The rolling reading is now a 12px bar that fills the row's free width (about 330px where the old one was a fixed 132px) with quarter marks at 25 / 50 / 75%, its percentage moved to the bar's right in the card's own 12.5px semibold tabular figure. Colour follows the composer pill's rule — green below 80%, amber from 80%, red at 100% or rate-limited — through one `barLevel()` helper, and the opened windows keep the native `<progress>` element while gaining those tiers through `accent-color`, so a collapsed row and its own detail can never disagree. The bar keeps `role="progressbar"` with `aria-valuenow`, so replacing the native meter cost the row no semantics; row height is unchanged, because the taller bar sits inside the row's existing padding.
780
-
781
- The row's summary line was content-driven: the name and key-state fields took their intrinsic widths, so two accounts whose names differ in length put their key state, their bar and their reading at different x positions. Measured in headless Edge at the settings column's 858px card width, the key-state column and the bar start moved by **112.1px** across four names, and the bar itself ranged from 181.3 to 237.3px. `Section.module.css` now lays the line out as a grid of fixed columns — 16px handle, 112px name, 92px key state, the bar's `minmax(0, 1fr)` track, 38px reading, 106px countdown, 14px disclosure — so only the bar absorbs slack. The flexible spacer is gone, the name column ellipsises with the full value in its `title`, and a row with nothing to read yet spans the bar, the reading and the countdown with one message instead of adding a column. The same measurement over the same four names now reports a **0px spread** for the key state, the bar, the reading, the countdown and the disclosure, with an identical 372px bar in every row.
782
-
783
- Validation: `npm run compile` regenerated both artifacts and `npm run test:ci` passed with a byte-identical `check:dist` — **455 tests in 30 files** — with `tsc -p tsconfig.host.json` and `tsc -p tsconfig.client.json` clean. Five new client regressions cover the row's fixed columns (seven children in order, the bar's role there, the ellipsised name's `title`, and the same grid for a row with nothing to read yet), the single header stamp surviving both the unfolded card and every row opened, the row's actions being exactly rename / replace-key / remove with no move control left, the bar's `aria-valuenow` plus its three quarter marks and fill width, and the 79 / 80 / rate-limited level boundaries. One unrelated timing test (`tests/metadata-transport.spec.ts`, a bandwidth-limited catalog deadline) flaked once under full-suite load, then passed alone and in the repeat run.
784
-
785
- The pull request's own CI then failed the installed-host matrix, and the failure was a client-side injection defect introduced by the first fix for the rows' quota reads. Naming `remote.opencodeGoUsage` in both settings injections is inert on a real host — the two lists are generation-exclusive, so only the applicable one fires — but `tests/fixtures/client-compatibility.mjs` fires an inject callback when `configForms`, `settingsScope` or `remote.opencodeGoUsage` appears in the service list, so both lists matched and the section mounted twice: all nine hosts failed `client-compatibility.mjs` with `1 !== 0` at the `bindScope` / `getForm` call-count assertion. The defect reproduced locally before the fix (`DSH_COMPAT_CONCURRENCY=4 npm run test:compat`: `AssertionError … at client-compatibility.mjs:110`, every host failing) and the same run passes after it (`PASS: installed-package compatibility across 9 host(s)`, 283.3s). The rows now leave upstream's two injection lists untouched and wait on the promise that publishes the namespace — `$mount(goRemote)`, the guard the catalog read already used — before reading a quota; that is the same context and the same call upstream's own default `readUsage` makes, with only the wait added.
786
-
787
- The rows' quota reads then had to be re-plumbed, and both rejected shapes are worth recording because each was wrong in a different direction. A read needs a context that injected the plugin's own usage namespace: the shipped client answers `cannot get property "remote.opencodeGoUsage" without inject` when one is missing, and that is exactly what a desktop install showed — both rows reporting that sentence behind a retry button — after the namespace was dropped from the settings injections and the read merely waited on the mount. Naming it in both settings injections instead mounts the page twice wherever a host carries both service sets, which is what the installed-host matrix rejected. Waiting on `$mount(goRemote)` alone publishes the namespace but grants no context the right to open it, so it reproduces the runtime error rather than the double mount.
788
-
789
- The shape that satisfies both is one injection of its own, the way the pill declares the namespace it reads: `mountSettings` captures a bound reader from `ctx.inject(['remote.opencodeGoUsage'], ready => …)`, the two generation-specific settings injections keep exactly the services upstream names, and a session that never injects the namespace reports "OpenCode Go account usage is unavailable in this session" on the row instead of reaching for a context that cannot open it. The fixture fires that callback too, so its proxy assertion — `usage service must be injected` — holds by construction, and the rows keep reading through the same namespace and the same call the pill uses.
790
-
791
- Final-shape validation: `npm run test:ci` passes **455 tests in 30 files** with a byte-identical `check:dist`, and `DSH_COMPAT_CONCURRENCY=4 npm run test:compat` reports `PASS: installed-package compatibility across 9 host(s)` in 292.5s — the same command failed all nine hosts on the double-mounting revision and all nine again on the mount-waiting one.
792
-
793
- ## Usage pill panel: model-selector card, explicit account choice (2026-10-05)
794
-
795
- The panel behind the composer's usage pill was rebuilt in the shape of the host's own model selector — a 320px card of 42px rows on a raised tile — after a rendered four-way comparison (`mockups/usage-pill/option-a…d.png`) picked the drill-down. The account now sits on a card row (state dot, name, `Preferred`, `Switch ›`), and `Switch ›` swaps the card's body for the account list, whose rows tick the current account and answer ↑ / ↓ or a click; Escape steps back to the reading instead of closing the panel, and a refused switch keeps the list open with the failure notice.
796
-
797
- The switch view says what it does: "Later requests in this profile use this account. The consumption order stays on the Settings page." This list is the user's own choice of which account spends now; the ordered fallback belongs to the Settings card, which is why the panel offers no order affordance.
798
-
799
- The modal hint line ("Account usage · used percentage · refreshes every minute") and its two copy keys are gone, and the native `<select>` went with them: an OS-rendered option list cannot be styled to match the host menu, which is what made the old picker look foreign. The three windows became one row each — label, 10px bar, reading — with the reset stamp under it and the refresh stamp in the card's foot. The bar stays the native `<progress>` element with its `high` / `limited` classes, so the colour tiers, their roles, and every existing bar assertion survive the redesign.
800
-
801
- Validation: `npm run test:ci` passes **459 tests in 30 files** — four new cases cover the card row and the departed picker, a successful drill-down switch, a refused one, and the arrow / Escape keys — with a byte-identical `check:dist` and both TypeScript projects clean. The installed-host fixture was A/B'd against the pre-change bundle in the same warm consumer: 1.47s before, 1.52s after. Local `test:compat` runs before that measurement tripped the script's 30s per-fixture ceiling on cold Windows file scans (the same fixture runs in 1.5s warm), so the ubuntu CI job is the gate that settles that matrix.
802
-
803
- ## Accounts: the reorder gesture lands, and a fallback adopts the account that served it (2026-10-07)
804
-
805
- Dragging an account row never reordered the list, and the card header kept naming the old account. The gesture was never the problem: in real Blink a drag starts from the handle, `dragstart` reaches the row with the handle as its target, and `dragover` accepts the drop. What went missing was the drop itself — the insertion line was a grid item of its own, so showing it pushed every row below it down by 12px and moved the pointer's own target out from under it; a release over the vacated line or the 10px gap hit-tested to elements with no handler, and the browser answers that by dispatching no `drop` at all. The mark now lives inside the row it belongs to (`.accountRow[data-drop]::after`, absolutely positioned, `pointer-events: none`), the drop handlers moved from each row to the list itself so the gaps and the mark are inside the drop surface, and the slot comes from `slotAt()` reading the rows' own midlines, which no longer move during the gesture. A drop that lands where the row already sits is a no-op instead of a refused write, and the release position — not the last hovered slot — decides where the row lands.
806
-
807
- The automatic fallback then stopped being a per-request detour. When an account starts producing output after a fallback, the Host makes it the current account — `settings.mutate('opencode-go', [{ op: 'set', path: ['apiKeyEnv'] }], revision)` on 0.1.7 profile entries, the `llm-opencode-go` section on older Hosts — so the Settings page and the usage pill follow it, and later requests start from the account that works instead of walking into the same wall again. A manual switch outranks the adoption (the guard compares the stored value with the account the request began with, and the revision fence settles the race), a refusal — read-only Host, lost revision, no settings service, a different entry id — leaves the stored value alone and never disturbs the request, and the fallback notice survives the adoption's own settings write. The list order stays the user's fallback order; only `apiKeyEnv` moves. Copy follows the same split: 当前账号 / `Current` for the account in use, and the switch hint now reads 额度耗尽或 Key 不可用时,在尚未输出内容或工具调用前,按设置页的账号顺序尝试其他账号;一旦某个账号开始正常输出,当前账号即切换为它。
808
-
809
- Validation: `npm run test:ci` passes **514 tests in 33 files** with a byte-identical `check:dist`, both TypeScript projects clean, and 19 new cases — 4 card-level drag cases, 5 adapter-level adoption cases, and 10 Host-wiring cases including a real `FileSettingsProvider` write and the three regression shapes an adversarial pass produced. The reorder fix was reproduced and re-verified in real headless Chromium over CDP against the bundled component: before the fix a release in the first row's top band produced `dragenter`/`dragover` and then no `drop`, with the line shifting the rows 12px; after it every scenario delivers the drop, marks sit in the gap centres, and no row moves while a mark is shown (0.00px across a three-account matrix, including the handle-column approach that had failed 5/5 before the `dragenter` acceptance was added). The adoption path was verified against the installed 0.2.x `SettingsForms` extracted from the app bundle (the volatile field accepted, `settings/document-updated` emitted, a stale revision refused, `accounts` untouched) and by a jsdom check that a published `apiKeyEnv` remounts the pill onto the new account. An adversarial pass found three counterexamples — a multi-hop no-op, legacy-row precedence, and a manual switch back inside the key-rejection window — all fixed, each guarded by a regression test that fails when its fix is reverted; the one test that did not discriminate was rewritten until it did.
810
-
811
-
812
- ## Account selection: one decision for notices and persistence (2026-10-08)
813
-
814
- The Host now consumes one `onAccountSettled` event for both the fallback notice and the saved account. `GoAccountSelection` owns that decision. Each request captures its sequence and account-state generation before asynchronous discovery, including prepared calls. The generation includes the settings revision, account configuration, settings-service connection and credential updates, so returning from A to C to A still invalidates an older request. The existing `onAccountSwitch` observer remains available to direct adapter consumers.
815
-
816
- A stale outcome cannot restore a deleted account, undo a manual selection, or override a newer accepted request. Saving still uses the Host's revision-checked settings operation and never blocks streamed output. The notice is restored after a successful adoption only when the committed revision and account identity match that write; a failed adoption leaves no pending marker for a later manual edit to inherit.
817
-
818
- Validation: `npm run compile` and `npm run test:ci` pass **537 tests in 34 files**, with **41 shipped build files** matching their source and both TypeScript projects clean. Seven additional real-`FileSettingsProvider` cases cover account removal, disabling fallback, A→C→A selection, newer-request precedence, credential replacement, delayed prepared-call dispatch, and refused-write notice cleanup. Three adapter cases cover generation capture before discovery and sequence ordering across prepared calls and model resolution. The race tests use explicit Promise gates rather than timing delays.
819
-
820
- Installed-package compatibility also passes on **0.1.5-rc.1, 0.1.7-rc.2 and 0.2.0-rc.2**. The profile fixture now drives a real fallback through SettingsForms, verifies the saved selection without changing account order or remounting the plugin, and repeats describe/usage reads to check that the adoption notice survives.
821
-
822
- The freshness check applies before starting a settings mutation. A mutation already accepted by the Host remains subject to its persistence and revision semantics; the plugin does not cancel or roll back an in-progress Host write.
823
-
824
- ## Architectural hardening: failure domains, bounded preparation and atomic settings (2026-10-09)
825
-
826
- Gateway HTTP evidence now drives failure classification before SDK message fallbacks. A 401 `ModelError` returns `UNKNOWN_MODEL` without account switching or rejected-key caching; 401 subscription limits switch with reason `quota` while retaining the key for later requests. Only explicit invalid credentials enter the rejection cache. Captured HTTP status also prevents a number such as 401 inside a 500 response message from masquerading as authentication failure.
827
-
828
- Modern image count overflow returns the host offload request before attachment reads. Four shared permits bound native preparation across simultaneous calls, and an occurrence-weighted base64 counter checks the payload as each unique attachment completes. The normal path updates this counter in constant time; only overflow invokes the host's prefix calculation. A byte overflow aborts queued work. Cancellation keeps a native operation's permit until that operation actually settles. Legacy hosts retain their existing transient image projection semantics.
829
-
830
- New positive integer settings `requestPreparationTimeoutMs` (60000 by default) and `requestTimeoutMs` (1800000 by default) bound discovery, credential/image preparation and whole dispatch, including account fallback. Caller cancellation ends credential/image/iterator/diagnostic waits even when their underlying service ignores its signal; SDK teardown no longer blocks cancellation. Caller aborts consistently produce an `ABORTED` finish, and deadline failures use `TIMEOUT`. Shared catalog refreshes remain independent of any single waiter's cancellation.
831
-
832
- The advanced form commits all ordinary set/unset operations through one revision-checked `scope.mutate`. A rejected batch writes no credentials and retains drafts. Accepted settings clear separately from failed credential drafts, so retry does not repeat successful operations. New edits typed during an in-flight save remain staged. Credentials keep their original account binding when another surface switches the selected account after a failed settings batch.
833
-
834
- Nonempty listings without valid IDs preserve the served catalog and report discovery failure; explicit empty listings still clear it. Cold listing failure reports `DISCOVERY_FAILED` rather than claiming a model is unknown. Closed SDK stream events are checked with a `never` assignment and rejected explicitly at runtime, and unknown host roles are refused. A safe `onCallTrace` summary supplies call ID, stage timing, attempt count and final outcome/code, including HTTP 200 stream truncation; observer failures do not affect requests.
835
-
836
- Validation on macOS with Node 24.14.1:
837
-
838
- - `npm run compile` and `npm run test:ci`: **590 tests in 37 files**, **47 shipped files** matching source, both TypeScript projects clean. The changes add 34 regression cases to the 556-test baseline.
839
- - `DSH_COMPAT_CONCURRENCY=4 npm run test:compat`: **all ten installed hosts pass**, including 0.1.5-rc.1/rc.2, 0.1.6-alpha.1/alpha.2, 0.1.7-alpha.1/alpha.2/rc.1/rc.2, 0.2.0-rc.2 and 0.2.1-alpha.1. The final artifact run completed in 73.4 seconds including cleanup.
840
- - `npm run test:install`: npm Git, pnpm Git and pnpm packed npm installations, ESM loading and public consumer types all pass without source builds or build approvals; 36.1 seconds including cleanup.
841
- - After isolating the short preparation-deadline cases from cold discovery timing, the adapter suite passes all 33 cases. `git diff --check` is clean.
842
-
843
- The tests use loopback gateways, fake credentials and fixture attachments. Settings and credentials still lack a cross-store crash transaction or durable recovery journal. Mixed valid/invalid listing rows still use the valid IDs; public disk metadata remains insufficient to establish gateway membership after restart. Underlying native work that permanently ignores cancellation can retain its image permits until host/service recovery. These boundaries are documented in `docs/development.md`.
844
-
845
- ## Architectural hardening, second pass: admission, attempt evidence and account recovery (2026-10-10)
846
-
847
- Untyped quota text is now limited to HTTP 401/402/403/429. A 500 diagnostic quoting an earlier credits failure remains `SERVER` and does not switch accounts; 504 remains `TIMEOUT`. Known structured quota types retain precedence, including when an upstream uses an unexpected status. Regression cases cover the classifier and the actual account fallback path.
848
-
849
- Image preparation now admits four active tasks and at most 32 queued tasks within the plugin module. A shared 128 MiB logical budget charges prepared raw data and occurrence-weighted base64 bytes; the adapter holds each lease through streaming and releases it on failure or consumer stop. Admission failure reports `IMAGE_RESOURCE_BUSY`. Tests exercise queue saturation, cancellation, shared reservations and a paused real adapter stream, including resource release and diagnostics. The budget excludes native decoder RSS, SDK/JSON copies and history; native work still has to cooperate with cancellation or settle before its permit becomes reusable.
850
-
851
- Mixed model-listing rows retain valid IDs while exposing a bounded warning through the RPC codec, settings UI and Host logger. Diagnostics include counts and at most eight row positions, without copying malformed data. A clean listing clears the warning. Each dispatch now has bounded attempt details for failures and the serving account: stages, HTTP status, redacted upstream request ID, first nonempty output latency, error code and image pool occupancy/waiting. HTTP evidence files carry matching call and attempt IDs. Tests cover a quota fallback followed by success, HTTP 200 truncation, credential echoes, evidence correlation and consumer stop.
852
-
853
- Account operations first confirm a durable, secret-free intention in their own section/profile before touching credentials. Host reconciliation completes stored-key additions and interrupted removals, preserves later account selections, and uses revision guards for metadata and intention cleanup. An addition without a stored key stays visible for replacement or removal. Recovery retries on settings/credential reconnection and updates, bounds each operation to 30 seconds, and cancels obsolete connection waits. Only exact generated ID/reference pairs authorize credential deletion; external references retain their keys. This adds recoverable eventual consistency, without claiming a cross-store transaction or isolation from arbitrary simultaneous profile edits.
854
-
855
- Validation on macOS with Node 24.14.1:
856
-
857
- - `npm run compile` and `npm run test:ci`: **622 tests in 39 files**, **49 shipped files** matching source, both TypeScript projects clean. This pass adds 32 cases beyond the prior 590-test suite.
858
- - `DSH_COMPAT_CONCURRENCY=4 npm run test:compat`: **all ten installed hosts pass**, **71.4 seconds** including cleanup. The four 0.1.5/0.1.6 fixtures now recover additions and removals with real settings and managed credential files. All six 0.1.7/0.2 fixtures exercise the same operations through real profile SettingsForms.
859
- - `npm run test:install`: npm Git, pnpm Git and packed npm installs, ESM loading and public consumer types pass without source builds or build approvals; **31.9 seconds** including cleanup.
860
- - `git diff --check` is clean. Tests use temporary homes, fixture credentials and loopback gateways; no paid inference or user profile is changed.
861
-
862
- The deliberate costs are bounded overload rejection, partial listings that may omit malformed models, and eventual rather than atomic account recovery. A key never committed cannot be reconstructed, old orphan keys predating the journal cannot be enumerated through the credential seam, and manually sharing generated references across profiles shares their deletion lifecycle. Cold offline membership, forced native-task termination and newer host content capabilities remain deferred; HTTP 200 in-stream errors still use the SDK/stream mapper's fallback classification.
863
-
864
- ## Google Generative AI and pi-ai 1.2.0 (2026-10-14)
865
-
866
- The online `gemini-4-argon-contributor` record already declared usable capacities and `@ai-sdk/google`; the plugin rejected its protocol and displayed the generic configuration-missing state. Metadata now maps that package to `google-generative-ai`, and the provider registers a wrapper around pi-ai's native Google adapter. The private SDK alias upgrades from 1.1.0 to the latest published stable 1.2.0, with pi-telemetry upgraded alongside it. A comparison of the published SDKs found one added Go built-in, `step-5-preview-free`, and unchanged Google, Chat Completions and Responses implementations. The upstream release also adds personalized OpenRouter catalogs/provider refresh base URLs, fixes Mistral stream timeouts and adds Anthropic preferred strict-tool fallback.
867
-
868
- The Google adapter still rejects custom fetch, so its native stream runs with a request-scoped fetch binding. One guarded build-time substitution in Google SDK 2.21.0 retains the plugin's diagnostic/proxy transport without changing global fetch or the SDK's serializers, stream parser, cancellation and retry code. Caller User-Agent replaces Google's prepended SDK identity. The same bridge runs in Vitest and esbuild and refuses an unreviewed Google version or changed HTTP call. Gemini 4 postdates pi-ai 1.2.0, so the wrapper selects metadata-declared discrete thinking levels instead of the SDK's legacy budget fallback. Default removes nested `thinkingConfig` before serialization; Low/Medium/High produce native levels.
869
-
870
- Validation on macOS with Node 24.14.1:
871
-
872
- - `npm run compile` and `npm run test:ci`: **669 tests in 42 files**, **62 shipped build files** matching source, with both TypeScript projects clean. Eight new Google cases cover catalog availability, Default/Low/Medium/High wire payloads, quota classification, cancellation and concurrent independent fetch bindings. Both authenticated HTTP and SOCKS5 proxy cases now also exercise Google streaming.
873
- - `DSH_COMPAT_CONCURRENCY=4 npm run test:compat`: **all ten installed hosts pass**, **34.3 seconds** including cleanup. Every host runs all four protocols through prompt/tool declarations, streamed tool calls, JSON-restored replay, continuation and one-shot requests. Google tool continuation preserves the original call ID and thought signature.
874
- - `npm run test:install`: npm Git, pnpm Git and packed npm installations, ESM loading and public consumer types pass without source builds or build approvals; **111.5 seconds** including cleanup. Consumers do not install the private pi-ai alias, Google SDK or protobufjs.
875
- - Fresh temporary root dependency resolution with `--strict-peer-deps --package-lock-only --ignore-scripts` succeeds. `git diff --check` is clean.
876
- - The built artifact read the real public gateway listing and models.dev catalog using a temporary home. Both sources were live, and Gemini was available with `google-generative-ai`, 1,048,576 context tokens, 65,536 output tokens and only Low/Medium/High thinking levels.
877
-
878
- The Google SDK and HTTP dependencies increase the unminified host bundle from approximately 0.37 MB to 2.1 MB; the packed artifact is approximately 1.0 MB. Original bundled licenses are included, including the license embedded in data-uri-to-buffer's README. The Google fetch bridge is deliberately pinned and must be reviewed before a Google SDK upgrade. Streaming validation uses loopback gateways and fake credentials; the live check only reads public catalogs. No paid inference, installed user-profile update or package publication was performed.
879
-
880
- ## Review follow-up: fixed Gemini backend and keyboard-accessible hints (2026-10-14)
881
-
882
- The Google SDK inherited `GOOGLE_GENAI_USE_VERTEXAI` or `GOOGLE_GENAI_USE_ENTERPRISE` from the host, silently changing the gateway endpoint from `/v1/models/...` to `/v1/publishers/google/models/...`. The shared build/test bridge now inserts `vertexai: false` into pi-ai 1.2.0's Google client factory, with an exact version check and a single constructor match. Other Google clients and the host environment keep their own backend selection.
883
-
884
- The settings hint mark was an unfocusable, accessibility-hidden span even though its sentence had moved out of visible text. It is now a named help button with a visible keyboard focus indicator; focusing it opens the host Tooltip, and moving focus back to the field closes it. The field retains its original `aria-describedby` sentence, and a host without Tooltip still renders that sentence visibly.
885
-
886
- Three new core cases failed before the fixes and now pass: each cloud-backend flag preserves the Gemini route without changing other clients, and keyboard focus opens and dismisses the hint. Installed transcript fixtures enable both cloud flags, while installed browser fixtures check focus, tooltip content and actual visibility against each host's real primitives. JSDOM's missing `ResizeObserver` is supplied with one static DOM measurement so modern Tooltips can finish placement; this fixture does not verify browser resizing or layout geometry.
887
-
888
- Validation: `npm run verify` passed on macOS with Node 24.14.1. All **683 tests in 43 files** pass, both TypeScript projects compile, and all **64 shipped files** match an independent rebuild. All **ten installed hosts** pass with four workers (**40.8 seconds** including cleanup). npm Git, pnpm Git and packed npm installations, ESM loading and public consumer types pass without source builds or build approvals (**45.6 seconds** including cleanup). `git diff --check` is clean.
889
-
890
- ## Account lifecycle regression fixes (2026-10-14)
891
-
892
- An addition now resumes only its exact operation/account ID. Matching labels on another browser surface cannot redirect a new key into an existing pending addition. Retrying the same form preserves its ID; cancelling and opening another addition form starts a fresh ID even when its label matches. A pending deletion cannot be replaced by an addition retry.
893
-
894
- Account credential mutations share a per-reference write queue within the Host process. Cancellation and command deadlines stop waiting without releasing the actual storage write. Removing an interrupted addition retains its durable deletion intention while earlier writes are running, then rechecks credential state and cleans up before clearing metadata and the intention. The queue survives manager reconnection, remounts and replacement credential-provider objects in the same module instance. Failed earlier writes release the queue too. Key replacement rechecks the exact account identity and deletion state immediately before its queued write, so an account removed during credential description cannot be recreated by that stale replacement. A replacement already writing finishes before credential deletion.
895
-
896
- Rejected-key caching now captures a per-reference credential version at the start of each attempt. A stored-key change or explicit retry invalidates older in-flight rejections as well as the existing cache. Updating a different reference does not suppress a valid rejection. Automatic fallback eligibility, first-output settlement, promotion of the serving account and the five-minute rejection TTL retain their existing policy.
897
-
898
- Regression coverage adds **12 cases** across account-manager, browser-controller/card and adapter tests. Ten cases failed against the original implementation before the fixes; the expanded suite covers same-label recovery, cancelled forms, interrupted and failed writes, reconnection/remount/provider replacement, command deadlines, concurrent replacement/removal, and real loopback HTTP rejections after key changes. Promise gates control race ordering; the deadline case uses fake timers. Fixture credentials and temporary homes are used throughout.
899
-
900
- Validation on macOS with Node 24.14.1:
901
-
902
- - `npm run compile` and `npm run test:ci`: **702 tests in 43 files**, both TypeScript projects clean, and all **64 shipped files** matching an independent rebuild.
903
- - `DSH_COMPAT_CONCURRENCY=4 npm run test:compat`: all **ten installed hosts** pass, including real legacy settings/credential-file recovery and profile account commands, adoption and recovery (**110.7 seconds** including cleanup).
904
- - `npm run test:install`: npm Git, pnpm Git and packed npm installations, ESM loading and public consumer types pass without source builds or dependency build approvals (**42.5 seconds** including cleanup).
905
- - `git diff --check` is clean. No paid inference, installed user-profile update or package publication is performed.
906
-
907
- The write queue coordinates this plugin's in-process mutations; it does not add a cross-process transaction between settings and credentials or change the existing ownership rules for references shared across profiles. Recovery intentions remain necessary, and a provider whose actual write never settles keeps the affected deletion pending until storage can finish or the process restarts.
908
-
909
- ## Gemini desktop inference route correction (2026-10-14)
910
-
911
- The desktop plugin was loaded and the model was selectable, but its default legacy Go address produced an HTML 404 at the Google RPC path. Public probes with an invalid test credential reproduced HTML 404 responses for both Google methods below /zen/go/v1; the canonical /inference/go/v1 routes returned structured authentication errors instead. The canonical model listing without credentials returned HTTP 200 with 37 models.
912
-
913
- modelBaseURL now maps only Google requests using the exact public legacy alias to the canonical Go base. A regression covers live-metadata conversion, trailing slashes, canonical addresses, custom gateways and preservation of the existing OpenAI/Anthropic paths. The build and targeted Google/catalog/proxy suite pass 57 tests. The full core suite passes 703 tests in 43 files, both TypeScript projects are clean, and all 64 shipped files match an independent rebuild. git diff --check is clean.
914
-
915
- A single native-runtime smoke call used the existing desktop account and actual app peer packages, a synthetic public prompt, Low thinking, a 512-token output cap and a 45-second deadline. The corrected built artifact reached the canonical Google path, received HTTP 200 text/event-stream, and finished with OK after about 8.1 seconds. Credentials remained in memory and no settings were written by that smoke process.
916
-
917
- The desktop gateway address was then changed through its settings UI to https://opencode.ai/inference/go/v1. The saved update took effect without restarting the app; the quota indicator still refreshed successfully. A short connection-test message in the user's existing Gemini chat returned 连接成功, with the UI showing completion after six seconds. Existing account settings and conversation history were retained. This follow-up includes two real inference calls and a saved desktop configuration update; it does not publish or reinstall a package.
918
-
919
- ## Canonical default gateway (2026-10-14)
920
-
921
- `DEFAULT_BASE_URL` now uses `https://opencode.ai/inference/go/v1`, so new installations, inherited defaults and settings resets use the canonical prefix for discovery, inference and usage. Saved legacy official addresses retain the Google-specific compatibility mapping above. The configuration example, declaration artifacts and both advanced guides are synchronized; the guides explain why a working `/models` listing does not establish that the Google RPC routes exist.
922
-
923
- `npm run compile` and `npm run test:ci` pass: **703 tests in 43 files**, both TypeScript projects clean, and all **64 shipped files** matching an independent rebuild. `DSH_COMPAT_CONCURRENCY=3 npm run test:compat -- v017-rc2 v020-rc2 v021-alpha1` passes all three installed hosts (**75.1 seconds** including cleanup). The existing real-profile fixture omits `baseURL` and now checks model discovery, inference with account fallback and usage requests against the canonical default. `git diff --check` is clean.
924
-
925
- ## Unified migration of the official gateway (2026-10-14)
926
-
927
- This supersedes the Google-only correction above. The legacy official base is now migrated before model discovery, every inference protocol and usage requests. The plain and volatile schemas share a self-contained transform that also survives settings-schema serialization. Raw callers receive the same migration through URL validation, catalog construction and metadata conversion. Custom gateways retain their configured addresses. Legacy section providers also rewrite saved aliases on startup and document updates, using revision checks to preserve concurrent changes; model requests already use the new gateway while persistence completes.
928
-
929
- Three regressions cover old composition entries and stored settings with the legacy gateway permanently unavailable, actual loopback streams for all four protocols, usage through the real RPC codec, rewriting saved settings, custom gateways and serialized schema callbacks. Installed profile fixtures verify that submitting an old address saves the canonical value without remounting. Installed legacy fixtures start with an old address alongside account recovery and confirm that the managed settings file is migrated.
930
-
931
- Validation: `npm run compile` and `npm run test:ci` pass **706 tests in 44 files**, both TypeScript projects, and all **65 shipped files** matching an independent rebuild. `DSH_COMPAT_CONCURRENCY=3 npm run test:compat -- v015-rc1 v016-alpha1 v020-rc2` passes all three installed hosts (**13.2 seconds** including cleanup), covering both legacy persisted sections and modern profile writes. The modern profile migration also passed on v017-rc2 and v021-alpha1 before the legacy persistence addition. `git diff --check` is clean.
932
-
933
- ## Release validation: 0.1.23 (2026-10-14)
934
-
935
- The package and root lockfile now both declare stable version `0.1.23`. The release includes Google protocol support, the canonical official gateway and persisted legacy-address migration, account credential-write race protection, current-account ordering, and the settings improvements since `0.1.22`.
936
-
937
- `DSH_COMPAT_CONCURRENCY=4 npm run verify` passes **706 core tests in 44 files**, both TypeScript projects, **65 matching build artifacts**, and all **ten installed DSH hosts**. The full host matrix completed before cleanup in **103.9 seconds**. npm Git, fresh pnpm 11.7.0 Git and packed-package installation, ESM loading and public consumer type checks all pass without dependency build approvals; the installation suite took **51.1 seconds**, including cleanup. `git diff --check` is clean. These release checks use isolated profiles, fixture credentials and local inference gateways.
938
-
939
- ## Windows Google bridge loader follow-up (2026-10-14)
940
-
941
- The first release CI run failed ten Google/proxy source tests on both Windows Node versions. Vite passes normalized forward-slash module IDs, whereas Node's `fileURLToPath` returns native Windows separators, so the exact-path comparison missed both Google substitutions in the test loader. Artifact freshness, installation checks and the installed-host matrix passed on that candidate.
942
-
943
- The shared bridge now normalizes separators, optional drive-prefix slashes and query suffixes before comparing module identities. Eight regressions cover Windows SDK/adapter paths, UNC and Unix IDs, and exclusion of other directories, drives and SDK entries. `npm run test:ci` now passes **714 tests in 45 files**, and the **65 built artifacts remain byte-identical**. The ten-host and npm/pnpm checks above exercised those same runtime artifacts.
77
+ No publication or paid upstream inference was performed for the dashboard migration.