Files
dsh-worktable/AGENTS.md
T

15 KiB
Raw Blame History

dsh-worktable 项目规则

工作台容器插件:侧边栏里收纳 agent 级项目的「应用抽屉」。纯增量,不替换官方插件。

协作方式(用户定案,最高优先级)

与全局 ~/.dsh/AGENTS.md 同步;外部 Agent(如 Codex)不加载全局文件,故此处保留全文。

  • 设计先给最小版:任何 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,不宣称全功能验收;桌面仅列已测 Windows 0.2.0-rc.2 范围;macOS 为实验性支持(核心文件路径代码已做跨平台适配, 尚未真机端到端验证)。路径拼接必须走 pathutil.ts helper 或 Node path API,不手写分隔符。

构建与验证

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 严格结构)→ 版本一致性 → 会话/输入/传输/挂载回归(test:input / test:session / test:details / test:transport / test:widget 共 98 项,评估源码契约)→ 构建(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 回归(8 场景,评估安装产物 lib/client.js; 测试接受包路径参数)→ 服务端数据目录回归(3 组场景,评估安装产物 lib/index.js)→ dist/v版本号/ 双资产(终态恰好 2 文件 + 双 SHA 同源)。 脚本零 git/gh 动作,发布上传由 gh 手动完成。 发布禁止裸 npm pack 或手工 tar 生成发布包;脚本从仓库任意目录调用均安全(以自身位置解析)。
  • 配套检查入口:npm run test:gate = 工厂门禁 10 个失败/正向用例; npm run test:widget = 项目/会话归属、共目录、延迟结果与补挂重试 26 项回归(执行实际函数,不等于模型生成端到端验收); node 04_test/anchor-dom.test.mjs [lib/client.js 路径] = 分栏锚点 8 场景(缺省用工作目录构建产物); 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'+包内版本。

领域约定(会话中必须遵守)

实现参考 对应材料
控制室 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() 加分支。

  • 分栏引擎双版本锚点(v0.3.3 兼容契约,0.1.1-rc.2 与 0.1.2-rc.1):会话根三种结构—— 0.1.1 active = [头部, 滚动区];空会话 hero = [隐藏头部(headerHidden), 内容区];0.1.2 无会话 hero = [内容区](单子元素)。resolveAnchor:仅「存在第二个子元素」且第一个可见(自身有高度, 或零高槽位包装内第一个可见后代——visibleHeaderIn)才作头部;否则顶部取根顶部。phase 排序 active > hero > settling,settling 等待不关闭。同根重锚(hero↔active)只在锚点元素真正 更换时才更新 saved 原值,否则关闭时泄漏已应用的 margin/宽度变量。改锚点必须跑 anchor-dom 8 场景(含 H:0.1.2 零高包装+内部头部)。0.1.2 的 children[0] 是 display:contents 式零高包装、真实标题栏在内部——不要凭 0.1.1 的结构想当然。

  • 数据目录解析无循环(v0.3.3 红线):resolveDshHomeSafe = 官方 @deepseek-ai/dsh-home-paths (可用时)→ baseDshHome 兜底;loadPkg 的 profiles 兜底只能用 baseDshHome,禁止回调 resolveDshHomeSafe(会成环)。baseDshHome 规则对齐官方:DSH_HOME 优先、空/纯空白视为未设、 ~ 与 ~/ 与 ~\ 展开、相对路径按 cwd、默认 ~/.dsh;trim 只用于空白判断、路径保留原字符串。 不把 dsh-home-paths 声明为生产依赖(其 peer cordis ^4.0.2 不满足 0.1.1-rc.2 的 4.0.1, 已试过并撤回)。改这块必须跑 server-home 3 组场景 + 突变体验证(恢复循环测试必须红)。

  • 新会话策略:创建后应用默认预设与有效模型;桥接保持原选择策略,持有结束即释放。失败不重复发送;插件导航须 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。插件仅提示不自升级,安装/重启留给用户。

  • 特定旧版 0.1.1-rc.2 的裸包 import / link peer 解析曾使用上级 node_modules junction 兜底,不是通用修法。 本机 0.2.0-rc.2 经无旧链接冷启动与真实工作台/控制室检查后已移除该链接,原目标目录保留。 重新遇到加载失败时先查实际宿主、profile、插件及第一条完整错误;不得直接创建更高层级链接或删除它指向的依赖目录。

宿主 bug 跟踪(UTF-16 路径截断)

  • 官方 native picker 的 readUtf16 只看 UTF-16LE 低字节,含 U+XX00 字符(开/一/言/Ā/🀀 等)的路径被截断。非我们插件问题。 修复与回归测试存档:02_process/upstream/utf16-picker-fix.patch;官方 Discussions #580 已接单。 待办:官方修 master 后核验对应 npm 版本是否含修复,随后可清理 patch 存档。

安装 / 重启

  • 注册: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 并刷新;桌面完整退出/手动重开。不得自动重启正式宿主。