Files
dsh-worktable/AGENTS.md
T

124 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# dsh-worktable 项目规则
> 工作台容器插件:侧边栏里收纳 agent 级项目的「应用抽屉」。纯增量,不替换官方插件。
## 协作方式(用户定案,最高优先级)
- **设计先给最小版**:任何 UI/文案/方案先交「最少元素」版本给用户拍板,确认后再增量;
默认克制——没有存在理由的元素不放;不一次搭「完整版」再返工。
- **长任务拆 checkpoint**:每个可验收阶段完成后停下汇报,等用户确认再进下一段;
不一口气跑完长任务。
- **对外动作永远先审**:发布、评论、给观众的内容、任何公开操作,一律先给用户过目,
用户不点头不执行。
- **改完必读回**:每次编辑后读回改动处的完整行/段落,确认无残留、无断尾
(包含命令与 URL)。
- **验收用最终产物,不用中间信号**:任何交付物(tgz、命令、文档、UI)的验收动作必须是
「解包 / 复制 / 实跑用户路径」;「构建成功」「bundle 里有字符串」「退出码 0」不算验收。
- **发布前自跑最终产物清单**:干净目录安装、最终包逐文件核对、双资产哈希、关键行逐字
grep —— 先自查再报告,不等外部复审来抓。
- **发布包验收=结构检查 + 真实安装**:标准 package/ 前缀、白名单精确 7 文件(无 src/、.map),
再独立目录 npm install + import() 断言;仅核对文件存在不能证明包可安装。
- **声明可证伪**:只写已验证范围;安装/导入不是端到端验收,版本比较正常不等于通知覆盖所有用户。
- **同 tag 资产不可覆盖(用户已拍板)**:坏包处理 = 发新版本号 + 旧 Release 正文标注问题,
默认不替换旧资产;仅经用户明确批准的紧急补救例外,且须:不改名(保持原 URL 名)、
用已验证包、不删不盖现有资产、上传后从完整 URL 下载核对、旧 Release 注明补救经过。
## 边界
- 插件包根目录 = `01_content/`;本仓库其余目录是项目文档与本地工具。
- **不替换、不禁用任何官方插件**(ui-sidebar / ui-workspace / ui-layout)。
- 项目/布局/绑定存 localStorage,媒体存 IndexedDB;文件窗、mkdir 与产物握手会读写用户配置的项目目录,权限边界见 `01_content/README.md`。
- dsh-travelatlas 是入驻项目而非本仓库的一部分;协议见 `02_process/PRD.md` §5.3/5.4。
- **平台边界**:Windows 网页端已测范围见 `01_content/README.md`,不宣称全功能验收;桌面已测版本与候选边界同包内 README;macOS 为实验性支持(核心文件路径代码已做跨平台适配,
尚未真机端到端验证)。路径拼接必须走 `pathutil.ts` helper 或 Node `path` API,不手写分隔符。
## 当前 0.5.0 安全与兼容范围
- 私有 HTTP/终端握手统一调用 `requestAuth.ts`:新宿主要求 owner,0.2.0 仅放行同一 operator 对象;未知/不可用服务关闭访问,不用 requestRejection-only 回退。health 与随机只读预览地址是明确例外。
- `SandboxedFrame` 禁止 `allow-same-origin`;本地目录先鉴权创建临时只读挂载,Desktop 资源走 Host HTTP 地址。不得为恢复页面存储/自动取字而取消隔离。影响、保活与过期语义必须见包内 README。
- 预览读取必须绑定同一文件句柄、限量并复核文件身份/路径及撤销状态;保留目录替换/检查后增长等回归。该机制不等于恶意本地进程的 OS 文件沙箱,文档不得作绝对防护承诺。
- 当前版本只声明 DSH 0.2.0-rc.2 / 0.2.1-rc.3;网页隔离实测与 Desktop 传输单测不能冒充真实 Desktop 全功能验收。下文旧版本兼容条目是历史依据。
- `npm run test:security` = 22 项源码/夹具回归;流水线源码回归合计 122 项。安装产物另跑 `node 04_test/split-margin.test.mjs [client.js 路径]` 的 5 项真实 Chromium 回归(Edge/Chrome 或 `DSH_TEST_CHROMIUM`),改 margin/换根观察器必须保留旧产物变红的回归证据。
## 构建与验证
```powershell
cd 01_content
npm install
npm run build # lib/index.js + lib/client.js
node --check lib/index.js
```
- 客户端 bundle 必须保持 `window.__ModuleLoader__.load` 握手与 external react/@deepseek-ai/*。
- 变更视图状态结构时同步更新 PRD 的持久化说明。
- **构建必须 `cd 01_content` 后执行**:误在仓库根跑会把 lib 写到仓库根 `lib/`,宿主仍加载
`01_content/lib` 旧 bundle,出现「改完不生效」假象(已有教训,见工作日志)。
- **发布打包唯一入口 = `npm run pack`(01_content/release-prep.mjs)**:身份断言(package/manifest/cordis.patch.yml 严格结构)→
版本一致性 → **会话/输入/传输/挂载/安全回归**(当前 122 项,评估源码契约及夹具)→ 构建(cwd 固定 01_content)→ node --check → npm pack → 结构清单断言(package/ 前缀 + 精确 7 文件,
无 src/、无 .map)→ 独立临时目录 npm install + import() 断言(apply 函数/inject 含 webServer+sessions/name/
HEALTH_PATH/包内双 bundle 版本)→ **客户端工厂求值门禁**(ModuleLoader 恰好注册一次 + ID 校验 + 精确外部依赖
白名单 react/react/jsx-runtime + apply/inject 断言)→ **分栏锚点 DOM 回归**(14 场景,评估安装产物 lib/client.js;
测试接受包路径参数)→ **侧栏弹窗回归**(安装产物,展开/收起共用层)→ **真实 Chromium 分栏 margin 回归**(5 场景,评估安装产物)→ **服务端数据目录回归**(3 组场景,评估安装产物 lib/index.js)→ dist/v版本号/ 双资产(终态恰好 2 文件 + 双 SHA 同源)。
脚本零 git/gh 动作,发布上传由 gh 手动完成。
**发布禁止裸 npm pack 或手工 tar 生成发布包**;脚本从仓库任意目录调用均安全(以自身位置解析)。
- 弹窗必须由展开/收起两种侧栏共用一份 JSX 层;保留路径选择失效/卸载守卫,禁止再复制弹窗。`npm run test:sidebar` 评估实际构建组件,旧缺失/重复渲染应变红。
- **配套检查入口**:`npm run test:gate` = 工厂门禁 10 个失败/正向用例;
`npm run test:widget` = 项目/会话归属、共目录、延迟结果与补挂重试 26 项回归(执行实际函数,不等于模型生成端到端验收);
`node 04_test/anchor-dom.test.mjs [lib/client.js 路径]` = 分栏锚点 14 场景(缺省用工作目录构建产物);
`node 04_test/server-home.test.mjs [lib/index.js 路径]` = 数据目录解析 3 组场景(子进程隔离夹具;
**突变体验证法**:把 loadPkg 兜底的 baseDshHome() 故意改回 resolveDshHomeSafe() 恢复循环,测试必须红)。
`npm run verify:remote -- --expect-sha <release-prep 输出的 SHA> [tag]` = 发布后只读核对
(远端固定名+版本化双资产文件名/结构/版本/双 SHA 同源且等于本地验收 SHA/安装/导入;
远端只读,本地仅临时目录)。上传后必须跑 verify:remote 并用 --expect-sha 比对,
防「双资产同错」;tag 模式断言 tag==='v'+包内版本。
## 领域约定(会话中必须遵守)
| 实现参考 | 对应材料 |
| --- | --- |
| 接口鉴权、预览能力、隔离限制与验收边界 | `docs/compatibility-and-security.md` |
| 控制室 UI、预设/模型、提醒、产物握手、更新状态 | `02_process/PRD.md` §14(修改对应组件时对照源码,不以历史 API 示例替代新旧桥接) |
- **Desktop 兼容边界**:0.4.0 保留客户端 platform=web(Desktop 0.2.0-rc.2 也按此契约加载)。终端 WebSocket 经 `hostTransport.ts` 使用宿主 `__DSH_TRANSPORT__.streamBaseUrl`,不能由 dsh-app://app 拼 ws://app;更新提示须指向 desktop profile 与桌面端自带 CLI,不能修改 web profile。页面点测与源码回归不能替代完整桌面端业务验收,不宣称桌面全功能兼容。
- **0.3.4 网页端兼容**:声明已测版本 0.1.1-rc.2 / 0.1.2-rc.1 / 0.2.0-rc.2,不外推 Desktop 或未测宿主。当前会话走 `currentSessionOf`(旧 current / 新 mainView),导航走 `openHostSession`(新 uiWorkspace / 旧 sessions.open),标注走 `appendHostInput`。Cordis 可选服务用 `ctx.get` 探测;返回 undefined 后不得再访问未 inject 的同名属性。旧 current 契约的引用坐标是显示文本位置,新 mainView 契约按单字符 chip 投影;不得因旧版也有 input.for 就误用新版坐标。`npm run test:input` 为 15 项定向回归。
- **0.2 会话状态/发送**:可选订阅 `uiSession.sessionStatus`,只读归一化列表与 subagentCatalog;completionUnread 不是任务成功证明,打开主会话会清除。已观察到的 running→false 供原产物握手使用,下一轮 running 清除;停止不等于成功。新会话发送通过 `sessions.using` 临时持有,等待 ready 后发送并由宿主释放;业务拒绝/超时不得再换通道重发。`npm run test:session` 为 20 项回归;0.2.0-rc.2 已实测短文本发送/回复,真实待决/子代理仍未验收。
- **0.2 新建/预览/选择接口**:`createHostSession` 必须区分显式未分组(第四参数 `none`)与隐式默认(`auto`)。自定义窗口显式未分组仅传 cwd(无目录则空选项),禁止注册/复用工作区或 initializeDefault;新版控制室空未分组会话输入区禁用,必须提示并阻止创建,由用户选组/加入可用现有会话,不偷偷改组、不发激活消息。现有分组未选 ID 同样拒绝;显式现有/新建组才传 workspaceId。分组列表晚返回不得覆盖用户已选分组。隐式默认保留此前工作区兜底,旧宿主保留原分支,不移动既有会话。预览读临时持有的 eventSource,模型目录在持有期间使用,结束即释放;预设接 remote.agentPresets。`npm run test:details` 为 30 项回归,含两处真实调用接线与延迟默认分组;不能替代真实输入区/模型会话验收。
- **窗口编号**:用户说「窗口1/2/3…」指布局里按「左栏 → 顶行 → 主行」顺序的第 N 个内容窗。
例:田字格预设(g4)窗口1/2 = 顶行左右、窗口3/4 = 底行左右;l13 窗口1 = 顶部大窗,
窗口2/3/4 = 底部三小窗(从左到右)。需要定位时按此映射,不要凭猜测。
- **预设追加规则**:新布局预设只允许追加到 `PRESET_DEFS` 末尾(选择器里的「+自定义」磁贴
永远是最后一个);字段 leftCount/topCount/contentCount/chatFull/topHeightDefault/topHeightRatio,
聊天窗恒在右侧;缩略图在 presetThumb() 加分支。
- **分栏锚点与还原**:历史宿主三种根结构、零高包装取可见头部的规则见 PRD §12.4;同根重锚仅在锚点更换时保存原值,避免关闭时泄漏 margin/宽度。写入 margin 后须从 CSSOM 读回基线;换 viewArea 同步重绑让位观察器。改动必须跑锚点与真实 Chromium 回归,保留旧产物变红证据。
- **数据目录解析无循环**:resolveDshHomeSafe 优先官方 home-paths,再 baseDshHome;loadPkg 兜底只能调 baseDshHome,不得反调 resolveDshHomeSafe。DSH_HOME 空白视未设;实际路径保留两端空格,支持 ~、~/、~\\ 与相对 cwd,默认 ~/.dsh。不得新增 dsh-home-paths 生产依赖(旧宿主 peer cordis 不兼容)。修改须跑 server-home 三组与循环突变体检查。
- **新会话策略**:创建后应用默认预设与有效模型;桥接保持原选择策略,持有结束即释放。失败不重复发送;插件导航须 markPluginSessionOpen,未绑定项目成功任务会 autoBind。
- **项目文件夹**:保存要求绝对路径,mkdir 非 2xx 必须中止;自定义任务带项目 cwd,产出落该目录。
- **窗口任务**:buildWindowTaskText 统一携带窗口身份/项目目录/通用知识;不重复侦察插件源码。
- **产物握手 v2**:目录不是项目身份。`widgetMount.ts` 校验 projectId/paneId/bindingId,禁止扫旧根清单或回退窗口1。提示词与完成读取限本会话;补挂重读并复验归属,成功后才清待办。手工关闭/替换撤销关联,追改可复用,完成不证明成功。协议见 PRD §14。
- **原生皮肤**:template/dshell.css + dshell.html 为单一来源,经 /api/worktable/template 下发;新增样式只加模板。
- **提示词零泄漏(硬约束)**:所有对外生成的提示词(buildWindowTaskText 窗口任务提示词、
buildCustomLayoutPrompt 剪贴板布局提示词)禁止写入用户的个人工作区分组名(如 Projects /
DeepseekHarness)、其他用户的项目名与私人路径。剪贴板提示词会发给别人的 DSH,必须只含
插件通用知识;窗口任务提示词只发用户自己的会话,允许携带该项目自己的文件夹路径。
分组下拉只是会话创建工作区的选择,绝不进入任何提示词文本。
- **提醒红线**:need > done > busy,聚合父/子会话。ack 只存 opaque key/kind,不存正文;身份更换重新亮,读取副本与持久化同步清理,确认先记已见身份;旧无 key 退路无法识别直接替换。
- **控制室红线**:wt-console 固定首位、不可删除、唯一 console 标签不可关;隐藏项目不入网格。状态/计时/预览只镜像宿主,不为计时激活冷会话,不把完成标记当成功证明;业务临时持有结束释放。
- **更新检查/发布**:设置与控制室检查暂未去重,共用 updateCheck/lastUpdateCheck/skipVersion.v1,updateCache.v1 仅控制室。单次 8s、最多 3 次尝试;失败不可报最新。版本从 package.json 构建注入;新版本需 tag+Release,双资产固定名 dsh-worktable.tgz + 版本化名同 SHA。插件仅提示不自升级,安装/重启留给用户。
## link 加载兼容边界
- 不把旧宿主的祖先 node_modules junction 当通用修法。加载失败先核对宿主/profile/插件版本与第一条完整错误;不得直接创建更高层级链接、删除其目标目录或把社区桌面 resources/app 路径视为官方桌面的固定契约。
## 宿主历史补丁
- `02_process/upstream/utf16-picker-fix.patch` 是旧 picker 路径问题的存档,不代表当前宿主仍有此缺陷。先核对目标版本源码与复现,不自动套用或清理旧补丁。
## 安装 / 重启
- 注册:`dsh plugin --profile web add "link:<repo>/01_content"`(写 ~/.dsh,需用户授权)。
- 发布版安装(给用户):`dsh plugin --profile web add "https://github.com/Aisland-SJL/dsh-worktable/releases/latest/download/dsh-worktable.tgz"`(依赖每个 Release 的固定名资产)。
- Desktop:用实际安装目录 resources/runtime/cli/bin/dsh.cmd 对 desktop profile 安装;不得以网页 CLI 或不固定 npx 替代。安装前用户完整退出,安装后用户手动重开。
- bundle 启动时组合:网页重启 dsh web 并刷新;桌面完整退出/手动重开。不得自动重启正式宿主。