# dsh-input-limit 设计文档 > 版本 0.1.1 · 适配 DeepSeek Harness `dsh` **0.1.5-rc.2** > 读者:插件维护者与贡献者。安装使用见根目录 [README](../README.md),发布流程见 [RELEASING](RELEASING.md)。 --- ## 1. 概述 `dsh-input-limit` 是一个 **浏览器端(browser-only)** 的 DeepSeek Harness 社区插件:在 Web UI 消息输入框的工具行(模型选择器旁)挂一个小胶囊(chip),显示当前会话模型的**输入上限 (上下文窗口,context window)**,点击即可在弹窗中**就地修改**并持久化为「按模型覆盖」的设置项, 无需进入 Settings 页面翻找。 它驱动的数值用于 Host 侧的**上下文占用计算与自动压缩判断**——当模型真实输入上限小于适配器 默认值(如 128K vs pi-ai 默认 262144)时,占用显示与压缩时机才能正确。 设计原则: - **纯客户端插件**:无自定义 Host 代码;读写都走 rc.2 客户端已有 wire face (`ctx.remote` Typert 命名空间 + `ctx.sessions` 对象层),与 Settings 页面使用完全相同的 RPC。 - **单单位原则**:输入框只说一种单位——精确 token 数;K/M 只是显示拼写。存储值与输入值 对「数量级」永不产生分歧。 - **可热更新**:设置文档变更(`settings/document-updated`)与本会话模型切换 (`modelSelection` projection)都会实时刷新已打开的胶囊。 ## 2. 目标与非目标 **目标** 1. 展示当前模型的有效输入上限(覆盖值或 provider 默认值),`· 默认` 标签区分来源。 2. 弹窗内输入精确 token 数(接受千分位逗号),实时换算 K/M,保存 / 恢复默认。 3. 持久化为 provider 设置段里的 per-model `contextWindow` 覆盖,行为与内置设置编辑器一致。 4. 写入冲突(`settings/conflict`)自动以新 revision 重试一次,两个编辑器互不静默覆盖。 5. 仅对「可配置 provider + 有模型选择」的会话可见;子代理(subagent)会话隐藏。 **非目标** - 不修改 Host 侧压缩策略本身(只提供它读取的数值)。 - 不做单位输入(`128K` 式输入永远拒绝——见 §6.5)。 - 不支持非 Web 面(headless / sdk / acp profile 无 UI seat)。 ## 3. 运行环境与依赖契约 | 依赖 | 契约 | |---|---| | `dsh` 0.1.5-rc.2 | profile/bundle 组合机制;`dsh plugin` = pnpm 转发 + `dsh.bundle` 声明自动入层 | | Cordis 4.x | 插件以 `name` + `inject` + `apply(ctx)` 挂载;`ctx.effect` 可逆注册;traceable context proxy 要求 **dotted inject 声明** | | `ctx.remote.settings` | `describe()`:返回 `writable` + 每命名空间的 `value/base/user` 三层视图与 `revision`;`mutate(ns, ops, expectedRevision)`:路径寻址 `set/unset`,乐观并发 | | `ctx.remote.session` | `modelCatalog()`:Host 级模型目录,含 `default` 选择 | | `ctx.sessions` | `binding(id).session.projections.faceOf('modelSelection')`:持久 model-selection 投影(`next`/`lastUsed`/`subscribe`);`subagentAddress(id)` 判定子代理会话 | | Web UI slots | `conversation.input.right` 列表 seat;locale 命名空间注册(`ctx.locale.register`) | ## 4. 包结构与双端形态 ``` dsh-input-limit/ ├─ package.json # dsh.bundle.patch + dsh.client.platform: "web" ├─ cordis.patch.yml # bundle patch:向 profile 插一行 { id, name } ├─ src/ │ ├─ index.ts # Host 半:无行为占位入口(存在即触发 Host 服务浏览器半) │ └─ client/ │ ├─ index.ts # 插件主体:注册 slot、locale、刷新扇出;在 plugin fiber 内 detach faces │ ├─ InputLimitChip.tsx# seat 组件(chip + popover,React 18) │ ├─ contract.ts # 注入给组件的业务面类型(available/read/write/reset/subscribe) │ ├─ provider.ts # 数据层:readLimit / applyModelLimit / resetModelLimit │ ├─ capacity.ts # 数值层:parseTokens / formatTokens / formatCompact │ ├─ wire.ts # rc.2 wire face 的结构类型镜像(零运行时依赖) │ ├─ locales.ts # zh(产品文案)/ en 双字典,en 以类型约束与 zh 键对齐 │ └─ InputLimitChip.module.css # 自包含调色板,--ilm-* 变量供宿主主题重指向 ├─ lib/ # 预构建产物(随仓库发布,安装方免工具链) │ ├─ index.mjs # Host 半 ESM │ └─ client.js(.map) # 浏览器半:__ModuleLoader__ 闭包工厂格式 └─ tests/ # vitest + 无依赖 verify 脚本 + bundle 冒烟 ``` **挂载链**:`package.json` 声明 `dsh.bundle.patch: ./cordis.patch.yml` → `dsh plugin add` 后该包 自动加入 profile 的 bundle 层栈 → patch 向配置插入一行 `{ id: input-limit, name: dsh-input-limit }` → Cordis 加载 Host 入口 → Web Host 按 `dsh.client.platform: "web"` 服务 `/plugins/dsh-input-limit/client.js` → 浏览器 `window.__ModuleLoader__.load({ id, factory })` 执行浏览器半。 **浏览器 bundle 合同**(与内置 UI 插件相同,`tsdown.config.ts` 复刻): - 仅允许外部化平台模块(`PLATFORM_MODULES`:react / cordis / ui-slots / …),其余全部内联; - CSS Modules 经 lightningcss 编译后内联为 `