dsh-opencode-go 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.en.md CHANGED
@@ -36,7 +36,7 @@ If your DSH version does not have an **Add plugin** entry, use the command-line
36
36
  ### Command-line installation (alternative)
37
37
 
38
38
  ```sh
39
- dsh plugin --profile web add dsh-opencode-go@0.1.13
39
+ dsh plugin --profile web add dsh-opencode-go@0.1.14
40
40
  ```
41
41
 
42
42
  Start or restart `dsh web`, then:
@@ -50,7 +50,7 @@ Start or restart `dsh web`, then:
50
50
  Install the plugin into the Headless profile:
51
51
 
52
52
  ```sh
53
- dsh plugin --profile headless add dsh-opencode-go@0.1.13
53
+ dsh plugin --profile headless add dsh-opencode-go@0.1.14
54
54
  ```
55
55
 
56
56
  Save the following as `headless.patch.yml` to select a default model:
@@ -77,7 +77,7 @@ To build from source and install a local package:
77
77
  ```sh
78
78
  npm ci --legacy-peer-deps
79
79
  npm pack
80
- dsh plugin --profile web add ./dsh-opencode-go-0.1.13.tgz
80
+ dsh plugin --profile web add ./dsh-opencode-go-0.1.14.tgz
81
81
  ```
82
82
 
83
83
  The development dependencies include real test packages from multiple DSH generations, so installation requires `--legacy-peer-deps`. For Headless, replace `web` with `headless`.
@@ -96,10 +96,6 @@ Restart `dsh web` and refresh the browser afterwards. For Headless, replace `web
96
96
 
97
97
  Usage refreshes every minute. Temporary network or service errors retain the last reading for the same account, with a failure notice, timestamp, and reason; the usage panel offers an immediate retry. Initial and authentication failures do not show old usage. Catalog, metadata, and usage JSON requests retry a transient connection reset once within the original timeout budget; this cannot guarantee recovery while the network is failing.
98
98
 
99
- The usage panel also shows the current session's cached input share, cache read/write tokens, total input tokens, and measured response count. The formula is `cacheReadTokens / (inputTokens + cacheReadTokens + cacheWriteTokens)`, weighted by tokens and excluding output tokens. Statistics are derived from the Host's loaded session history, can be reconstructed after session restore, and add no network requests. The panel subscribes only while open. Account usage failures do not affect these statistics.
100
-
101
- Only settled Go responses with an identifiable route and valid usage are counted. Unsettled attempts and inherited fork responses are excluded; missing route/usage and incomplete history are indicated. Older Hosts without a session event feed continue to show account usage. A stable `x-opencode-session` supports routing affinity but does not establish a cache hit; actual cache reads are reported by the gateway.
102
-
103
99
  ![OpenCode Go usage display](image.png)
104
100
 
105
101
  ## Interface language
package/README.md CHANGED
@@ -36,7 +36,7 @@
36
36
  ### 命令行安装(备选)
37
37
 
38
38
  ```sh
39
- dsh plugin --profile web add dsh-opencode-go@0.1.13
39
+ dsh plugin --profile web add dsh-opencode-go@0.1.14
40
40
  ```
41
41
 
42
42
  安装后启动或重启 `dsh web`,然后:
@@ -50,7 +50,7 @@ dsh plugin --profile web add dsh-opencode-go@0.1.13
50
50
  安装到 Headless profile:
51
51
 
52
52
  ```sh
53
- dsh plugin --profile headless add dsh-opencode-go@0.1.13
53
+ dsh plugin --profile headless add dsh-opencode-go@0.1.14
54
54
  ```
55
55
 
56
56
  将以下内容保存为 `headless.patch.yml`,选择默认模型:
@@ -77,7 +77,7 @@ dsh --profile headless --patch ./headless.patch.yml "你好"
77
77
  ```sh
78
78
  npm ci --legacy-peer-deps
79
79
  npm pack
80
- dsh plugin --profile web add ./dsh-opencode-go-0.1.13.tgz
80
+ dsh plugin --profile web add ./dsh-opencode-go-0.1.14.tgz
81
81
  ```
82
82
 
83
83
  开发依赖包含多代 DSH 的真实测试包,安装时需要 `--legacy-peer-deps`。Headless 用户将 `web` 换成 `headless`。
@@ -96,10 +96,6 @@ dsh plugin --profile web update dsh-opencode-go --latest
96
96
 
97
97
  用量每分钟刷新。临时网络或服务错误会保留同一账号的上次数据,并标明刷新失败、更新时间和错误原因;可在用量弹层中立即重试。首次获取失败或鉴权失败时不显示旧额度。模型目录、模型配置和用量的 JSON 请求遇到短暂连接重置时会在原有超时范围内额外重试一次;这不能保证故障中的网络恢复可用。
98
98
 
99
- 用量弹层还会显示当前会话的缓存读取比例、缓存读取/写入 tokens、总输入 tokens 和已统计响应数。计算公式为 `cacheReadTokens / (inputTokens + cacheReadTokens + cacheWriteTokens)`,按 tokens 加权,不包含输出 tokens。数据来自宿主已加载的会话历史,恢复会话后可重新计算,不增加网络请求;仅打开弹层时订阅更新。账号额度读取失败不影响这些统计。
100
-
101
- 统计只包含可确定路由且有有效用量的 Go 已结算响应,排除未结算请求和分叉继承的响应;缺少路由或用量的响应会注明未统计,历史未加载完整时也会提示。不支持会话事件接口的旧宿主继续显示账号额度。稳定的 `x-opencode-session` 只支持路由保持一致,不能证明缓存命中;实际读取量由网关报告。
102
-
103
99
  ![OpenCode Go usage display](image.png)
104
100
 
105
101
  ## 界面语言
@@ -1,5 +1,13 @@
1
1
  # Verification
2
2
 
3
+ ## Account usage panel and unsupported-history guards (plugin 0.1.14, 2026-09-26)
4
+
5
+ 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.
6
+
7
+ 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.
8
+
9
+ 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.
10
+
3
11
  ## Issue #14: distinguish unavailable metadata from missing model configuration (2026-09-26)
4
12
 
5
13
  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.
package/lib/client.js CHANGED
@@ -130,16 +130,16 @@ var modelsRemote = {
130
130
  }]
131
131
  };
132
132
 
133
- // plugin-css:/private/var/folders/4z/q9yrksqd0y12spqrxkv0p_dm0000gn/T/dsh-release-0.1.13-Tf3MEL/source/src/client/Section.module.css
133
+ // plugin-css:/Users/tanglei/.codex/worktrees/release-0-1-14/dsh-opencode-go/src/client/Section.module.css
134
134
  var id = "dsh-opencode-go/Section.module.css";
135
135
  if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")) {
136
136
  const style = document.createElement("style");
137
137
  style.dataset.plugin = "dsh-opencode-go";
138
138
  style.dataset.pluginCss = id;
139
- style.textContent = '.Juo2ua_page{flex-direction:column;min-width:0;height:100%;min-height:0;display:flex;container-type:size}.Juo2ua_pageBody{scrollbar-gutter:stable;flex:1;min-height:0;padding-right:4px;overflow-y:auto}.Juo2ua_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.Juo2ua_field+.Juo2ua_field{border-top:.5px solid var(--dsw-alias-border-l2)}.Juo2ua_head{align-items:center;gap:8px;display:flex}.Juo2ua_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.Juo2ua_badges{align-items:center;gap:8px;display:inline-flex}.Juo2ua_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.Juo2ua_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.Juo2ua_reset:disabled{cursor:default}.Juo2ua_reset:focus-visible,.Juo2ua_disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.Juo2ua_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.Juo2ua_input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-color:var(--dsw-alias-brand-primary)}.Juo2ua_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.Juo2ua_inputInvalid{border-color:var(--dsw-alias-label-error);}.Juo2ua_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Juo2ua_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.Juo2ua_intro{color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-size:13px;line-height:1.5}.Juo2ua_disclosure{font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;margin:0;padding:0;display:flex}.Juo2ua_disclosure:hover .Juo2ua_label{color:var(--dsw-alias-brand-primary)}.Juo2ua_chevron,.Juo2ua_chevronOpen{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none}.Juo2ua_chevron{transform:rotate(-90deg)}.Juo2ua_advanced{flex-direction:column;gap:4px;padding:4px 0 0 22px;display:flex}.Juo2ua_advanced .Juo2ua_field+.Juo2ua_field{border-top:none}.Juo2ua_chips{flex-wrap:wrap;gap:6px;display:flex}.Juo2ua_limitsEditor{flex-direction:column;gap:10px;padding-top:4px;display:flex}.Juo2ua_limitsToolbar{grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:6px 10px;display:grid}.Juo2ua_limitsFilterLabel{color:var(--dsw-alias-label-secondary);grid-column:1/-1;font-size:12px;font-weight:500;line-height:1.5}.Juo2ua_limitsSummary{color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-bottom:8px;font-size:12px;line-height:1.5}.Juo2ua_limitsId,.Juo2ua_limitsModelId{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.Juo2ua_limitsId{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.4}.Juo2ua_limitsModelId{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.4}.Juo2ua_visuallyHidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width<=560px){.Juo2ua_limitsToolbar{grid-template-columns:1fr}.Juo2ua_limitsSummary{padding-bottom:0}}.Juo2ua_actions{border-top:.5px solid var(--dsw-alias-border-l2);flex-wrap:wrap;flex:none;align-items:center;gap:8px;padding:12px 0;display:flex}.Juo2ua_starLink{max-width:100%;color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;gap:7px;margin-left:auto;padding:6px 0;font-size:12px;line-height:1.5;text-decoration:none;display:inline-flex}.Juo2ua_starLink svg{flex:none}.Juo2ua_starLink:hover{color:var(--dsw-alias-brand-primary)}.Juo2ua_starLink:hover span{text-underline-offset:3px;text-decoration:underline}.Juo2ua_starLink:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.Juo2ua_failedNote{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.Juo2ua_pageTop{align-items:flex-start;gap:16px;display:flex}.Juo2ua_pageTop .Juo2ua_intro{flex:1}.Juo2ua_advancedTrigger{flex-shrink:0;margin-left:auto;}.Juo2ua_advancedTrigger .Juo2ua_label{color:var(--dsw-alias-label-tertiary);font-size:12px}.Juo2ua_filters{flex-wrap:wrap;gap:6px;display:flex}.Juo2ua_filter{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;padding:6px 9px;font-size:12px}.Juo2ua_filter[aria-pressed=true]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3)}.Juo2ua_filter span{color:var(--dsw-alias-label-tertiary);margin-left:4px}.Juo2ua_modelLayout{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;grid-template-columns:minmax(0,43fr) minmax(0,57fr);height:clamp(240px,100cqh - 460px,480px);display:grid;overflow:hidden}.Juo2ua_modelList{border-right:.5px solid var(--dsw-alias-border-l2);min-height:0;padding:6px;overflow-y:auto}.Juo2ua_modelRow{align-items:center;gap:8px;padding-right:6px;display:flex}.Juo2ua_modelRow>[role=switch]{flex-shrink:0}.Juo2ua_modelChoice{text-align:left;min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:1;padding:12px 9px;display:block}.Juo2ua_modelChoice[aria-pressed=true],.Juo2ua_modelChoice:hover{background:var(--dsw-alias-bg-layer-3)}.Juo2ua_modelName{overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:5px;font-size:13px;display:flex}.Juo2ua_modelDetails{flex-direction:column;align-items:stretch;gap:10px;min-width:0;padding:18px;display:flex;overflow-y:auto}.Juo2ua_modelDetails h3{overflow-wrap:anywhere;margin:0;font-size:16px}.Juo2ua_modelHeading{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.Juo2ua_modelDetails .Juo2ua_field{border:0;padding:8px 0}.Juo2ua_modelDetails .Juo2ua_input{box-sizing:border-box;width:100%;min-width:0}.Juo2ua_modelDetails .Juo2ua_reset{align-self:flex-start}.Juo2ua_newBadge{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:4px;padding:2px 4px;font-size:9px}.Juo2ua_releaseDate{color:var(--dsw-alias-label-tertiary);margin-top:4px;font-size:10px;display:block}.Juo2ua_modelChoice:focus-visible,.Juo2ua_filter:focus-visible,.Juo2ua_advancedTrigger:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}@container (width<=440px){.Juo2ua_pageTop{flex-wrap:wrap}.Juo2ua_modelLayout{grid-template-columns:minmax(0,1fr);height:auto}.Juo2ua_modelList{border-right:0;border-bottom:.5px solid var(--dsw-alias-border-l2);max-height:240px}.Juo2ua_modelDetails{max-height:460px}}.Juo2ua_keySection{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px 0}.Juo2ua_keySection summary{cursor:pointer;list-style:none}.Juo2ua_keySection summary:after{content:"\u2304";color:var(--dsw-alias-label-tertiary)}.Juo2ua_keySection summary::-webkit-details-marker{display:none}.Juo2ua_keySection>.Juo2ua_input{box-sizing:border-box;width:100%;margin:10px 0 6px}.Juo2ua_keySection summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}';
139
+ style.textContent = '.LKs06q_page{flex-direction:column;min-width:0;height:100%;min-height:0;display:flex;container-type:size}.LKs06q_pageBody{scrollbar-gutter:stable;flex:1;min-height:0;padding-right:4px;overflow-y:auto}.LKs06q_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.LKs06q_field+.LKs06q_field{border-top:.5px solid var(--dsw-alias-border-l2)}.LKs06q_head{align-items:center;gap:8px;display:flex}.LKs06q_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.LKs06q_badges{align-items:center;gap:8px;display:inline-flex}.LKs06q_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.LKs06q_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.LKs06q_reset:disabled{cursor:default}.LKs06q_reset:focus-visible,.LKs06q_disclosure:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.LKs06q_input{border:.5px solid var(--dsw-alias-border-l4);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.LKs06q_input:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px;border-color:var(--dsw-alias-brand-primary)}.LKs06q_input:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.LKs06q_inputInvalid{border-color:var(--dsw-alias-label-error);}.LKs06q_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.LKs06q_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}.LKs06q_intro{color:var(--dsw-alias-label-secondary);margin:0 0 8px;font-size:13px;line-height:1.5}.LKs06q_disclosure{font:inherit;text-align:left;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;margin:0;padding:0;display:flex}.LKs06q_disclosure:hover .LKs06q_label{color:var(--dsw-alias-brand-primary)}.LKs06q_chevron,.LKs06q_chevronOpen{width:14px;height:14px;color:var(--dsw-alias-label-tertiary);flex:none}.LKs06q_chevron{transform:rotate(-90deg)}.LKs06q_advanced{flex-direction:column;gap:4px;padding:4px 0 0 22px;display:flex}.LKs06q_advanced .LKs06q_field+.LKs06q_field{border-top:none}.LKs06q_chips{flex-wrap:wrap;gap:6px;display:flex}.LKs06q_limitsEditor{flex-direction:column;gap:10px;padding-top:4px;display:flex}.LKs06q_limitsToolbar{grid-template-columns:minmax(0,1fr) auto;align-items:end;gap:6px 10px;display:grid}.LKs06q_limitsFilterLabel{color:var(--dsw-alias-label-secondary);grid-column:1/-1;font-size:12px;font-weight:500;line-height:1.5}.LKs06q_limitsSummary{color:var(--dsw-alias-label-tertiary);white-space:nowrap;padding-bottom:8px;font-size:12px;line-height:1.5}.LKs06q_limitsId,.LKs06q_limitsModelId{text-overflow:ellipsis;white-space:nowrap;display:block;overflow:hidden}.LKs06q_limitsId{color:var(--dsw-alias-label-primary);font-size:13px;line-height:1.4}.LKs06q_limitsModelId{color:var(--dsw-alias-label-tertiary);margin-top:2px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;line-height:1.4}.LKs06q_visuallyHidden{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}@media (width<=560px){.LKs06q_limitsToolbar{grid-template-columns:1fr}.LKs06q_limitsSummary{padding-bottom:0}}.LKs06q_actions{border-top:.5px solid var(--dsw-alias-border-l2);flex-wrap:wrap;flex:none;align-items:center;gap:8px;padding:12px 0;display:flex}.LKs06q_starLink{max-width:100%;color:var(--dsw-alias-label-secondary);border-radius:4px;align-items:center;gap:7px;margin-left:auto;padding:6px 0;font-size:12px;line-height:1.5;text-decoration:none;display:inline-flex}.LKs06q_starLink svg{flex:none}.LKs06q_starLink:hover{color:var(--dsw-alias-brand-primary)}.LKs06q_starLink:hover span{text-underline-offset:3px;text-decoration:underline}.LKs06q_starLink:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:3px}.LKs06q_failedNote{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.LKs06q_pageTop{align-items:flex-start;gap:16px;display:flex}.LKs06q_pageTop .LKs06q_intro{flex:1}.LKs06q_advancedTrigger{flex-shrink:0;margin-left:auto;}.LKs06q_advancedTrigger .LKs06q_label{color:var(--dsw-alias-label-tertiary);font-size:12px}.LKs06q_filters{flex-wrap:wrap;gap:6px;display:flex}.LKs06q_filter{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:0;border-radius:6px;padding:6px 9px;font-size:12px}.LKs06q_filter[aria-pressed=true]{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3)}.LKs06q_filter span{color:var(--dsw-alias-label-tertiary);margin-left:4px}.LKs06q_modelLayout{border:.5px solid var(--dsw-alias-border-l4);border-radius:10px;grid-template-columns:minmax(0,43fr) minmax(0,57fr);height:clamp(240px,100cqh - 460px,480px);display:grid;overflow:hidden}.LKs06q_modelList{border-right:.5px solid var(--dsw-alias-border-l2);min-height:0;padding:6px;overflow-y:auto}.LKs06q_modelRow{align-items:center;gap:8px;padding-right:6px;display:flex}.LKs06q_modelRow>[role=switch]{flex-shrink:0}.LKs06q_modelChoice{text-align:left;min-width:0;color:var(--dsw-alias-label-primary);cursor:pointer;background:0 0;border:0;border-radius:6px;flex:1;padding:12px 9px;display:block}.LKs06q_modelChoice[aria-pressed=true],.LKs06q_modelChoice:hover{background:var(--dsw-alias-bg-layer-3)}.LKs06q_modelName{overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:5px;font-size:13px;display:flex}.LKs06q_modelDetails{flex-direction:column;align-items:stretch;gap:10px;min-width:0;padding:18px;display:flex;overflow-y:auto}.LKs06q_modelDetails h3{overflow-wrap:anywhere;margin:0;font-size:16px}.LKs06q_modelHeading{flex-wrap:wrap;align-items:center;gap:6px;display:flex}.LKs06q_modelDetails .LKs06q_field{border:0;padding:8px 0}.LKs06q_modelDetails .LKs06q_input{box-sizing:border-box;width:100%;min-width:0}.LKs06q_modelDetails .LKs06q_reset{align-self:flex-start}.LKs06q_newBadge{color:var(--dsw-alias-label-primary);background:var(--dsw-alias-bg-layer-3);border:.5px solid var(--dsw-alias-border-l4);border-radius:4px;padding:2px 4px;font-size:9px}.LKs06q_releaseDate{color:var(--dsw-alias-label-tertiary);margin-top:4px;font-size:10px;display:block}.LKs06q_modelChoice:focus-visible,.LKs06q_filter:focus-visible,.LKs06q_advancedTrigger:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}@container (width<=440px){.LKs06q_pageTop{flex-wrap:wrap}.LKs06q_modelLayout{grid-template-columns:minmax(0,1fr);height:auto}.LKs06q_modelList{border-right:0;border-bottom:.5px solid var(--dsw-alias-border-l2);max-height:240px}.LKs06q_modelDetails{max-height:460px}}.LKs06q_keySection{border-top:.5px solid var(--dsw-alias-border-l2);padding:12px 0}.LKs06q_keySection summary{cursor:pointer;list-style:none}.LKs06q_keySection summary:after{content:"\u2304";color:var(--dsw-alias-label-tertiary)}.LKs06q_keySection summary::-webkit-details-marker{display:none}.LKs06q_keySection>.LKs06q_input{box-sizing:border-box;width:100%;margin:10px 0 6px}.LKs06q_keySection summary:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}';
140
140
  document.head.appendChild(style);
141
141
  }
142
- var Section_default = { "modelRow": "Juo2ua_modelRow", "chevronOpen": "Juo2ua_chevronOpen", "head": "Juo2ua_head", "badges": "Juo2ua_badges", "starLink": "Juo2ua_starLink", "limitsModelId": "Juo2ua_limitsModelId", "intro": "Juo2ua_intro", "limitsSummary": "Juo2ua_limitsSummary", "limitsFilterLabel": "Juo2ua_limitsFilterLabel", "modelName": "Juo2ua_modelName", "failedNote": "Juo2ua_failedNote", "hint": "Juo2ua_hint", "limitsId": "Juo2ua_limitsId", "inputInvalid": "Juo2ua_inputInvalid Juo2ua_input", "releaseDate": "Juo2ua_releaseDate", "pageBody": "Juo2ua_pageBody", "pageTop": "Juo2ua_pageTop", "label": "Juo2ua_label", "limitsEditor": "Juo2ua_limitsEditor", "actions": "Juo2ua_actions", "filter": "Juo2ua_filter", "filters": "Juo2ua_filters", "modelLayout": "Juo2ua_modelLayout", "field": "Juo2ua_field", "modelList": "Juo2ua_modelList", "modelHeading": "Juo2ua_modelHeading", "newBadge": "Juo2ua_newBadge", "keySection": "Juo2ua_keySection", "disclosure": "Juo2ua_disclosure", "visuallyHidden": "Juo2ua_visuallyHidden", "advancedTrigger": "Juo2ua_advancedTrigger Juo2ua_disclosure", "reset": "Juo2ua_reset", "limitsToolbar": "Juo2ua_limitsToolbar", "advanced": "Juo2ua_advanced", "chevron": "Juo2ua_chevron", "input": "Juo2ua_input", "chips": "Juo2ua_chips", "page": "Juo2ua_page", "modelChoice": "Juo2ua_modelChoice", "invalid": "Juo2ua_invalid", "modelDetails": "Juo2ua_modelDetails" };
142
+ var Section_default = { "field": "LKs06q_field", "head": "LKs06q_head", "chevron": "LKs06q_chevron", "label": "LKs06q_label", "inputInvalid": "LKs06q_inputInvalid LKs06q_input", "chevronOpen": "LKs06q_chevronOpen", "chips": "LKs06q_chips", "badges": "LKs06q_badges", "actions": "LKs06q_actions", "modelRow": "LKs06q_modelRow", "pageBody": "LKs06q_pageBody", "visuallyHidden": "LKs06q_visuallyHidden", "advancedTrigger": "LKs06q_advancedTrigger LKs06q_disclosure", "pageTop": "LKs06q_pageTop", "filters": "LKs06q_filters", "newBadge": "LKs06q_newBadge", "limitsModelId": "LKs06q_limitsModelId", "releaseDate": "LKs06q_releaseDate", "hint": "LKs06q_hint", "limitsToolbar": "LKs06q_limitsToolbar", "input": "LKs06q_input", "limitsId": "LKs06q_limitsId", "starLink": "LKs06q_starLink", "modelName": "LKs06q_modelName", "invalid": "LKs06q_invalid", "limitsFilterLabel": "LKs06q_limitsFilterLabel", "limitsSummary": "LKs06q_limitsSummary", "modelLayout": "LKs06q_modelLayout", "modelDetails": "LKs06q_modelDetails", "modelHeading": "LKs06q_modelHeading", "keySection": "LKs06q_keySection", "advanced": "LKs06q_advanced", "failedNote": "LKs06q_failedNote", "filter": "LKs06q_filter", "reset": "LKs06q_reset", "intro": "LKs06q_intro", "limitsEditor": "LKs06q_limitsEditor", "modelList": "LKs06q_modelList", "modelChoice": "LKs06q_modelChoice", "page": "LKs06q_page", "disclosure": "LKs06q_disclosure" };
143
143
 
144
144
  // src/client/ModelEditor.tsx
145
145
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -1229,17 +1229,6 @@ function refOf(snapshot) {
1229
1229
 
1230
1230
  // src/client/locales.ts
1231
1231
  var en = {
1232
- cacheTitle: "Session cache reads",
1233
- cacheReadRatio: "Cached input share",
1234
- cacheReadTokens: "Cache read tokens",
1235
- cacheInputTokens: "Total input tokens",
1236
- cacheWriteTokens: "Cache write tokens",
1237
- cacheResponses: "Measured responses",
1238
- cacheEmpty: "No usable Go response usage in the loaded history yet.",
1239
- cacheExcluded: "Responses excluded for missing route or usage:",
1240
- cacheScope: "Settled Go responses in loaded history. Inherited fork responses and unsettled attempts are excluded.",
1241
- cacheMore: "Older history is not fully loaded.",
1242
- cacheRoutingHint: "A stable session header supports routing affinity. Cache reads are reported separately by the gateway and may remain zero.",
1243
1232
  usageTitle: "OpenCode Go usage",
1244
1233
  usageRollingShort: "5h",
1245
1234
  usageHint: "Account usage \xB7 used percentage \xB7 refreshes every minute",
@@ -1348,17 +1337,6 @@ var en = {
1348
1337
  unavailable: "The OpenCode Go settings are not served in this deployment."
1349
1338
  };
1350
1339
  var zh = {
1351
- cacheTitle: "\u4F1A\u8BDD\u7F13\u5B58\u8BFB\u53D6",
1352
- cacheReadRatio: "\u7F13\u5B58\u8BFB\u53D6\u5360\u8F93\u5165\u6BD4\u4F8B",
1353
- cacheReadTokens: "\u7F13\u5B58\u8BFB\u53D6 tokens",
1354
- cacheInputTokens: "\u603B\u8F93\u5165 tokens",
1355
- cacheWriteTokens: "\u7F13\u5B58\u5199\u5165 tokens",
1356
- cacheResponses: "\u5DF2\u7EDF\u8BA1\u54CD\u5E94\u6570",
1357
- cacheEmpty: "\u5DF2\u52A0\u8F7D\u5386\u53F2\u4E2D\u6682\u65E0\u53EF\u7528\u7684 Go \u54CD\u5E94\u7528\u91CF\u3002",
1358
- cacheExcluded: "\u56E0\u7F3A\u5C11\u8DEF\u7531\u6216\u7528\u91CF\u800C\u672A\u7EDF\u8BA1\u7684\u54CD\u5E94\uFF1A",
1359
- cacheScope: "\u7EDF\u8BA1\u5DF2\u52A0\u8F7D\u5386\u53F2\u4E2D\u7684 Go \u5DF2\u7ED3\u7B97\u54CD\u5E94\uFF0C\u6392\u9664\u5206\u53C9\u7EE7\u627F\u7684\u54CD\u5E94\u53CA\u672A\u7ED3\u7B97\u7684\u8BF7\u6C42\u3002",
1360
- cacheMore: "\u66F4\u65E9\u7684\u5386\u53F2\u5C1A\u672A\u5168\u90E8\u52A0\u8F7D\u3002",
1361
- cacheRoutingHint: "\u7A33\u5B9A\u7684\u4F1A\u8BDD\u6807\u8BC6\u6709\u52A9\u4E8E\u8DEF\u7531\u4FDD\u6301\u4E00\u81F4\uFF1B\u5B9E\u9645\u7F13\u5B58\u8BFB\u53D6\u91CF\u7531\u7F51\u5173\u53E6\u884C\u62A5\u544A\uFF0C\u4ECD\u53EF\u80FD\u4E3A\u96F6\u3002",
1362
1340
  usageTitle: "OpenCode Go \u7528\u91CF",
1363
1341
  usageRollingShort: "5\u5C0F\u65F6",
1364
1342
  usageHint: "\u8D26\u53F7\u989D\u5EA6 \xB7 \u5DF2\u7528\u767E\u5206\u6BD4 \xB7 \u6BCF\u5206\u949F\u5237\u65B0",
@@ -1513,152 +1491,21 @@ var goRemote = {
1513
1491
  };
1514
1492
 
1515
1493
  // src/client/UsagePill.tsx
1516
- var import_react4 = require("react");
1517
-
1518
- // src/client/SessionCache.tsx
1519
1494
  var import_react3 = require("react");
1520
1495
 
1521
- // src/client/session-cache.ts
1522
- function record(value) {
1523
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1524
- }
1525
- function tokens(value) {
1526
- return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
1527
- }
1528
- function sessionEventSource(sessions, sessionId) {
1529
- const service = record(sessions);
1530
- if (typeof service?.binding !== "function") return void 0;
1531
- const source = record(record(service.binding(sessionId))?.eventSource);
1532
- if (typeof source?.getSnapshot !== "function" || typeof source.subscribe !== "function") return void 0;
1533
- const getSnapshot = source.getSnapshot.bind(source);
1534
- const subscribe = source.subscribe.bind(source);
1535
- return {
1536
- getSnapshot: () => getSnapshot(),
1537
- subscribe: (listener) => subscribe(listener)
1538
- };
1539
- }
1540
- function summarizeSessionCache(window) {
1541
- const snapshot = record(window);
1542
- let stats = {
1543
- responses: 0,
1544
- missingUsage: 0,
1545
- unattributed: 0,
1546
- inputTokens: 0,
1547
- cacheReadTokens: 0,
1548
- cacheWriteTokens: 0,
1549
- hasMore: snapshot?.hasMore === true
1550
- };
1551
- let provider;
1552
- const seen = /* @__PURE__ */ new Set();
1553
- for (const entry of Array.isArray(snapshot?.entries) ? snapshot.entries : []) {
1554
- const row = record(entry);
1555
- const event = record(row?.event);
1556
- if (row?.type !== "event" || !event || !tokens(event.seq) || seen.has(event.seq)) continue;
1557
- seen.add(event.seq);
1558
- const data = record(event.data);
1559
- if (event.type === "request/header") provider = record(record(data?.header)?.config)?.provider;
1560
- if (event.type === "session/end-seed" && data?.inherited === true) {
1561
- stats = {
1562
- ...stats,
1563
- responses: 0,
1564
- missingUsage: 0,
1565
- unattributed: 0,
1566
- inputTokens: 0,
1567
- cacheReadTokens: 0,
1568
- cacheWriteTokens: 0
1569
- };
1570
- }
1571
- if (event.type !== "assistant/message") continue;
1572
- if (provider === void 0) {
1573
- stats.unattributed++;
1574
- continue;
1575
- }
1576
- if (provider !== "opencode-go") continue;
1577
- const usage = record(data?.usage);
1578
- const input = usage?.inputTokens;
1579
- const read = usage?.cacheReadTokens === void 0 ? 0 : usage.cacheReadTokens;
1580
- const write = usage?.cacheWriteTokens === void 0 ? 0 : usage.cacheWriteTokens;
1581
- if (!tokens(input) || !tokens(read) || !tokens(write) || !Number.isSafeInteger(stats.inputTokens + input + read + write)) {
1582
- stats.missingUsage++;
1583
- continue;
1584
- }
1585
- stats.responses++;
1586
- stats.inputTokens += input + read + write;
1587
- stats.cacheReadTokens += read;
1588
- stats.cacheWriteTokens += write;
1589
- }
1590
- return stats;
1591
- }
1592
- function sessionCacheSource(events) {
1593
- let window;
1594
- let value;
1595
- return {
1596
- subscribe: (listener) => events.subscribe(listener),
1597
- getSnapshot: () => {
1598
- const next = events.getSnapshot();
1599
- if (value !== void 0 && next === window) return value;
1600
- const previous = record(window);
1601
- window = next;
1602
- const current = record(next);
1603
- const change = record(current?.change);
1604
- if (value !== void 0 && typeof previous?.revision === "number" && current?.revision === previous.revision + 1 && change?.kind === "append" && Array.isArray(change.entries) && change.entries.every((entry) => record(entry)?.type === "transient")) return value;
1605
- const stats = summarizeSessionCache(next);
1606
- if (value === void 0 || Object.keys(stats).some((key) => stats[key] !== value[key])) value = stats;
1607
- return value;
1608
- }
1609
- };
1610
- }
1611
-
1612
- // plugin-css:/private/var/folders/4z/q9yrksqd0y12spqrxkv0p_dm0000gn/T/dsh-release-0.1.13-Tf3MEL/source/src/client/UsagePill.module.css
1496
+ // plugin-css:/Users/tanglei/.codex/worktrees/release-0-1-14/dsh-opencode-go/src/client/UsagePill.module.css
1613
1497
  var id2 = "dsh-opencode-go/UsagePill.module.css";
1614
1498
  if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]")) {
1615
1499
  const style = document.createElement("style");
1616
1500
  style.dataset.plugin = "dsh-opencode-go";
1617
1501
  style.dataset.pluginCss = id2;
1618
- style.textContent = ".xXzfkq_root{min-width:0;display:inline-flex;position:relative}.xXzfkq_trigger{color:inherit;opacity:.75;font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:4px 6px;font-size:12px}.xXzfkq_trigger:hover,.xXzfkq_trigger:focus-visible{opacity:1;background:color-mix(in srgb, currentColor 8%, transparent)}.xXzfkq_panel{z-index:100;box-sizing:border-box;border:1px solid color-mix(in srgb, currentColor 18%, transparent);width:280px;max-width:calc(100vw - 32px);max-height:70vh;color:var(--dsw-alias-label-primary,CanvasText);background:var(--dsw-specific-menu,Canvas);border-radius:12px;padding:16px;font-size:12px;position:absolute;bottom:calc(100% + 12px);right:0;overflow-y:auto;box-shadow:0 8px 30px #0002}.xXzfkq_hint{opacity:.65;font-size:11px;line-height:1.6}.xXzfkq_warning{background:color-mix(in srgb, currentColor 6%, transparent);overflow-wrap:anywhere;border-radius:6px;margin:10px 0;padding:10px;line-height:1.5}.xXzfkq_warning p{margin:5px 0 0}.xXzfkq_retry{border:1px solid color-mix(in srgb, currentColor 25%, transparent);color:inherit;font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:5px 9px}.xXzfkq_retry:disabled{opacity:.6;cursor:default}.xXzfkq_retry:focus-visible{outline-offset:2px;outline:2px solid}.xXzfkq_window{margin-top:14px}.xXzfkq_row{justify-content:space-between;margin-bottom:5px;display:flex}.xXzfkq_cache{border-top:1px solid color-mix(in srgb, currentColor 18%, transparent);margin-top:16px;padding-top:12px}.xXzfkq_cache>strong{margin-bottom:10px;display:block}.xXzfkq_stats{grid-template-columns:1fr auto;gap:5px 10px;display:grid}.xXzfkq_stats dd{text-align:right;font-variant-numeric:tabular-nums;margin:0}.xXzfkq_window progress{appearance:none;background:color-mix(in srgb, currentColor 15%, transparent);border:0;border-radius:4px;width:100%;height:5px;margin-bottom:5px;display:block}.xXzfkq_window progress::-webkit-progress-bar{background:0 0;border-radius:4px}.xXzfkq_window progress::-webkit-progress-value{background:#39a878;border-radius:4px}.xXzfkq_window progress::-moz-progress-bar{background:#39a878;border-radius:4px}";
1502
+ style.textContent = ".XD2TAq_root{min-width:0;display:inline-flex;position:relative}.XD2TAq_trigger{color:inherit;opacity:.75;font:inherit;cursor:pointer;white-space:nowrap;background:0 0;border:0;border-radius:6px;padding:4px 6px;font-size:12px}.XD2TAq_trigger:hover,.XD2TAq_trigger:focus-visible{opacity:1;background:color-mix(in srgb, currentColor 8%, transparent)}.XD2TAq_panel{z-index:100;box-sizing:border-box;border:1px solid color-mix(in srgb, currentColor 18%, transparent);width:280px;max-width:calc(100vw - 32px);max-height:70vh;color:var(--dsw-alias-label-primary,CanvasText);background:var(--dsw-specific-menu,Canvas);border-radius:12px;padding:16px;font-size:12px;position:absolute;bottom:calc(100% + 12px);right:0;overflow-y:auto;box-shadow:0 8px 30px #0002}.XD2TAq_hint{opacity:.65;font-size:11px;line-height:1.6}.XD2TAq_warning{background:color-mix(in srgb, currentColor 6%, transparent);overflow-wrap:anywhere;border-radius:6px;margin:10px 0;padding:10px;line-height:1.5}.XD2TAq_warning p{margin:5px 0 0}.XD2TAq_retry{border:1px solid color-mix(in srgb, currentColor 25%, transparent);color:inherit;font:inherit;cursor:pointer;background:0 0;border-radius:6px;padding:5px 9px}.XD2TAq_retry:disabled{opacity:.6;cursor:default}.XD2TAq_retry:focus-visible{outline-offset:2px;outline:2px solid}.XD2TAq_window{margin-top:14px}.XD2TAq_row{justify-content:space-between;margin-bottom:5px;display:flex}.XD2TAq_window progress{appearance:none;background:color-mix(in srgb, currentColor 15%, transparent);border:0;border-radius:4px;width:100%;height:5px;margin-bottom:5px;display:block}.XD2TAq_window progress::-webkit-progress-bar{background:0 0;border-radius:4px}.XD2TAq_window progress::-webkit-progress-value{background:#39a878;border-radius:4px}.XD2TAq_window progress::-moz-progress-bar{background:#39a878;border-radius:4px}";
1619
1503
  document.head.appendChild(style);
1620
1504
  }
1621
- var UsagePill_default = { "warning": "xXzfkq_warning", "stats": "xXzfkq_stats", "trigger": "xXzfkq_trigger", "hint": "xXzfkq_hint", "panel": "xXzfkq_panel", "row": "xXzfkq_row", "window": "xXzfkq_window", "root": "xXzfkq_root", "retry": "xXzfkq_retry", "cache": "xXzfkq_cache" };
1622
-
1623
- // src/client/SessionCache.tsx
1624
- var import_jsx_runtime3 = require("react/jsx-runtime");
1625
- function SessionCache({ events, t, getLocale }) {
1626
- const source = (0, import_react3.useMemo)(() => sessionCacheSource(events), [events]);
1627
- const stats = (0, import_react3.useSyncExternalStore)(source.subscribe, source.getSnapshot, source.getSnapshot);
1628
- const format = new Intl.NumberFormat(getLocale?.());
1629
- const ratio = stats.inputTokens > 0 ? new Intl.NumberFormat(getLocale?.(), { style: "percent", maximumFractionDigits: 1 }).format(stats.cacheReadTokens / stats.inputTokens) : "\u2014";
1630
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { className: UsagePill_default.cache, "aria-label": t("cacheTitle"), children: [
1631
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: t("cacheTitle") }),
1632
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.row, children: [
1633
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t("cacheReadRatio") }),
1634
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: ratio })
1635
- ] }),
1636
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("dl", { className: UsagePill_default.stats, children: [
1637
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dt", { children: t("cacheReadTokens") }),
1638
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dd", { children: format.format(stats.cacheReadTokens) }),
1639
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dt", { children: t("cacheInputTokens") }),
1640
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dd", { children: format.format(stats.inputTokens) }),
1641
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dt", { children: t("cacheWriteTokens") }),
1642
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dd", { children: format.format(stats.cacheWriteTokens) }),
1643
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dt", { children: t("cacheResponses") }),
1644
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("dd", { children: format.format(stats.responses) })
1645
- ] }),
1646
- stats.responses === 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: UsagePill_default.hint, children: t("cacheEmpty") }),
1647
- stats.missingUsage + stats.unattributed > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: UsagePill_default.hint, children: [
1648
- t("cacheExcluded"),
1649
- " ",
1650
- format.format(stats.missingUsage + stats.unattributed)
1651
- ] }),
1652
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: UsagePill_default.hint, children: [
1653
- t("cacheScope"),
1654
- stats.hasMore ? ` ${t("cacheMore")}` : ""
1655
- ] }),
1656
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: UsagePill_default.hint, children: t("cacheRoutingHint") })
1657
- ] });
1658
- }
1505
+ var UsagePill_default = { "root": "XD2TAq_root", "hint": "XD2TAq_hint", "window": "XD2TAq_window", "trigger": "XD2TAq_trigger", "panel": "XD2TAq_panel", "warning": "XD2TAq_warning", "row": "XD2TAq_row", "retry": "XD2TAq_retry" };
1659
1506
 
1660
1507
  // src/client/UsagePill.tsx
1661
- var import_jsx_runtime4 = require("react/jsx-runtime");
1508
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1662
1509
  function usageFailure(error) {
1663
1510
  if (error && typeof error === "object" && "code" in error && error.code === "opencode-go/usage-unavailable" && "details" in error && error.details && typeof error.details === "object") {
1664
1511
  const details = error.details;
@@ -1671,18 +1518,18 @@ function usageFailure(error) {
1671
1518
  return { retainPrevious: false };
1672
1519
  }
1673
1520
  function UsagePill({ directory, ...props }) {
1674
- const state = (0, import_react4.useSyncExternalStore)(directory.subscribe, directory.getSnapshot, directory.getSnapshot);
1675
- return state.current?.provider === "opencode-go" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ActiveUsage, { ...props }) : null;
1521
+ const state = (0, import_react3.useSyncExternalStore)(directory.subscribe, directory.getSnapshot, directory.getSnapshot);
1522
+ return state.current?.provider === "opencode-go" ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(ActiveUsage, { ...props }) : null;
1676
1523
  }
1677
- function ActiveUsage({ readUsage, t, getLocale, sessionEvents }) {
1678
- const [snapshot, setSnapshot] = (0, import_react4.useState)(null);
1679
- const [failed, setFailed] = (0, import_react4.useState)(null);
1680
- const [refreshing, setRefreshing] = (0, import_react4.useState)(false);
1681
- const [open, setOpen] = (0, import_react4.useState)(false);
1682
- const root = (0, import_react4.useRef)(null);
1683
- const retry = (0, import_react4.useRef)(() => {
1524
+ function ActiveUsage({ readUsage, t, getLocale }) {
1525
+ const [snapshot, setSnapshot] = (0, import_react3.useState)(null);
1526
+ const [failed, setFailed] = (0, import_react3.useState)(null);
1527
+ const [refreshing, setRefreshing] = (0, import_react3.useState)(false);
1528
+ const [open, setOpen] = (0, import_react3.useState)(false);
1529
+ const root = (0, import_react3.useRef)(null);
1530
+ const retry = (0, import_react3.useRef)(() => {
1684
1531
  });
1685
- (0, import_react4.useEffect)(() => {
1532
+ (0, import_react3.useEffect)(() => {
1686
1533
  let alive = true;
1687
1534
  let busy = false;
1688
1535
  setSnapshot(null);
@@ -1728,7 +1575,7 @@ function ActiveUsage({ readUsage, t, getLocale, sessionEvents }) {
1728
1575
  document.removeEventListener("visibilitychange", visible);
1729
1576
  };
1730
1577
  }, [readUsage]);
1731
- (0, import_react4.useEffect)(() => {
1578
+ (0, import_react3.useEffect)(() => {
1732
1579
  if (!open) return;
1733
1580
  const click = (event) => {
1734
1581
  if (!root.current?.contains(event.target)) setOpen(false);
@@ -1747,8 +1594,8 @@ function ActiveUsage({ readUsage, t, getLocale, sessionEvents }) {
1747
1594
  const usage = current?.usage;
1748
1595
  const failure = failed?.reader === readUsage ? failed.failure : null;
1749
1596
  const label = usage ? `Go \xB7 ${t("usageRollingShort")} ${usage.rolling.percent}% \xB7 ${t("usageWeekShort")} ${usage.weekly.percent}%${failure ? ` \xB7 ${t("usageStaleShort")}` : ""}` : `Go \xB7 ${failure ? t("usageUnavailable") : "\u2026"}`;
1750
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: UsagePill_default.root, ref: root, children: [
1751
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1597
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("span", { className: UsagePill_default.root, ref: root, children: [
1598
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1752
1599
  "button",
1753
1600
  {
1754
1601
  type: "button",
@@ -1762,15 +1609,15 @@ function ActiveUsage({ readUsage, t, getLocale, sessionEvents }) {
1762
1609
  children: label
1763
1610
  }
1764
1611
  ),
1765
- open && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: UsagePill_default.panel, role: "dialog", "aria-label": t("usageTitle"), "aria-busy": refreshing, children: [
1766
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: t("usageTitle") }),
1767
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: UsagePill_default.hint, children: t("usageHint") }),
1768
- failure ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: UsagePill_default.warning, role: "alert", children: [
1769
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: t("usageRefreshFailed") }),
1770
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: failure.message ?? t("usageUnavailable") }),
1771
- usage ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: t("usageStaleHint") }) : null
1612
+ open && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.panel, role: "dialog", "aria-label": t("usageTitle"), "aria-busy": refreshing, children: [
1613
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: t("usageTitle") }),
1614
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: UsagePill_default.hint, children: t("usageHint") }),
1615
+ failure ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.warning, role: "alert", children: [
1616
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: t("usageRefreshFailed") }),
1617
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: failure.message ?? t("usageUnavailable") }),
1618
+ usage ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: t("usageStaleHint") }) : null
1772
1619
  ] }) : null,
1773
- failure ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1620
+ failure ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1774
1621
  "button",
1775
1622
  {
1776
1623
  type: "button",
@@ -1782,28 +1629,27 @@ function ActiveUsage({ readUsage, t, getLocale, sessionEvents }) {
1782
1629
  children: t(refreshing ? "usageRefreshing" : "usageRetry")
1783
1630
  }
1784
1631
  ) : null,
1785
- current ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { className: UsagePill_default.hint, children: [
1632
+ current ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { className: UsagePill_default.hint, children: [
1786
1633
  t("usageLastUpdated"),
1787
1634
  " ",
1788
1635
  new Date(current.updatedAt).toLocaleString(getLocale?.())
1789
1636
  ] }) : null,
1790
- usage ? ["rolling", "weekly", "monthly"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: UsagePill_default.window, children: [
1791
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: UsagePill_default.row, children: [
1792
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: t(`usage_${key}`) }),
1793
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("strong", { children: [
1637
+ usage ? ["rolling", "weekly", "monthly"].map((key) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.window, children: [
1638
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.row, children: [
1639
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { children: t(`usage_${key}`) }),
1640
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("strong", { children: [
1794
1641
  usage[key].percent,
1795
1642
  "%"
1796
1643
  ] })
1797
1644
  ] }),
1798
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("progress", { "aria-label": t(`usage_${key}`), max: 100, value: Math.min(100, usage[key].percent) }),
1799
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: UsagePill_default.hint, children: [
1645
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("progress", { "aria-label": t(`usage_${key}`), max: 100, value: Math.min(100, usage[key].percent) }),
1646
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: UsagePill_default.hint, children: [
1800
1647
  t("usageResets"),
1801
1648
  " ",
1802
1649
  new Date(usage[key].resetsAt).toLocaleString(getLocale?.())
1803
1650
  ] }),
1804
- usage[key].status === "rate-limited" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { children: t("usageLimited") })
1805
- ] }, key)) : failure ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: t("usageLoading") }),
1806
- sessionEvents && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SessionCache, { events: sessionEvents, t, getLocale })
1651
+ usage[key].status === "rate-limited" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { children: t("usageLimited") })
1652
+ ] }, key)) : failure ? null : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { children: t("usageLoading") })
1807
1653
  ] })
1808
1654
  ] });
1809
1655
  }
@@ -1824,7 +1670,6 @@ function registerUsagePill(ctx) {
1824
1670
  order: 1e3,
1825
1671
  inject: (sessionId) => ({
1826
1672
  directory: ready.modelDirectories.directoryFor(sessionId).store,
1827
- sessionEvents: sessionEventSource(ready.get("sessions"), sessionId),
1828
1673
  readUsage,
1829
1674
  getLocale: () => ready.locale.getLocale().active,
1830
1675
  t: (key) => translate(key)
package/lib/index.js CHANGED
@@ -312,8 +312,12 @@ function toolMessage(message) {
312
312
  if (message.role !== "tool") return void 0;
313
313
  return message;
314
314
  }
315
- function assertSupportedImageRoles(messages) {
315
+ function assertSupportedHistory(messages) {
316
316
  for (const message of messages) {
317
+ if (message.role === "developer") throw new LlmError3("Developer messages are not supported yet", "UNSUPPORTED_CONTENT");
318
+ if (message.content.some((block) => block.type === "tool-addition" || block.type === "tool-removal")) {
319
+ throw new LlmError3("Tool-change blocks require developer role", "UNSUPPORTED_CONTENT");
320
+ }
317
321
  if (message.role !== "user" && !toolMessage(message) && contentHasImage(message.content)) {
318
322
  throw new LlmError3(
319
323
  `pi-ai cannot represent an image in an in-history ${message.role} message`,
@@ -382,6 +386,9 @@ async function prepareRequestImages(messages, attachments, budget, signal) {
382
386
  return versions;
383
387
  }
384
388
  function toolsOf(options) {
389
+ if (options.tools?.some((tool) => tool.deferLoading === true)) {
390
+ throw new LlmError3("Deferred tool loading is not supported yet", "UNSUPPORTED_CONTENT");
391
+ }
385
392
  return options.tools?.map((tool) => ({
386
393
  name: tool.name,
387
394
  description: tool.description,
@@ -413,7 +420,7 @@ function appendAssistant(message, messages, toolNames, onReplayDegrade) {
413
420
  messages.push(assistant);
414
421
  }
415
422
  function textOnlyContext(options, onReplayDegrade) {
416
- assertSupportedImageRoles(options.messages);
423
+ assertSupportedHistory(options.messages);
417
424
  const split = splitSystemPrompt(options);
418
425
  const toolNames = /* @__PURE__ */ new Map();
419
426
  const messages = [];
@@ -476,7 +483,7 @@ async function toPiContextWithImages(options, images, onReplayDegrade) {
476
483
  maxPixels: DEFAULT_REQUEST_IMAGE_PIXEL_BUDGET,
477
484
  maxBytes: DEFAULT_REQUEST_IMAGE_MAX_BYTES
478
485
  };
479
- assertSupportedImageRoles(options.messages);
486
+ assertSupportedHistory(options.messages);
480
487
  const split = splitSystemPrompt(options);
481
488
  const projection = {
482
489
  maxBytes: maxRequestImageBytes,
@@ -1,13 +1,11 @@
1
1
  import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
2
2
  import type { ModelDirectoryState } from '@deepseek-ai/dsh-client-ui-model-selection/client';
3
3
  import type { GoUsage } from '../usage-contract.ts';
4
- import type { SessionEventSource } from './session-cache.ts';
5
4
  export interface UsagePillProps {
6
5
  directory: SnapshotStore<ModelDirectoryState>;
7
6
  readUsage: () => Promise<GoUsage>;
8
7
  t: (key: string) => string;
9
8
  getLocale?: () => string;
10
- sessionEvents?: SessionEventSource;
11
9
  }
12
10
  /** Only the selected Go provider mounts a poller, so other models send no usage traffic. */
13
11
  export declare function UsagePill({ directory, ...props }: UsagePillProps): import("react").JSX.Element | null;
@@ -1,16 +1,5 @@
1
1
  /** Copy dictionaries for the OpenCode Go settings page. English is the key-set source of truth. */
2
2
  export declare const en: {
3
- cacheTitle: string;
4
- cacheReadRatio: string;
5
- cacheReadTokens: string;
6
- cacheInputTokens: string;
7
- cacheWriteTokens: string;
8
- cacheResponses: string;
9
- cacheEmpty: string;
10
- cacheExcluded: string;
11
- cacheScope: string;
12
- cacheMore: string;
13
- cacheRoutingHint: string;
14
3
  usageTitle: string;
15
4
  usageRollingShort: string;
16
5
  usageHint: string;
@@ -31,7 +31,7 @@ export interface PiImageRequestBudget {
31
31
  * @param images - absent; selects the synchronous conversion.
32
32
  * @param onReplayDegrade - forwarded to {@link toPiAssistant} for each assistant message.
33
33
  * @returns the pi-ai context; `tools` is omitted when the request declares none.
34
- * @throws {LlmError} `UNSUPPORTED_CONTENT` for images in any history role, including a leading system message.
34
+ * @throws {LlmError} `UNSUPPORTED_CONTENT` for a `developer` history message, a tool-change block, a deferred tool, or an image in any history role, including a leading system message.
35
35
  */
36
36
  export declare function toPiContext(options: GenerateOptions, images?: undefined, onReplayDegrade?: (reason: string) => void): PiContext;
37
37
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-opencode-go",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "OpenCode Go model provider and settings UI for DeepSeek Harness",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,6 +0,0 @@
1
- import { type SessionEventSource } from './session-cache.ts';
2
- export declare function SessionCache({ events, t, getLocale }: {
3
- events: SessionEventSource;
4
- t: (key: string) => string;
5
- getLocale?: () => string;
6
- }): import("react").JSX.Element;
@@ -1,18 +0,0 @@
1
- import type { ObservableSnapshot } from '@deepseek-ai/dsh-client-store';
2
- /** Small structural face of the optional Host session event feed, across Host versions. */
3
- export type SessionEventSource = ObservableSnapshot<unknown>;
4
- export interface SessionCacheStats {
5
- responses: number;
6
- missingUsage: number;
7
- unattributed: number;
8
- inputTokens: number;
9
- cacheReadTokens: number;
10
- cacheWriteTokens: number;
11
- hasMore: boolean;
12
- }
13
- /** Use only already-loaded history; never open a session or fetch more pages for diagnostics. */
14
- export declare function sessionEventSource(sessions: unknown, sessionId: string): SessionEventSource | undefined;
15
- /** Aggregate settled Go responses. Pi-ai's input excludes cache reads AND writes. */
16
- export declare function summarizeSessionCache(window: unknown): SessionCacheStats;
17
- /** Stable snapshots avoid rendering on unrelated live chunks; history replacement recomputes. */
18
- export declare function sessionCacheSource(events: SessionEventSource): ObservableSnapshot<SessionCacheStats>;