Skip to content
dsh.fish
Bundle

dsh-sym

给 DeepSeek Harness 的界面读数层:输入框状态栏的实时会话花费(人民币、按实际厂商/模型与峰谷时段折算)、输入框工具行里模型选择器左侧的 DeepSeek 账户余额、品牌行的峰谷时段标记、把任意一条回复作为上下文引用的 @ 按钮、侧栏右缝里可自定义的快捷按钮条(含设置页),以及侧栏会话菜单里的「复制会话 ID / 导出会话日志」。

Source
seeseeczl
stars
1 stars
License
MIT
Updated
Updated 3 days ago

Readme

# dsh-sym

> **给 DeepSeek Harness 的界面读数层。**
> 实时花费、账户余额、峰谷时段、引用回复 —— 四件事直接叠在 DSH 原生界面上,
> 不新增面板,不打断工作流。

**为什么叫 `dsh-sym`**:`sym` 取自 **symbiote(共生体)** —— 就是毒液(Venom)的本体。
共生体的行为是附着在宿主身上、与宿主共生、把宿主的能力放大;这个名字记录的正是这个插件
和 DSH 的关系,也刻意不限定功能范围 —— 它会长出什么,不由名字预先决定。

<p align="center">
  <img src="assets/composer-cost.png" width="860" alt="输入框状态栏末尾的两个人民币费用读数">
</p>

| 图标 | 功能 | 出现位置 |
|:---:|---|---|
| 💵 | **会话花费** | 输入框状态栏末尾,两个人民币金额 |
| 👛 | **账户余额** | 输入框下方工具行,模型名左边 |
| 🕐 | **峰谷时段** | 品牌行 `deepseek HARNESS` 后面 |
| `@` | **引用回复** | 每条已完成回复的动作行 |
| ▮ | **快捷按钮条** | 侧栏与对话区之间的竖条,内容自己配 |
| ⚙ | **共生体设置** | 设置 → 插件 → 共生体设置 |
| ⑂ | **Git 状态** | 会话头部标题行最前,显示当前分支与工作区改动,点分支名可切分支 |

这些地方互相独立:某一处读不到数据时它自己安静退场,不影响其余几处,也不影响 DSH 本身。

---

## 目录

- [功能](#功能)
- [安装](#安装)
- [计价规则](#计价规则)
- [工作原理](#工作原理)
- [配置](#配置)
- [开发](#开发)
- [已知限制](#已知限制)
- [许可](#许可)

---

## 功能

### 1. 会话花费

输入框下方状态栏的最后,多出两个人民币金额:

```
⏱ 8 轮 355 步 · 255 tok/s   🗄 125M tok · 缓存命中 99.8%   💵 ¥18.03 · ¥0.466   🔲 333M   ⟳ 压缩 2 次   ◐ 61%
                                                          └ 本会话总计  └ 刻度选中那次任务  └ DSH 进程内存  └ 上下文压缩
```

- **💵 本会话总计** —— 这个会话从第一条消息到现在,**所有轮次**累计花了多少。
- **💬 本次任务** —— 你在聊天右侧那条**轮次导航刻度**上选中的那一格,对应的这一轮花了多少。
  在刻度上点选或滚动,这个数字跟着变;只有一轮时不重复显示。
- 金额旁边**不写「第几轮」** —— 是哪一轮由你在刻度上的位置决定;鼠标悬停时才告诉你。
- **悬停任一金额**展开完整账单:厂商、模型、峰时/谷时、单价,以及 cache hit / cache miss / output
  三个桶各自的 token 数与算式,可逐笔核对。
- **⟳ 上下文压缩** —— 本会话压过多少次上下文(compact 把历史换成摘要 + prune 丢掉工具结果,两者合计)。
  **没压过就显示「压缩 0 次」** —— 0 是真实读数(确实还没丢信息),不是缺数据,所以不隐藏;
  压过之后这一格带上警示色。丢失的 token 量只在悬停里给(正文不放,它与决策无关)。
- 字号与行高和同一行的其他统计信息**完全一致**(继承 DSH 的 `--dsh-content-font-size-secondary`)。

计价覆盖 **1046 个模型、42 家厂商**,不只是 DeepSeek —— 详见[计价规则](#计价规则)。

### 2. 账户余额

在输入框下方工具行的右半段,**模型选择器左边**:

```
[+] [权限] [计划]           [钱包] ¥123.45  [模型名 ▾] [活动] [发送]
```

- 走的是 `ctx.remote.account`,和「设置 → 账户」那张卡**同一个官方接口、同一份凭据**。
- 只读 **DeepSeek 账户余额**;不涉及其他厂商,也不做多账户。
- 挂载时读一次,之后每 60 秒刷新;点击立即刷新;读不到时改成每 5 分钟才重试。
- 悬停显示充值余额、赠送余额和上次更新时间。
- 优先显示 CNY 钱包,只有美元钱包时显示 `$`。

> **位置是怎么落上去的**:挂 `conversation.input.right` —— 它和模型选择器同在官方的
> `standardControls` 容器里,官方渲染顺序就是「right 槽 → 模型选择器」,所以余额天然落在
> 模型名正左边,**不需要任何 CSS 去推它**。
>
> 它不改造官方布局:没有 `position: fixed`、没有量 DOM、没有重试定时器。早先的版本把
> 自己注册在会话头部槽里、再用 fixed 把像素画到侧栏底部,还配了一条 CSS 把官方 footer
> 从竖排改成横排 —— 那条规则帮不上脱流的 fixed 元素,唯一的实际效果是把官方账户行推到
> 了右侧,已删除。

### 3. 峰谷时段

<p align="center">
  <img src="assets/peak-tag.png" width="520" alt="品牌行后面跟着一个琥珀色的峰时标记">
</p>

品牌行后面跟着一个状态标记,一眼就能看出**现在贵还是便宜**:

- **峰时** —— 琥珀色描边,DeepSeek 按标准价计费
- **谷时** —— 灰色,DeepSeek 按**半价**计费

悬停显示规则原文。

**刷新时机**:定时器直接算到**下一个边界**,一天只在固定时刻触发 **5 次**
(北京时间 `09:00`、`12:00`、`14:00`、`18:00`、`00:00`),在边界后约半秒翻转。

这是**单次定时器,不是轮询** —— 一天 5 次,比任何固定间隔轮询都省,
而且边界一到就变。窗口重新获得焦点、或从后台切回时还会**立即重新对时**:
浏览器会节流后台标签页的定时器,只靠定时器的话,从睡眠唤醒后标记可能还是旧的。

> **实现方式**:品牌行是官方元素,没有加法插槽。本插件用 CSS 给它的 `::after`
> 接了个标签,内容从根上的 CSS 变量 `--dsh-peak-label` 读,颜色由 `data-dsh-peak` 决定。
> 没有替换任何官方组件;卸载后属性、变量和样式一起消失。

### 4. 文件链接右键菜单

回复里的文件链接(写成 `[短名](/绝对/路径)` 的 Markdown 链接)除点击打开外,
**右键**会弹出一个菜单:

- **复制路径** —— 把磁盘上的绝对路径放进剪贴板
- **在访达中显示** —— 走官方的 `session.openWorkspacePath` 的 `reveal` 动作

只在**文件链接**上接管右键,其他位置的系统菜单原样保留。菜单样式沿用官方
弹出层的变量(`--dsw-specific-menu` / `--dsw-elevation-prominent`)。

> 顺带一条使用约定:给模型看的路径写成**反引号**(只读),给人点的写成
> **Markdown 链接**。裸写路径虽然 DSH 也会自动转成链接,但显示出来是一长串
> URL,比链接难看。

### 5. 引用回复

<p align="center">
  <img src="assets/quote.png" width="820" alt="输入框里出现一个短标记,模型读到的是完整原文">
</p>

每条已完成的回复,动作行里多一个 `@`(在 👍👎 之后)。点一下,输入框里只出现**一个短标记**:

```
@引用#9806056fac67 
```

你接着写新任务、发送即可 —— **模型读到的是那条回复的完整原文**,不是这个标记。
点完 `@` 光标**仍在输入框里**(标记落在原来的插入点之后),不用再点一下输入框。

**怎么做到的**:标记里只有消息 id 的前 12 位,替换发生在**发送时、宿主侧**:

1. 宿主半边一直听着 `session/event`,把每条已完成的回复按 message id 记进一张**有界索引**
   (只留最近 400 条);
2. 消息进入模型请求之前,宿主的 `agent/pre-step` 钩子扫描即将发送的消息,找到
   `@引用#xxxxxxxxxxxx`,从索引取出原文,替换成 Markdown 引用块(每行前缀 `>`);
3. 索引里找不到的标记**原样保留** —— 不报错,也不丢内容。

思路和 DSH 自己注入会话快照(`dsh-session-reference`)的方式一致。整个链路都在本地,不联网。

**几个边界**:

- **只取正文**:`text` 内容块按顺序拼接;推理内容和工具调用**不含**在内 ——
  引用的是结论,不是过程(否则一次带界面操作的回复会拖进去几万字的快照)。
- **只对之后的发送生效**:标记在发送那一刻展开,历史消息不受影响。
- **不动会话结构**:不分支、不新建会话,只是把那段原文带进这一次请求。
- **索引在内存里**:App 重启后索引随会话被读取重新填充;引用一条很久以前、已被淘汰的回复时,
  标记会原样保留。
- 插入成功按钮短暂显示 `✓`;输入框不可用(拿不到插入点)显示 `!`,把光标放进输入框再点一次。

> **为什么不用 DSH 原生的 `@` 引用**:DSH 的引用语法 `@[label](dsh-session:<id>)` 是**会话级**的,
> 最小粒度就是整个会话,没有「引用某一条消息」。所以要精确引用「中途那一次回复」,
> 只能用一个自定义标记,再由宿主在发送时展开。

### 6. 快捷按钮条

侧栏与对话区之间有一条竖排的图形按钮。点一下,按钮里的内容就**填进输入框**,
而且**光标留在输入框里**,接着写就行:

- **官方命令** —— 效果与你从输入框左下角 `+` 菜单里选中同一条命令**完全一致**:
  有参数的命令(目标、计划)在草稿里落成蓝色命令 chip 并提示输入参数;没有参数的命令
  (压缩上下文、权限)点一下直接执行。
- **技能** —— 填 `/技能名 `。末尾那个空格是关键:客户端把「命令 + 空格」认作
  "指令行、开始收参数",和你在 `/` 菜单里选中一条命令后的状态一致。
- **预设提示词** —— 整段文字进草稿,你确认后发送。

三类在竖条上**分组排布,组间有分割线**,图标统一用中性的次级文字色 —— 不按类型着色:
一排小图标各染一色会显得吵,也把颜色从"承担语义"降格成装饰。分组靠**位置 + 分割线**
表达:顺序固定「官方命令 → 技能 → 提示词」,同一类永远挨在一起。

默认 7 个按钮:目标、计划、压缩上下文,加四个预设提示词(审查改动、跑一遍回归、
解释报错、写提交信息)。内容全部在 [共生体设置](#7-共生体设置)里改。

> **"和 `+` 菜单一样"是怎么做到的**:不是插件自己拼出那个蓝色 chip(那确实拼不出来),
> 而是把这一次选择**交回官方** —— 调用官方 `/` 菜单自己的 pick 决策表
> (`commandUi.dispatch`),拿到结果后按官方菜单点击的同一条路径把 claim 交给会话输入框。
> 所以 chip 的外观、参数提示、以及无参数命令的执行方式,都是官方那一份,不是仿的。
> 走不通时(官方内部接口变了、目录还没就绪)会**降级**:有参数的命令填进草稿等你按回车,
> 没有参数的命令走宿主命令通道直接执行 —— 不会出现"点了没反应"。

> **为什么它挂在侧栏竖缝里而不是输入框上方**:DSH 在输入框那一带留的槽是
> **会话级、会被替换**的 —— 交互式问卷或审批一弹出,官方就把输入框整块换成别的内容,
> 挂在上面的东西会**整条消失**。所以竖条改挂常驻槽(会话标题栏那一排),
> 再用 `fixed` 定位到侧栏右缝。代价是位置得自己量 DOM(`_sidebarCol`),
> 好处是问卷、审批、切换视图时它都在。另外它外面包了错误边界:渲染期抛错会被 React
> 静默卸载,界面上表现为"什么都没有",所以错误还会被存下来,在设置页里看得到。

### 7. 共生体设置

**设置 → 插件 → 共生体设置**,三件事都在这一页:

- **显示项**(页首四个开关):账户余额、会话计费、内存占用、**上下文压缩**。拨动即生效,不用保存。
- **快捷按钮**:按**官方命令 → 技能 → 提示词**分成三组编辑,顺序**只能在同一组内调**
  (↑ ↓ 不会跨类);增删、改名、选图标(105 个内置图标,官方 `/` 菜单里那 8 个排在最前)、
  改类型(改了自动归到新组)、恢复默认。保存时会告诉你保存了几条、跳过了哪些空内容按钮。
- **导入 / 导出**:配置存成 JSON 文件,换机器时带走。

配置存在**浏览器本地存储**里,不写进 DSH 的配置文件;保存后立即生效,不用重启 App。

---

### 8. Git 状态胶囊

会话头部标题行最前(「对话 / 轨迹」那一行的正上方)常驻一枚读数:

```
⑂ main  +346 −1,105  ?3
```

- **分支名** —— 当前分支;游离头(detached)时显示短 sha。悬停给出仓库绝对路径、上游领先/落后、
  改动文件数。
- **`+N −M`** —— 工作区相对 `HEAD` 的增删行数;**`?N`** 是未跟踪文件数。
- **点分支名切分支** —— 本地分支数 > 1 时分支名可点,菜单里选中即在该会话的工作目录执行
  `git switch`,成功后读数立刻刷新(只有一个分支时它是普通文字,不给没用的按钮)。
  工作区改动会被覆盖时 **git 会拒绝**,菜单里原样显示它的解释;插件**不用** `-f`、
  `--discard-changes`、`stash`、`reset` —— 宁可不切,也不丢你的改动。
- **不是 git 仓库、机器上没有 git、或会话没有工作目录时整块不渲染**,官方头部一个像素都不变。
- 数据来自宿主一条认证路由(`GET` 读数 + `POST` 切分支),客户端**每 2 秒**轮询一次、页面隐藏时停表、
  切回前台/窗口聚焦/智能体一轮刚结束时立刻重读;
  契约与字段清单见 [AGENTS.md](AGENTS.md) 的「公开契约」。

### 9. 会话菜单里的复制 ID 与导出

侧栏每个会话行悬停时的「**⋯**」菜单,在官方四项(置顶 / 重命名 / 派生 / 归档)下方多出一组
(用一条分隔线隔开):

- **复制会话 ID** —— 把该会话的 ID(`008cd494-8461-42e0-b050-3b71fa903448` 这样的 UUID)放进剪贴板。
  这是宿主给会话落盘用的名字,贴进命令行、日志、issue 都直接可用。复制的是**裸 ID**,不带标题。
  **复制失败时菜单不关**,该行自己改成「复制失败,请手动选取」—— 不制造"点了但剪贴板是空的"。
- **导出会话日志** —— 与**会话头部「更多操作」里的「下载 Session 日志」是同一个功能**:
  官方 `dsh-session-log-export` 负责下载端点、预检、进度弹窗与并发折叠,这里只是**多开一个入口**,
  所以下到的 ZIP 内容、文件名、弹窗反馈完全一致。正在下载时该行禁用并显示「正在导出…」。

两条都是**加法**:官方布局与官方四项一个像素都不变。没装导出包时导出项**不注册**(不留空行),
复制项照常可用;官方 primitives 缺失时两项都不注册。

---

## 安装

这是一个 **DSH 组合包(bundle)**:装进来之后,profile 会把它自带的 `cordis.patch.yml`
合并进自己的 cordis 配置树,无需手工改配置。

### 方式一:让 DSH 装(推荐)

```bash
# 从 GitHub 装(推荐)
dsh plugin --profile desktop add github:seeseeczl/dsh-sym

# 从 Release 附件里的 tarball
dsh plugin --profile desktop add /绝对路径/dsh-sym-<版本>.tgz

# 从本地目录
dsh plugin --profile desktop add /绝对路径/dsh-sym
```

> 本包**没有发布到 npm**:它是零依赖、无构建步骤的纯 JavaScript,从 git 或 tarball
> 安装与从 npm 安装没有区别。npm 上确实有一个叫 `dsh-hud` 的同类包(另一个作者的项目),
> 与本项目无关。

也可以在 GUI 里走「设置 → 插件 → 安装」。

### 方式二:手工挂进 patch

不装进 `node_modules`,直接让 profile 的 `cordis.patch.yml` 指向本地文件:

```yaml
- id: sym-cost
  name: 'file:///绝对路径/dsh-sym/lib/host-v19.js'
```

再在同一个文件末尾确保它是启用的:

```yaml
- id: sym-cost
  disabled: false
```

### 卸载

停用即可(GUI 里关掉,或在 patch 里写 `disabled: true`),然后删掉包。
本插件不会修改 profile 里任何其它条目。

---

## 计价规则

### DeepSeek 官方价(人民币 / 每百万 token)

| 模型 | cache hit | cache miss | output |
|---|---:|---:|---:|
| `deepseek-flash` | 0.04 | 2 | 8 |
| `deepseek-v4-flash` | 0.04 | 2 | 8 |
| `deepseek-v4-flash-vision-exp` | 0.04 | 2 | 8 |
| `deepseek-v4-pro` | 0.30 | 9 | 27 |

**谷时(空闲时段)价格 = 表中数字 × 0.5。**

### 峰谷时段

以**北京时间**为准:

- **峰时**:周一至周五 `09:00–12:00` 与 `14:00–18:00`,**法定节假日除外**
- **谷时**:其余全部时间(含周末、法定节假日全天、以及上面两个区间之外的工作时段)

节假日表在 `lib/prices.json` 的 `holidays` 字段里,格式 `YYYY-MM-DD`,按年维护。

### 其他厂商的模型

DSH 内置了一份 **pi-ai 价目目录**(42 家厂商、1046 个模型,美元计价,含 `cacheRead`/`cacheWrite`)。
当会话用的不是 DeepSeek 模型时,本插件按下面的顺序找价格:

1. **DeepSeek 官方价**(上面的表)—— 命中就用它,人民币直接计价,不经过汇率;
2. **pi-ai 目录** —— 按「厂商 + 模型」精确匹配;匹配不到时退化为按模型名匹配(多个厂商拥有同名
   模型时取最短厂商名,保证结果稳定);
3. **`lib/prices.json` 的 `models` 覆盖** —— 你手工写的价目,**优先级最高**,改完存盘即生效。

命中 2 或 3 时,美元价按 `usdToCny` 折算成人民币。

**认不出的模型不会被计费**,只会在悬停账单里标出来(`unpriced`)—— 宁可少算,也不瞎算。

### 自定义价格

编辑 `lib/prices.json`:

```json
{
  "usdToCny": 7,
  "holidays": ["2026-01-01", "..."],
  "models": {
    "some-model": { "input": 1.5, "output": 6, "cacheRead": 0.15, "cacheWrite": 2 }
  }
}
```

- `usdToCny` —— 美元折算汇率
- `holidays` —— 法定节假日(峰谷判断用)
- `models` —— 覆盖价目;键是模型名,值是**美元 / 每百万 token**
- 文件里的 `_readme` 字段带着中文字段说明,不用另查文档
- **改完存盘立即生效,不用重启**(宿主每次投影都会检查文件修改时间)

---

## 工作原理

本插件由**两个半边**组成,各自跑在不同的进程里:

```
┌─ 宿主(Electron 主进程,Cordis 插件树) ─────────────────────┐
│  lib/host-v19.js                                              │
│   • sessionProjections 注册 sessionCost —— 会话事件的纯折叠   │
│   • 折叠 request/header、assistant/message、llm/retry-started │
│   • 只存 token 数(按峰/谷、按轮次、按模型分桶),不存金额     │
│   • 价目:DeepSeek → pi-ai 目录 → prices.json 覆盖            │
│   • 监听 session/event 维护 messageId → 正文索引              │
│   • 监听 agent/pre-step 展开引用短标记                        │
└──────────────────────────────────────────────────────────────┘
                            ↓ 投影(纯 JSON)
┌─ 浏览器(渲染进程,客户端插件) ─────────────────────────────┐
│  lib/client.js                                               │
│   • conversation.composer.dock   → 两个费用金额              │
│   • conversation.input.right     → DeepSeek 余额             │
│   • conversation.chat.assistant-actions → @ 引用按钮          │
│   • 根上的 CSS 变量 + data 属性  → 品牌行的峰谷标记           │
└──────────────────────────────────────────────────────────────┘
```

**为什么金额在客户端算、token 在宿主算**:宿主只折叠 token 数(可序列化、可 checkpoint、
跨重启一致),价格表可能被 `prices.json` 随时改。客户端每次渲染时用当前价目重新折算,
所以**改价格不需要重算历史、也不需要重启**。

**投影是纯折叠**:`sessionCost` 对每个已提交的会话事件做一次 `apply`,不产生副作用。
会话恢复时从事件重放,结果一致;`stateVersion` 变化时注册表会拒绝旧 checkpoint 并重建。

---

## 配置

**界面上的东西在设置页里改**:设置 → 插件 → 共生体设置(见[第 7 节](#7-共生体设置))。
显示哪些读数、快捷按钮条上有哪些按钮、每个按钮长什么样,都在那里;配置存在**浏览器
本地存储**里,保存立即生效,并且可以导出成 JSON 带走。

装好即用:不做任何设置也能正常工作(默认显示花费、余额、内存,竖条上默认是 3 个官方命令
+ 4 个预设提示词)。

仓库里唯一的配置文件是 `lib/prices.json`(见上)。

宿主半边改动(`lib/host-v19.js`)需要**重启 App**;客户端半边(`lib/client.js`)和
`lib/prices.json` 都是**热生效**的。

---

## 开发

### 目录

```
lib/host-v19.js     宿主半边:投影折叠 + 价目来源 + 引用展开
lib/client.js      客户端半边:各处读数 + 快捷按钮条 + 设置页 + 峰谷标记
lib/prices.json    价目覆盖 / 汇率 / 节假日
cordis.patch.yml   组合包补丁(让 profile 一次性装好)
test/              仓库内回归(npm test,57 条,零依赖)
scripts/           开发脚本(宿主换名助手 reload-host.mjs)
```

两个半边都是**零依赖**的纯 JavaScript(ESM),没有构建步骤,改完直接生效。

### 热重载

| 改动 | 生效方式 |
|---|---|
| `lib/client.js` | 客户端插件热更新,页面自动重载该模块 |
| `lib/prices.json` | 立即生效(宿主按 mtime 检测) |
| `lib/host-v19.js` | **需要重启 App**,或用「停用 → 换文件名 → 启用」绕开模块缓存 |

宿主半边改动之所以麻烦,是因为 DSH 的宿主热重载只监听配置与补丁文件,不监听插件代码。
换一个新的文件名(`host-v12.js` → `host-v19.js`)能拿到一个全新的模块实例,但**必须等旧实例
完成 dispose**,否则新旧注册会撞在一起。换名与同步引用已脚本化:

```bash
node scripts/reload-host.mjs --dry-run   # 先看会改哪些文件与行
node scripts/reload-host.mjs --apply     # 真改;停用/启用与重启仍由人完成
```

### 测试

仓库内有回归,入口是 `npm test`(等价于 `node --test`,**不要**写成 `node --test test/`,
Node 24 会把目录当模块解析而失败):

| 文件 | 覆盖 |
|---|---|
| `test/host.test.mjs` | 峰时边界、周末/节假日、引用展开与未知 id、索引淘汰、价目常量、内存读数、失败路径 |
| `test/client.test.mjs` | `describeScope` 账单文本(峰谷拆分、谷时省钱、未收录模型、flat 厂商)、插槽注册幂等、降级日志 |
| `test/contracts.test.mjs` | 跨端契约常量两端一致,客户端写出的引用标记宿主能展开,缓存省下文案不回流 |

**回归只覆盖纯函数**:渲染、插槽注册的实际效果、热更新仍要人工验证。开发时的其余做法:

- **客户端组件**:`test/helpers/load-client.mjs` 用最小的 React hooks 替身物化模块工厂
  (不引入 jsdom);要断言真实渲染仍需 CDP 驱动一个 headless 页面点击真实按钮
- **端到端**:`curl` 取页面里 `plugins/??dsh-sym/client.js` 的 bundle,确认各项注册都在
- **降级排查**:控制台搜 `[dsh-sym]`,每个来源只记一条,能看到是哪个可选能力缺席了

---

## 已知限制

- **余额需要桌面版已登录 DeepSeek 账号**。未登录、账号态读不到时显示 `—`,不会显示 0;格子始终占位 —— 它忽隐忽现会推动旁边的模型选择器。
- **引用索引是内存里的**,App 重启后重新填充;引用一条已被淘汰的旧回复时标记原样保留。
- **峰谷判断按北京时间**,用内置节假日表;表过期时节假日会被当成工作日(记得按年更新 `holidays`)。
- **认不出的模型不计费**,只在悬停账单里标出。
- **没有「把会话移动到别的工作区」这个功能**。它曾有一份用真实数据副本验证过的实现,
  但 DSH 在**构建期**就固定了客户端可用的 Remote 命名空间,插件无法新增
  「界面点一下 → 宿主做一件事」的通道,所以它永远点不到。代码已于 2026-09-30 移入
  `docs/01-architecture/adr-003-session-move-not-wired.md` 作为设计记录,
  **不是**已交付能力(ADR-003)。
- 输入框的**实验性语音输入**(录音 / 转写)展开时,官方会把整个 `standardControls` 容器隐藏,余额跟着模型选择器一起隐藏(它俩同进同退);没装那个实验 bundle 时永不隐藏。
- 官方同一时刻可以挂多份会话(右侧栏聊天标签、子代理面板),**每份工具行都会显示一个余额**。
- **新会话第一个任务在结算前只显示内存读数**,两个金额要等这一轮跑完才出现 —— DSH 在流式
  期间不产生用量数据,官方自己的 token 读数也是结算后才更新。

---

## 许可

[MIT](LICENSE)

Install

dsh plugin --profile web add github:seeseeczl/dsh-sym

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source