# AGENTS.md — dsh-hub 开发约束(总纲) > **本文件是 dsh-hub 的全部开发 harness(总纲)。** 开发/修改任何部分之前,**必须**先阅读本文件,并阅读 [PROCESS_QUALITY.md](PROCESS_QUALITY.md)(SOP + 质量管理流程约束,铁律 7)与对应职能目录下的子 harness(见文末「分层 harness 索引」)。 > > dsh-hub 是 **DeepSeek Harness(dsh)的桌面端框架**:以原生窗口(Tauri 2.x,dev-v2 Tauri-only;WebView2 时代壳已删除)承载 dsh Web UI,并提供系统托盘、主题同步、窗口记忆、右侧栏、会话通知等桌面能力。它是 **dsh 生态的插件 + 桌面壳**,**绝不修改 dsh 底层源码**。 --- ## 0. 铁律(不可违反) 1. **绝不碰 dsh 底层代码**。本仓库不复制、不修改、不 patch `deepseek-harness` 源码;一切能力通过 dsh 官方插件接口(Cordis `ctx`、slot、HTTP 路由、事件)接入。参考 dsh 源码**只读**。 2. **插件身份一致性**(历史血泪:改名事故导致 `duplicate loader entry id` / `loaded without registering`)。以下四处**必须永远相等**: - `package.json` 的 `name` - `tsdown.config.ts` 的 `PLUGIN_ID` - `cordis.patch.yml` 的 `insert.name` - web profile `bundles` 里的条目 改任一必须同步全部,并重新 `npm run build && npm run build:client`。 3. **启动门控不可破坏**:`cordis.patch.yml` 的 `disabled: !!js process.env.DSH_HUB_LAUNCHED !== '1'` 保证普通 `dsh web` 不加载桌面壳。任何改动不得让 CLI 模式加载壳/插件页。 4. **多实例防护不可削弱**:默认拒绝与已运行的 dsh 共存(`allowMultipleInstances=false`)。多个 dsh 共享 `$DSH_HOME` 会话存储,同会话双写会损坏会话日志(seq 冲突,已实际发生并需手工修复,见 [docs/关键踩坑记录.md#24](docs/关键踩坑记录.md))。任何修改不得默认放开共存。 5. **settings 命名空间约束**:`settingsNamespace('dsh-hub')` 强制小写 kebab-case;带 scope 的包名(`@marecgents/dsh-hub`)不能用作 settings ns 或 API 前缀。壳配置 UI 走插件自有 HTTP 路由 `/api/dsh-hub/*`;需要修改官方模型能力(如 `llm-pi-ai.reasoningEfforts`)时,必须复用官方 `settingsScope`/settings RPC,禁止直接改写 settings 文件。 6. **发布前检查不可跳过**:每次 `npm publish` 之前**必须**运行 `node scripts/verify-release.mjs` 且**全部 PASS**(含「干净安装 → 首启装配」冒烟),FAIL 立即停止排查,**禁止发布**。发布流程与检查细则见 §5(rc.10–rc.13 曾因跳过"全新环境安装验证"连发 4 版首启即崩的包,见 [docs/关键踩坑记录.md#33](docs/关键踩坑记录.md))。 7. **开发流程必须遵循 PROCESS_QUALITY**:任何开发任务(迁移、功能、修复、文档、发布)**必须严格遵循 [PROCESS_QUALITY.md](PROCESS_QUALITY.md) 的 SOP(标准作业程序)与质量管理规范**——含阶段输入/输出/门禁(Gate)逐项核查、验收表、回归基线、DMAIC 改进循环。**禁止跳过阶段门禁**;与本文件铁律冲突时,以本文件(AGENTS.md)为准。 8. **bug 修复必须遵循 BUG_FIX_SOP.md**(五阶段:复现/测量/根因/方案审查/实施验证;先测量后下结论;链路探针用完即删;修复沉淀进踩坑记录)。官方弹层类 UI 在本环境可能不可达(合成事件与真实事件均失败,实测)——依赖官方 UI 入口前先判别其可用性,功能落点优先官方数据层 API。(详见 [BUG_FIX_SOP.md](BUG_FIX_SOP.md)) 9. **数据安全红线:绝不触碰 home / C 盘**(历史血泪:`~/` 与 C 盘曾被删除导致配置全丢、系统重装,2026-08-25 复现)。**任何 agent / 脚本 / 手工操作都必须遵守**: - **禁止**删除/移动/清空 `C:\Users\*` 下任何文件(尤其 `~`、`$HOME`、`USERPROFILE`、真实 `~/.dsh`、`.ssh`、`.gitconfig` 等配置文件);任何 `rm` / `Remove-Item` / `RMDir` / `unlinkSync` / `rmSync` 的目标路径**不得含** `~`、`$HOME`、`USERPROFILE`、`C:\Users`、`homedir()`。 - **测试 / 复现 / 安装 / 卸载一律用隔离环境**:`DSH_HOME` 指向 E:/D: 盘或 `%TEMP%` 下的独立目录(如 `$env:TEMP\dsh-hub-repro`),**禁止**指向真实 `~/.dsh`;「清干净重来」只对隔离目录执行,**永不删除真实 `~/.dsh`**(dsh 会自动重建空 profile,配置/会话/凭据不可恢复)。 - **删除纪律**:执行任何删除前,先打印/回显目标绝对路径并确认不是 home 子树;`rm -rf` / `Remove-Item -Recurse -Force` 必须写完整绝对路径,禁止裸 `~`、禁止环境变量缩写、禁止 `$INSTDIR` 未经展开即递归删除。 - **权限纪律**:破坏性操作(删目录、卸载、覆盖配置)不得在 `never` 审批模式下静默执行;执行前必须自检目标路径是否属于 home 子树。 - **排查初始化/装配问题**:先换「新的隔离 DSH_HOME」复现,再查日志;把「删掉用户真实配置」列为最后手段且需用户明示同意。 --- ## 1. 架构定位与职责隔离(壳 vs 插件) dsh-hub 是**双 half** 结构,且**套壳代码与插件代码必须严格模块化隔离**: ``` ┌─────────────────────────────────────────────────────────────┐ │ host half(dsh 进程内,Node)——分层结构见 src/AGENTS.md │ │ src/index.ts Controller:插件入口,编排装配 │ │ src/core/ Core:命令注册表 / 生命周期(registry) │ │ src/managers/ 壳 Manager:tauri-shell(唯一壳 Manager)│ │ src/server/ Server:HTTP 路由工厂(/api/dsh-hub/*) │ │ src/services/ Services:纯领域业务(config-store / pty-manager)│ │ src/helpers/ Helper:无状态工具(state-store) │ │ src/controllers/ Controller:业务编排(session-runtime/tray-pipe)│ │ src/models/ Model:共享类型/常量(pipe 帧、ShellConfig) │ │ src/utils/ Utils:纯函数(管道帧解析) │ ├─────────────────────────────────────────────────────────────┤ │ client half(浏览器内,React) │ │ src/client/* 插件 UI:设置卡片 + 右侧栏(body portal)│ ├─────────────────────────────────────────────────────────────┤ │ 独立 dsh 插件(双轨分发,见 §1.1 铁律 8) │ plugins// 独立插件(独立 npm + 随 hub resources 分发) ├─────────────────────────────────────────────────────────────┤ │ Tauri 原生壳(Rust,src-tauri/src/)——见 src-tauri/src/AGENTS.md│ │ lib.rs / main.rs Controller:壳入口 │ │ commands/ managers/ services/ helpers/(SPT 分层) │ ├─────────────────────────────────────────────────────────────┤ │ 壳入口 / 装配(dev-v2 Tauri-only,launcher 家族已删) │ │ src-tauri/src/lib.rs Tauri 壳入口(见上) │ │ bin/dsh-web-sidecar.mjs sidecar 辅助(装配/入口解析,M5 预留)│ │ scripts/assemble-profile.mjs 运行 profile 装配(幂等) │ └─────────────────────────────────────────────────────────────┘ ``` **隔离边界(dev-v2 Tauri-only,2026-08 更新)**: - **壳层**(`src/managers/*` + `src/helpers/`(state-store;dwm-theme / explorer / screen 等 Windows 专属能力已随 WebView2 壳删除、迁入 Rust 壳)+ `src-tauri/src/` 整个 Rust 壳)——壳能力已整体落在 Rust 侧;**不得**让插件逻辑渗入壳层。 - **插件层**(client/* + `src/server/*` 路由 + settings-card)——与壳解耦,通过**明确定义的接口**(HTTP 路由 / IPC 桥 / 事件)通信;Tauri 迁移时保留。 - 新增代码时,先判断属于壳还是插件,落入对应目录;**禁止在壳层写插件业务、在插件层写窗口/托盘/系统调用**。 **模块化分级**(大厂级代码组织,新增文件必须归类): | 类别 | 职责 | 命名示例 | |---|---|---| | **Controller** | 入口编排、装配、生命周期挂钩 | `index.ts`、`lib.rs` | | **Manager** | 管理复杂对象的生命周期/状态机 | `managers/tauri-shell.ts`(壳)、`managers/tray.rs`、`managers/icon.rs`(图标 6 面编排:面级幂等 + 全局串行锁 + 单 worker/pending 合并防快速切换卡死;`theme.rs` 回归无状态纯函数) | | **Services** | 业务服务,对外提供能力 | `services/config-store.ts`、`services/notify.rs` | | **Server** | HTTP 路由 / 对外端口 | `server/config-api.ts`、`server/workspace-api.ts` | | **Helper** | 纯工具函数,无状态、无副作用 | `helpers/state-store.ts` | | **Core** | 全局注册表 / 生命周期顺序 | `core/registry.ts` | **分层依赖红线(SPT 单向依赖,2026-08-18 重构确立,防"改一处坏一处")**: - **单向依赖**:上层可依赖下层,下层**禁止**依赖上层。层级:`index.ts → controllers → core → managers/server/services → helpers → models/utils`(host half);`lib.rs → commands → managers/services → helpers`(Rust 壳 half)。 - 跨层引用违规 = 审查必纠项;新增文件先归类(Core/Controller/Manager/Services/Server/Helper/Model/Utils),命名清晰表达职能。 - **命令分发**:托盘/管道命令一律走 `core/registry.ts` 注册表(新增命令 = `trayCommands.register(...)`),禁止在 index.ts 散落 if/else 分发链。 - **双向管道协议同步**:壳→host 走 stdin `MG_TRAY`;host→壳走 stdout `DSH_CMD`。帧格式与命令名变更必须同步 `src-tauri/src/managers/node.rs` 分发表 ↔ `src/managers/tauri-shell.ts`(sendDshCmd)↔ `src/controllers/tray-pipe.ts`(MG_TRAY 读取)。 ### 1.1 新功能分发策略(2026-08-23 确立,铁律 8) 新功能落地**先判定类型、再定分发**;**禁止"死目录"**(只放代码不接装配链——代码进仓库但无法生效 = 未完成的功能)。 | 功能类型 | 判定 | 分发策略 | |---|---|---| | **dsh plugin**(独立插件) | 纯 dsh 生态能力(`ctx.tools` / `systemPrompt` / `session/event` / `webServer` / 独立 client section),**可脱离本壳使用** | **双轨必须同时**:① 独立 npm 发布(`plugins//` 包 `private:false`,走 §5 发布流程 + verify-release 门禁);② 随 hub 分发(tauri resources 含 `plugins//**/*` + assemble-profile 装配进 profile + cordis.patch.yml 挂载 → NSIS 自带) | | **壳单一功能** | 壳/插件层增强(client UI、托盘、窗口、Rust 壳、设置卡),**只在本壳有意义** | **不发布 npm**:随 hub 仓库走(client 经 `build:client` 编译进 lib/;Rust 经 cargo 编译进 exe);装配走 `src/client/index.ts` 或 `src-tauri` | | **plugin + 壳联动** | 既有通用插件能力、又有壳侧集成(如插件 host 能力 + 壳 UI/托盘接线) | **拆分双轨**:插件部分(通用能力)→ 独立 npm 发布 **且** 随 hub 分发;壳侧集成(UI/装配/托盘接线)→ 随 hub。**两边都必须接装配链**(插件进 profile;壳侧走 hub 装配),禁止只发一半 | **判定要点**: - 依赖 dsh 官方扩展点(tools / systemPrompt / 事件 / webServer)且脱离本壳仍有用 = **plugin** → 双轨(npm + hub)。 - 依赖壳能力(tauri 命令、托盘、窗口、`DSH_CMD`、壳配置)或本壳专属视觉(皮肤/背景/rail/右侧栏)= **壳功能** → 随 hub,不发 npm。 - 每个新功能落地时必须回答:"**不接装配链它能生效吗?**" 不能 = 必须补装配链(插件:cordis.patch.yml 挂载 + 进 profile;壳功能:index.ts 装配);能 = 才算完成。 --- ## 2. 严格遵循 dsh 架构接口与开发者文档 - 一切 dsh 能力通过**官方接口**接入:Cordis 插件(`ctx.effect` / `ctx.on` / `ctx.waterfall`)、client slot、`ctx.webServer.register`、dsh 事件(`session/event`、`agent/*`)。 - **开发某功能前**,若对 dsh 接口不熟:先读 `deepseek-harness/docs/` 下对应文档(`architecture.md`、`cordis-primer.md`、`web-styling.md`、`development.md` 等);内容不足时**浏览整个 deepseek-harness 项目**找最适配的架构/技术路线作为参考(只读,不复制源码)。 - 使用 dsh 官方 client 包:`@deepseek-ai/dsh-client-ui-primitives`(图标)、`@deepseek-ai/dsh-client-*`(UI 组件);client 构建时平台模块必须保持 external(见 `tsdown.config.ts` 的 `PLATFORM_MODULES`)。 - dsh 版本升级后,核对 `peerDependencies` / `devDependencies` 与 junction(`npm install` 会清掉 SDK junction,须重跑 `npm run build:client`)。 ## 3. 严格遵循 dsh web 前端 UI 风格 - 颜色/字体/圆角/间距一律用官方 `--dsw-*` token(`--dsw-alias-*` / `--dsw-specific-*` / `--dsw-static-*`),**禁止硬编码 hex/灰度**(可保留 `var(..., fallback)` 双保险)。 - 图标用官方图标库 `@deepseek-ai/dsh-client-ui-primitives`(`Icon*Outline16` 系列),不自行造图标。 - **单点豁免(置顶 pin 图标,到期条款)**:官方图标库无 pin/bookmark/star,`src/client/pin-conversations.ts` 自绘 **1 个** 24 视口填充路径 pin glyph(`currentColor`、`aria-hidden`)——限定 1 个 glyph / 1 个模块 / 不扩散;**官方提供 pin 图标后立即切换**(见 src/client/AGENTS.md §UI 铁律 2)。 - 遵循 dsh 官方组件样式:设置卡片参照 `ui-settings-plugins` 的 `PluginCard.module.css` / `fields.module.css`;tab 参照 `ConversationRoot.module.css`;tooltip 参照 `ui-primitives/Tooltip.module.css`。 - 右侧栏用 **body portal** 挂载(不占用官方 details slot),自管宽度;收起 rail 与左弹 tooltip 逻辑保持。 - **背景图(background image)豁免(与皮肤并列的新视觉层)**:`src/client/backgrounds.ts` 通过注入 `