@dan-ai-studio/dshopencodego 0.1.80 → 0.1.82

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
@@ -144,7 +144,7 @@ The API key comes from the Harness credential store (reference name `OPENCODE_GO
144
144
  - **Two entries, one page**:
145
145
  - **A sidebar icon → a full page in the main column**: every request across sessions. The log is a process-wide thing, so the sidebar is where it belongs — a fixed, always-visible spot, and the same pattern the Harness's own plugin manager uses.
146
146
  - **A "Request log" View inside the conversation**: this session's requests, in place — answering "why was that turn slow" without leaving the chat.
147
- - It is a page, not a floating card: the head, the summary (requests / failures / mean latency / mean first token / mean tok/s / mean cache hit / connection reuse) and the filters stay put while the table scrolls. Each row carries time, model, session, input/output/cache, **cache hit**, **tok/s**, first token, latency and status; clicking one expands its HTTP detail (method, protocol, URL, body size, **preparation**, redacted headers, request shape, result and usage, reasoning split, cache writes, per-request cost, content span). **Preparation** is the local time from request start to the HTTP send — large means the slowness is local (cold catalog / images / history conversion); small with a large first token means the gateway / model side is slow. Filters: model, result, slow requests, session; groups: session, model, result; **click a column head to sort by it**, click again to flip.
147
+ - It is a page, not a floating card: the head, the summary (requests / failures / mean latency / mean first token / mean tok/s / mean cache hit / connection reuse) and the filters stay put while the table scrolls. Each row carries time, model, session, input/output/cache, **cache hit**, **tok/s**, first token, latency and status; clicking one expands its HTTP detail (first a **request timeline** — preparation → waiting for first token → streaming, plus the end tick, with per-segment milliseconds in the legend; then method, protocol, URL, body size, **preparation**, redacted headers, request shape, result and usage, reasoning split, cache writes, per-request cost, content span). **Preparation** is the local time from request start to the HTTP send — large means the slowness is local (cold catalog / images / history conversion); small with a large first token means the gateway / model side is slow. A dedicated sortable `Prep` column sits next to first token. Filters: model, result, slow requests, session; groups: session, model, result; **click a column head to sort by it**, click again to flip.
148
148
  - **tok/s** is the output rate over the decode window (first token to the end), on the same scale as the Harness's own per-turn reading (reasoning tokens included). A request that produced no token has no rate — none is invented. **A burst delivery has no rate either**: when the gateway holds a whole turn and flushes it at the end (common with reasoning models: 8 seconds of thinking invisible on the wire, then a hundred tokens in a few milliseconds), a sub-100ms content span is delivery shape, not generation speed — it shows as — and stays out of the mean.
149
149
  - The session column names the **session title** (from the session catalog; falls back to the id, whose full spelling stays one hover away). The session filter matches titles and ids; session groups are headed the same way. **Clicking a title opens that session** (an archived one refuses, and says so).
150
150
  - **Cache hit** is the share of input tokens served from cache (cache reads over uncached input plus cache reads). No reported input means no share — none is invented either.
package/README.md CHANGED
@@ -143,7 +143,7 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
143
143
  - **两个入口,同一个页面**:
144
144
  - **侧边栏图标 → 主区域整页**:跨会话看全部请求。日志是进程级的数据,放在侧边栏最合理——位置固定、常驻可见,和 DSH 自己的插件管理器同一个范式。
145
145
  - **会话里的「请求日志」标签页**:就地看**本会话**的请求,回答"刚才那轮为什么慢"不用离开对话。
146
- - 页面是一整页而不是浮层:标题、统计(请求数/失败数/平均耗时/平均首字/平均 tok/s/平均缓存命中/连接复用率)、筛选栏固定在上方,只有表格自己滚动。每行含时间、模型、会话、输入/输出/缓存、**缓存命中**、**tok/s**、首字、耗时与状态;点一行展开 HTTP 明细(方法、协议、地址、请求体大小、**准备耗时**、脱敏后的请求头、请求构成、结果与用量、推理拆分、缓存写入、单轮花费、内容跨度)。**准备耗时**是从请求开始到 HTTP 发出的本地耗时——它大是本地(目录冷启动/图片/历史转换)慢,它小但首字大是网关/模型侧慢。筛选有模型/结果/慢请求/会话,分组有会话/模型/结果;**点列头按该列排序**,再点一次反向。
146
+ - 页面是一整页而不是浮层:标题、统计(请求数/失败数/平均耗时/平均首字/平均 tok/s/平均缓存命中/连接复用率)、筛选栏固定在上方,只有表格自己滚动。每行含时间、模型、会话、输入/输出/缓存、**缓存命中**、**tok/s**、首字、耗时与状态;点一行展开 HTTP 明细(第一行是**请求时间线**:准备 → 等待首字 → 输出三段加结束刻度,图例带每段毫秒数;方法、协议、地址、请求体大小、**准备耗时**、脱敏后的请求头、请求构成、结果与用量、推理拆分、缓存写入、单轮花费、内容跨度)。**准备耗时**是从请求开始到 HTTP 发出的本地耗时——它大是本地(目录冷启动/图片/历史转换)慢,它小但首字大是网关/模型侧慢;另有独立的 `准备` 列可点击排序。筛选有模型/结果/慢请求/会话,分组有会话/模型/结果;**点列头按该列排序**,再点一次反向。
147
147
  - **tok/s** 是**首字到结束**这段解码窗口上的输出速率,口径与 Harness 自己的每轮读数一致(含推理 token)。没有 token 的请求没有速率——不编数。**突发推送同样没有速率**:网关把一整轮攒到最后一次性推过来时(推理模型常见:8 秒思考在 wire 上不可见,然后上百个 token 几毫秒内到达),内容跨度不足 100ms,算出来的"每秒几万 token"不是生成速度——显示为 —,也不计入平均。
148
148
  - 会话列显示**会话标题**(标题来自会话目录;拿不到时回退显示 id,悬停可见完整 id)。会话筛选同时匹配标题与 id;按会话分组的组头同样显示标题。**点标题直接打开那个会话**(归档的打不开,会明说)。
149
149
  - **缓存命中**是输入 token 里走缓存的占比(缓存读取/(未缓存输入+缓存读取))。网关没报输入就没有占比——同样不编数。
package/lib/client.js CHANGED
@@ -565,6 +565,11 @@ var en = {
565
565
  requestLogColCacheRate: "Cache hit",
566
566
  requestLogColLatency: "Latency",
567
567
  requestLogColTps: "tok/s",
568
+ requestLogColPrep: "Prep",
569
+ requestLogColWait: "Wait",
570
+ requestLogTimelineWait: "Waiting for first token",
571
+ requestLogTimelineStream: "Streaming",
572
+ requestLogTimelineBurst: "Delivered in one burst",
568
573
  requestLogColStream: "Content span",
569
574
  requestLogColFirstToken: "First token",
570
575
  requestLogColStatus: "Status",
@@ -765,6 +770,11 @@ var zh = {
765
770
  requestLogColCacheRate: "\u7F13\u5B58\u547D\u4E2D",
766
771
  requestLogColLatency: "\u8017\u65F6",
767
772
  requestLogColTps: "tok/s",
773
+ requestLogColPrep: "\u51C6\u5907",
774
+ requestLogColWait: "\u7B49\u5F85",
775
+ requestLogTimelineWait: "\u7B49\u5F85\u9996\u5B57",
776
+ requestLogTimelineStream: "\u8F93\u51FA\u4E2D",
777
+ requestLogTimelineBurst: "\u6574\u8F6E\u4E00\u6B21\u9001\u8FBE",
768
778
  requestLogColStream: "\u5185\u5BB9\u8DE8\u5EA6",
769
779
  requestLogColFirstToken: "\u9996\u5B57",
770
780
  requestLogColStatus: "\u72B6\u6001",
@@ -2142,16 +2152,19 @@ function Section({ controller, t, getLocale, openRequestLog }) {
2142
2152
  var import_react2 = require("react");
2143
2153
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
2144
2154
 
2155
+ // src/request-log/store.ts
2156
+ var MIN_STREAM_MS_FOR_RATE = 100;
2157
+
2145
2158
  // plugin-css:/home/runner/work/dshopencodego/dshopencodego/src/client/request-log.module.css
2146
2159
  var id2 = "@dan-ai-studio/dshopencodego/request-log.module.css";
2147
2160
  if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]")) {
2148
2161
  const style = document.createElement("style");
2149
2162
  style.dataset.plugin = "@dan-ai-studio/dshopencodego";
2150
2163
  style.dataset.pluginCss = id2;
2151
- style.textContent = ".Lx7MOq_page{box-sizing:border-box;background:var(--dsw-alias-bg-base);height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}.Lx7MOq_pageLoose{height:auto}.Lx7MOq_pageLoose .Lx7MOq_tableWrap{overflow:visible}.Lx7MOq_pageLoose .Lx7MOq_stuck{z-index:3;background:var(--dsw-alias-bg-base);position:sticky;top:0}.Lx7MOq_pageLoose .Lx7MOq_table th{top:var(--request-log-stuck,0px)}.Lx7MOq_pageLoose .Lx7MOq_foot{z-index:2;background:var(--dsw-alias-bg-base);position:sticky;bottom:0}.Lx7MOq_head{flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:20px clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_headText{min-width:0}.Lx7MOq_title{margin:0;font-size:20px;font-weight:500;line-height:28px}.Lx7MOq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.Lx7MOq_headActions{flex:none;align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_button{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.Lx7MOq_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_button:disabled{cursor:default;opacity:.5}.Lx7MOq_summary{flex-wrap:wrap;flex:none;gap:8px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_card{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:2px;min-width:104px;padding:8px 12px;display:flex}.Lx7MOq_cardLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.Lx7MOq_cardValue{font-variant-numeric:tabular-nums;font-size:16px;font-weight:500;line-height:22px}.Lx7MOq_cardError .Lx7MOq_cardValue{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_cardWarn .Lx7MOq_cardValue{color:var(--dsw-alias-state-warn-primary)}.Lx7MOq_toolbar{flex-wrap:wrap;flex:none;align-items:center;gap:8px 12px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_field{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.Lx7MOq_select,.Lx7MOq_input{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;padding:0 8px;font-size:13px}.Lx7MOq_input{width:160px}.Lx7MOq_inputNumber{width:72px}.Lx7MOq_checkbox{accent-color:var(--dsw-alias-brand-primary)}.Lx7MOq_sessionLink{color:var(--dsw-alias-link);font:inherit;text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.Lx7MOq_sessionLink:hover{text-decoration:underline}.Lx7MOq_sessionText{text-overflow:ellipsis;vertical-align:bottom;max-width:340px;display:inline-block;overflow:hidden}.Lx7MOq_modelText{text-overflow:ellipsis;vertical-align:bottom;max-width:220px;display:inline-block;overflow:hidden}.Lx7MOq_tableWrap{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex:1;min-height:0;margin:0 clamp(16px,3vw,32px);overflow:auto}.Lx7MOq_table{border-collapse:separate;border-spacing:0;width:100%;min-width:940px;font-size:13px;line-height:20px}.Lx7MOq_table th{z-index:1;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);text-align:left;white-space:nowrap;padding:8px 10px;font-size:12px;font-weight:500;position:sticky;top:0}.Lx7MOq_thSortable{cursor:pointer;user-select:none}.Lx7MOq_thSortable:hover{color:var(--dsw-alias-label-primary)}.Lx7MOq_table td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;font-variant-numeric:tabular-nums;padding:6px 10px}.Lx7MOq_table tbody tr:last-child td{border-bottom:none}.Lx7MOq_row{cursor:pointer}.Lx7MOq_row:hover td{background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_rowOk td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-border-l3)}.Lx7MOq_rowSlow td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-warn-primary)}.Lx7MOq_rowFail td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-error-primary)}.Lx7MOq_rowRunning td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary)}.Lx7MOq_rowFail td:last-child,.Lx7MOq_rowSlow td:last-child{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_group td{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding-top:12px;font-weight:500}.Lx7MOq_detail td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:normal;background:var(--dsw-alias-bg-layer-2);padding:12px 10px 16px}.Lx7MOq_detailGrid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 24px;display:grid}.Lx7MOq_detailItem{gap:8px;min-width:0;font-size:13px;line-height:20px;display:flex}.Lx7MOq_detailLabel{color:var(--dsw-alias-label-tertiary);flex:none}.Lx7MOq_detailValue{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary)}.Lx7MOq_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.Lx7MOq_empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:48px 16px}.Lx7MOq_foot{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:12px clamp(16px,3vw,32px) 20px;font-size:12px;display:flex}.Lx7MOq_pager{align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_notice{color:var(--dsw-alias-label-secondary);padding:0 clamp(16px,3vw,32px) 12px;font-size:13px;line-height:20px}.Lx7MOq_noticeError{color:var(--dsw-alias-state-error-primary)}";
2164
+ style.textContent = ".Lx7MOq_page{box-sizing:border-box;background:var(--dsw-alias-bg-base);height:100%;min-height:0;color:var(--dsw-alias-label-primary);flex-direction:column;display:flex}.Lx7MOq_pageLoose{height:auto}.Lx7MOq_pageLoose .Lx7MOq_tableWrap{overflow:visible}.Lx7MOq_pageLoose .Lx7MOq_stuck{z-index:3;background:var(--dsw-alias-bg-base);position:sticky;top:0}.Lx7MOq_pageLoose .Lx7MOq_table th{top:var(--request-log-stuck,0px)}.Lx7MOq_pageLoose .Lx7MOq_foot{z-index:2;background:var(--dsw-alias-bg-base);position:sticky;bottom:0}.Lx7MOq_head{flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:20px clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_headText{min-width:0}.Lx7MOq_title{margin:0;font-size:20px;font-weight:500;line-height:28px}.Lx7MOq_subtitle{color:var(--dsw-alias-label-secondary);margin:4px 0 0;font-size:13px;line-height:20px}.Lx7MOq_headActions{flex:none;align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_button{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;align-items:center;gap:6px;padding:0 12px;font-size:13px;display:inline-flex}.Lx7MOq_button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_button:disabled{cursor:default;opacity:.5}.Lx7MOq_summary{flex-wrap:wrap;flex:none;gap:8px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_card{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex-direction:column;gap:2px;min-width:104px;padding:8px 12px;display:flex}.Lx7MOq_cardLabel{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:16px}.Lx7MOq_cardValue{font-variant-numeric:tabular-nums;font-size:16px;font-weight:500;line-height:22px}.Lx7MOq_cardError .Lx7MOq_cardValue{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_cardWarn .Lx7MOq_cardValue{color:var(--dsw-alias-state-warn-primary)}.Lx7MOq_toolbar{flex-wrap:wrap;flex:none;align-items:center;gap:8px 12px;padding:0 clamp(16px,3vw,32px) 12px;display:flex}.Lx7MOq_field{color:var(--dsw-alias-label-secondary);align-items:center;gap:6px;font-size:13px;line-height:20px;display:inline-flex}.Lx7MOq_select,.Lx7MOq_input{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-sm);background:var(--dsw-alias-bg-layer-1);height:28px;color:var(--dsw-alias-label-primary);font:inherit;padding:0 8px;font-size:13px}.Lx7MOq_input{width:160px}.Lx7MOq_inputNumber{width:72px}.Lx7MOq_checkbox{accent-color:var(--dsw-alias-brand-primary)}.Lx7MOq_sessionLink{color:var(--dsw-alias-link);font:inherit;text-align:left;cursor:pointer;white-space:nowrap;background:0 0;border:none;padding:0}.Lx7MOq_sessionLink:hover{text-decoration:underline}.Lx7MOq_sessionText{text-overflow:ellipsis;vertical-align:bottom;max-width:340px;display:inline-block;overflow:hidden}.Lx7MOq_modelText{text-overflow:ellipsis;vertical-align:bottom;max-width:220px;display:inline-block;overflow:hidden}.Lx7MOq_tableWrap{border:1px solid var(--dsw-alias-border-l2);border-radius:var(--dsw-radius-md);background:var(--dsw-alias-bg-layer-1);flex:1;min-height:0;margin:0 clamp(16px,3vw,32px);overflow:auto}.Lx7MOq_table{border-collapse:separate;border-spacing:0;width:100%;min-width:940px;font-size:13px;line-height:20px}.Lx7MOq_table th{z-index:1;border-bottom:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-tertiary);text-align:left;white-space:nowrap;padding:8px 10px;font-size:12px;font-weight:500;position:sticky;top:0}.Lx7MOq_thSortable{cursor:pointer;user-select:none}.Lx7MOq_thSortable:hover{color:var(--dsw-alias-label-primary)}.Lx7MOq_table td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:nowrap;font-variant-numeric:tabular-nums;padding:6px 10px}.Lx7MOq_table tbody tr:last-child td{border-bottom:none}.Lx7MOq_row{cursor:pointer}.Lx7MOq_row:hover td{background:var(--dsw-alias-interactive-bg-hover)}.Lx7MOq_rowOk td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-border-l3)}.Lx7MOq_rowSlow td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-warn-primary)}.Lx7MOq_rowFail td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-state-error-primary)}.Lx7MOq_rowRunning td:first-child{box-shadow:inset 3px 0 0 var(--dsw-alias-brand-primary)}.Lx7MOq_rowFail td:last-child,.Lx7MOq_rowSlow td:last-child{color:var(--dsw-alias-state-error-primary)}.Lx7MOq_group td{border-bottom:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);padding-top:12px;font-weight:500}.Lx7MOq_detail td{border-bottom:1px solid var(--dsw-alias-border-l1);white-space:normal;background:var(--dsw-alias-bg-layer-2);padding:12px 10px 16px}.Lx7MOq_detailGrid{grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:6px 24px;display:grid}.Lx7MOq_timeline{flex-direction:column;grid-column:1/-1;gap:6px;padding:4px 0 8px;display:flex}.Lx7MOq_timelineTrackWrap{padding-bottom:18px;position:relative}.Lx7MOq_timelineTrack{background:var(--dsw-alias-bg-layer-1);border-radius:999px;height:10px;position:relative;overflow:hidden}.Lx7MOq_timelineMidTick{background:var(--dsw-alias-label-tertiary);opacity:.7;width:1px;position:absolute;top:0;bottom:0;left:50%}.Lx7MOq_timelineMid{color:var(--dsw-alias-label-tertiary);white-space:nowrap;font-variant-numeric:tabular-nums;font-size:11px;line-height:16px;position:absolute;top:100%;transform:translate(-50%)}.Lx7MOq_timelineSeg{min-width:2px;position:absolute;top:0;bottom:0}.Lx7MOq_timelinePrep{background:var(--dsw-alias-brand-primary)}.Lx7MOq_timelineWait{background:var(--dsw-alias-state-warn-primary)}.Lx7MOq_timelineStream{background:var(--dsw-alias-brand-primary);opacity:.45}.Lx7MOq_timelineTick{background:var(--dsw-alias-label-primary);width:2px;margin-left:-2px;position:absolute;top:-2px;bottom:-2px;left:100%}.Lx7MOq_timelineLegend{color:var(--dsw-alias-label-secondary);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 16px;font-size:12px;display:flex}.Lx7MOq_timelineItem{white-space:nowrap;align-items:center;gap:6px;display:inline-flex}.Lx7MOq_timelineSwatch{border-radius:2px;width:8px;height:8px}.Lx7MOq_timelineTickSwatch{background:var(--dsw-alias-label-primary)}.Lx7MOq_detailItem{gap:8px;min-width:0;font-size:13px;line-height:20px;display:flex}.Lx7MOq_detailLabel{color:var(--dsw-alias-label-tertiary);flex:none}.Lx7MOq_detailValue{overflow-wrap:anywhere;min-width:0;color:var(--dsw-alias-label-primary)}.Lx7MOq_mono{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:12px}.Lx7MOq_empty{text-align:center;color:var(--dsw-alias-label-tertiary);padding:48px 16px}.Lx7MOq_foot{color:var(--dsw-alias-label-tertiary);flex-wrap:wrap;flex:none;align-items:center;gap:12px;padding:12px clamp(16px,3vw,32px) 20px;font-size:12px;display:flex}.Lx7MOq_pager{align-items:center;gap:8px;margin-left:auto;display:flex}.Lx7MOq_notice{color:var(--dsw-alias-label-secondary);padding:0 clamp(16px,3vw,32px) 12px;font-size:13px;line-height:20px}.Lx7MOq_noticeError{color:var(--dsw-alias-state-error-primary)}";
2152
2165
  document.head.appendChild(style);
2153
2166
  }
2154
- var request_log_default = { "button": "Lx7MOq_button", "card": "Lx7MOq_card", "cardError": "Lx7MOq_cardError", "cardLabel": "Lx7MOq_cardLabel", "cardValue": "Lx7MOq_cardValue", "cardWarn": "Lx7MOq_cardWarn", "checkbox": "Lx7MOq_checkbox", "detail": "Lx7MOq_detail", "detailGrid": "Lx7MOq_detailGrid", "detailItem": "Lx7MOq_detailItem", "detailLabel": "Lx7MOq_detailLabel", "detailValue": "Lx7MOq_detailValue", "empty": "Lx7MOq_empty", "field": "Lx7MOq_field", "foot": "Lx7MOq_foot", "group": "Lx7MOq_group", "head": "Lx7MOq_head", "headActions": "Lx7MOq_headActions", "headText": "Lx7MOq_headText", "input": "Lx7MOq_input", "inputNumber": "Lx7MOq_inputNumber", "modelText": "Lx7MOq_modelText", "mono": "Lx7MOq_mono", "notice": "Lx7MOq_notice", "noticeError": "Lx7MOq_noticeError", "page": "Lx7MOq_page", "pageLoose": "Lx7MOq_pageLoose", "pager": "Lx7MOq_pager", "row": "Lx7MOq_row", "rowFail": "Lx7MOq_rowFail", "rowOk": "Lx7MOq_rowOk", "rowRunning": "Lx7MOq_rowRunning", "rowSlow": "Lx7MOq_rowSlow", "select": "Lx7MOq_select", "sessionLink": "Lx7MOq_sessionLink", "sessionText": "Lx7MOq_sessionText", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
2167
+ var request_log_default = { "button": "Lx7MOq_button", "card": "Lx7MOq_card", "cardError": "Lx7MOq_cardError", "cardLabel": "Lx7MOq_cardLabel", "cardValue": "Lx7MOq_cardValue", "cardWarn": "Lx7MOq_cardWarn", "checkbox": "Lx7MOq_checkbox", "detail": "Lx7MOq_detail", "detailGrid": "Lx7MOq_detailGrid", "detailItem": "Lx7MOq_detailItem", "detailLabel": "Lx7MOq_detailLabel", "detailValue": "Lx7MOq_detailValue", "empty": "Lx7MOq_empty", "field": "Lx7MOq_field", "foot": "Lx7MOq_foot", "group": "Lx7MOq_group", "head": "Lx7MOq_head", "headActions": "Lx7MOq_headActions", "headText": "Lx7MOq_headText", "input": "Lx7MOq_input", "inputNumber": "Lx7MOq_inputNumber", "modelText": "Lx7MOq_modelText", "mono": "Lx7MOq_mono", "notice": "Lx7MOq_notice", "noticeError": "Lx7MOq_noticeError", "page": "Lx7MOq_page", "pageLoose": "Lx7MOq_pageLoose", "pager": "Lx7MOq_pager", "row": "Lx7MOq_row", "rowFail": "Lx7MOq_rowFail", "rowOk": "Lx7MOq_rowOk", "rowRunning": "Lx7MOq_rowRunning", "rowSlow": "Lx7MOq_rowSlow", "select": "Lx7MOq_select", "sessionLink": "Lx7MOq_sessionLink", "sessionText": "Lx7MOq_sessionText", "stuck": "Lx7MOq_stuck", "subtitle": "Lx7MOq_subtitle", "summary": "Lx7MOq_summary", "table": "Lx7MOq_table", "tableWrap": "Lx7MOq_tableWrap", "thSortable": "Lx7MOq_thSortable", "timeline": "Lx7MOq_timeline", "timelineItem": "Lx7MOq_timelineItem", "timelineLegend": "Lx7MOq_timelineLegend", "timelineMid": "Lx7MOq_timelineMid", "timelineMidTick": "Lx7MOq_timelineMidTick", "timelinePrep": "Lx7MOq_timelinePrep", "timelineSeg": "Lx7MOq_timelineSeg", "timelineStream": "Lx7MOq_timelineStream", "timelineSwatch": "Lx7MOq_timelineSwatch", "timelineTick": "Lx7MOq_timelineTick", "timelineTickSwatch": "Lx7MOq_timelineTickSwatch", "timelineTrack": "Lx7MOq_timelineTrack", "timelineTrackWrap": "Lx7MOq_timelineTrackWrap", "timelineWait": "Lx7MOq_timelineWait", "title": "Lx7MOq_title", "toolbar": "Lx7MOq_toolbar" };
2155
2168
 
2156
2169
  // src/client/RequestLogPage.tsx
2157
2170
  var import_jsx_runtime2 = require("react/jsx-runtime");
@@ -2164,6 +2177,8 @@ var SORT_DEFAULT_DIR = {
2164
2177
  cacheReadTokens: "desc",
2165
2178
  cacheRate: "desc",
2166
2179
  tps: "desc",
2180
+ prepMs: "desc",
2181
+ waitMs: "desc",
2167
2182
  firstTokenMs: "desc",
2168
2183
  durationMs: "desc",
2169
2184
  status: "asc"
@@ -2186,6 +2201,10 @@ function sortValue(entry, key) {
2186
2201
  return cacheRate(entry) ?? -1;
2187
2202
  case "tps":
2188
2203
  return entry.tps ?? -1;
2204
+ case "prepMs":
2205
+ return entry.prepMs ?? -1;
2206
+ case "waitMs":
2207
+ return waitMs(entry) ?? -1;
2189
2208
  case "firstTokenMs":
2190
2209
  return entry.firstTokenMs ?? -1;
2191
2210
  case "durationMs":
@@ -2226,9 +2245,46 @@ function cacheRate(entry) {
2226
2245
  const total = entry.inputTokens + cached;
2227
2246
  return total > 0 ? cached / total : void 0;
2228
2247
  }
2248
+ function timelineOf(entry, t) {
2249
+ const running = entry.running === true;
2250
+ const end = entry.durationMs ?? (running ? Math.max(0, Date.now() - entry.atMs) : void 0);
2251
+ if (end === void 0 || end <= 0) return void 0;
2252
+ const prep = entry.prepMs;
2253
+ const first = entry.firstTokenMs;
2254
+ if (prep === void 0 && first === void 0) return void 0;
2255
+ const start = prep ?? 0;
2256
+ const slices = [];
2257
+ if (prep !== void 0 && prep > 0) {
2258
+ slices.push({ kind: "prep", label: t("requestLogDetailPrep"), from: 0, to: Math.min(prep, end) });
2259
+ }
2260
+ if (first !== void 0) {
2261
+ if (first > start) {
2262
+ slices.push({
2263
+ kind: "wait",
2264
+ label: t("requestLogTimelineWait"),
2265
+ from: Math.min(start, end),
2266
+ to: Math.min(first, end)
2267
+ });
2268
+ }
2269
+ if (entry.streamMs !== void 0 && entry.streamMs > 0) {
2270
+ const last = Math.min(first + entry.streamMs, end);
2271
+ if (last > first) slices.push({ kind: "stream", label: t("requestLogTimelineStream"), from: first, to: last });
2272
+ }
2273
+ }
2274
+ if (slices.length === 0) return void 0;
2275
+ return {
2276
+ slices,
2277
+ end,
2278
+ burst: !running && first !== void 0 && (entry.streamMs === void 0 || entry.streamMs < MIN_STREAM_MS_FOR_RATE)
2279
+ };
2280
+ }
2229
2281
  function percent(value) {
2230
2282
  return value === void 0 ? "\u2014" : `${(value * 100).toFixed(0)}%`;
2231
2283
  }
2284
+ function waitMs(entry) {
2285
+ if (entry.firstTokenMs === void 0) return void 0;
2286
+ return Math.max(0, entry.firstTokenMs - (entry.prepMs ?? 0));
2287
+ }
2232
2288
  function average(values) {
2233
2289
  if (values.length === 0) return void 0;
2234
2290
  let sum = 0;
@@ -2317,7 +2373,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
2317
2373
  if (!hasRunning) return;
2318
2374
  const timer = setInterval(() => {
2319
2375
  void load();
2320
- }, 1e3);
2376
+ }, 2e3);
2321
2377
  return () => {
2322
2378
  clearInterval(timer);
2323
2379
  };
@@ -2378,7 +2434,7 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
2378
2434
  if (group !== void 0 && group !== lastGroup) {
2379
2435
  lastGroup = group;
2380
2436
  out.push(
2381
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.group, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 11, children: groupBy === "session" ? sessionLabel(group).text : groupBy === "result" ? resultGroupLabel(group) : group }) }, `group-${group}-${entry.seq}`)
2437
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.group, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 13, children: groupBy === "session" ? sessionLabel(group).text : groupBy === "result" ? resultGroupLabel(group) : group }) }, `group-${group}-${entry.seq}`)
2382
2438
  );
2383
2439
  }
2384
2440
  const state = entry.running === true ? "rowRunning" : !entry.ok ? "rowFail" : entry.tps !== void 0 && entry.tps < 5 ? "rowSlow" : "rowOk";
@@ -2418,6 +2474,8 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
2418
2474
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: count2(entry.cacheReadTokens) }),
2419
2475
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: percent(cacheRate(entry)) }),
2420
2476
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: rate2(entry.tps) }),
2477
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(entry.prepMs) }),
2478
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(waitMs(entry)) }),
2421
2479
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: duration(entry.firstTokenMs) }),
2422
2480
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.running === true ? duration(Math.max(0, Date.now() - entry.atMs)) : duration(entry.durationMs) }),
2423
2481
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { children: entry.running === true ? `${t("requestLogRunning")} \xB7 ${duration(Math.max(0, Date.now() - entry.atMs))}` : entry.ok ? entry.status === void 0 ? "\u2713" : String(entry.status) : entry.code ?? "\u2717" })
@@ -2428,7 +2486,52 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
2428
2486
  );
2429
2487
  if (expanded === entry.seq) {
2430
2488
  out.push(
2431
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.detail, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 11, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailGrid, children: [
2489
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("tr", { className: request_log_default.detail, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("td", { colSpan: 13, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailGrid, children: [
2490
+ (() => {
2491
+ const timeline = timelineOf(entry, t);
2492
+ if (timeline === void 0) return null;
2493
+ const kindClass = (kind) => kind === "prep" ? request_log_default.timelinePrep : kind === "wait" ? request_log_default.timelineWait : request_log_default.timelineStream;
2494
+ const spoken = [
2495
+ ...timeline.slices.map((slice) => `${slice.label} ${duration(slice.to - slice.from)}`),
2496
+ `${t("requestLogColLatency")} ${duration(timeline.end)}`
2497
+ ].join(", ");
2498
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.timeline, role: "img", "aria-label": spoken, children: [
2499
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.timelineTrackWrap, children: [
2500
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.timelineTrack, children: [
2501
+ timeline.slices.map((slice) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
2502
+ "div",
2503
+ {
2504
+ className: `${request_log_default.timelineSeg} ${kindClass(slice.kind)}`,
2505
+ style: {
2506
+ left: `${(slice.from / timeline.end * 100).toFixed(2)}%`,
2507
+ width: `${Math.max(0, (slice.to - slice.from) / timeline.end * 100).toFixed(2)}%`
2508
+ },
2509
+ title: `${slice.label} ${duration(slice.to - slice.from)}`
2510
+ },
2511
+ slice.kind
2512
+ )),
2513
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: request_log_default.timelineMidTick }),
2514
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: request_log_default.timelineTick, title: duration(timeline.end) })
2515
+ ] }),
2516
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: request_log_default.timelineMid, title: duration(timeline.end / 2), children: duration(timeline.end / 2) })
2517
+ ] }),
2518
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.timelineLegend, children: [
2519
+ timeline.slices.map((slice) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: request_log_default.timelineItem, children: [
2520
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${request_log_default.timelineSwatch} ${kindClass(slice.kind)}` }),
2521
+ slice.label,
2522
+ " ",
2523
+ duration(slice.to - slice.from)
2524
+ ] }, slice.kind)),
2525
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { className: request_log_default.timelineItem, children: [
2526
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${request_log_default.timelineSwatch} ${request_log_default.timelineTickSwatch}` }),
2527
+ t("requestLogColLatency"),
2528
+ " ",
2529
+ duration(timeline.end)
2530
+ ] }),
2531
+ timeline.burst && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.timelineItem, children: t("requestLogTimelineBurst") })
2532
+ ] })
2533
+ ] });
2534
+ })(),
2432
2535
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: request_log_default.detailItem, children: [
2433
2536
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: request_log_default.detailLabel, children: t("requestLogDetailMethod") }),
2434
2537
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: `${request_log_default.detailValue} ${request_log_default.mono}`, children: entry.method })
@@ -2698,6 +2801,8 @@ function RequestLogPage({ readLog, enabled, canOpenSession, openSession, t, getL
2698
2801
  head("cacheReadTokens", t("requestLogColCache"), t("requestLogColCacheHint")),
2699
2802
  head("cacheRate", t("requestLogColCacheRate")),
2700
2803
  head("tps", t("requestLogColTps")),
2804
+ head("prepMs", t("requestLogColPrep")),
2805
+ head("waitMs", t("requestLogColWait")),
2701
2806
  head("firstTokenMs", t("requestLogColFirstToken")),
2702
2807
  head("durationMs", t("requestLogColLatency")),
2703
2808
  head("status", t("requestLogColStatus"))
package/lib/index.js CHANGED
@@ -78134,7 +78134,7 @@ function createUpdateChecker(deps) {
78134
78134
  }
78135
78135
 
78136
78136
  // src/update/version.ts
78137
- var PLUGIN_VERSION = true ? "0.1.80" : "0.0.0-dev";
78137
+ var PLUGIN_VERSION = true ? "0.1.82" : "0.0.0-dev";
78138
78138
 
78139
78139
  // src/update/service.ts
78140
78140
  import { TypertRemoteService as TypertRemoteService4 } from "@deepseek-ai/dsh-typert-protocol";
@@ -175,6 +175,19 @@ export interface RequestLogGates {
175
175
  /** How many entries to keep; the ring trims to it on every append. */
176
176
  readonly maxEntries: () => number;
177
177
  }
178
+ /**
179
+ * Minimum content span for a meaningful rate, in milliseconds.
180
+ *
181
+ * A rate needs a window, and a sub-100ms arrival spread is delivery shape,
182
+ * not generation speed: wall-clock timers resolve to a millisecond, one
183
+ * network flush fans its events out across several event-loop turns, and no
184
+ * served LLM sustains thousands of tokens per second. Below this line the
185
+ * tokens arrived in one burst — the reply's speed on the wire is unknown,
186
+ * so no rate is reported rather than a fabricated one. The viewer reuses
187
+ * the same line to name that shape instead of leaving the missing output
188
+ * slice unexplained.
189
+ */
190
+ export declare const MIN_STREAM_MS_FOR_RATE = 100;
178
191
  /**
179
192
  * An in-flight request, handed back only when the log is enabled.
180
193
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-ai-studio/dshopencodego",
3
- "version": "0.1.80",
3
+ "version": "0.1.82",
4
4
  "description": "OpenCode Go provider for DeepSeek Harness: live gateway catalog, per-conversation session header, usage",
5
5
  "type": "module",
6
6
  "license": "MIT",