hyzyn/dsh-plugin-kit

codegraph

DSH Web GUI 的 Codegraph 集成:设置 → 插件 中的「Codegraph」卡片提供索引状态、符号搜索、callers/callees/impact 查看与一键 sync/index;托管 codegraph MCP 服务器,并向 system prompt 注入 Codegraph 使用指引。需安装 codegraph CLI。

工具与能力codegraphdeepseek-harnessdsh

@hyzyn/dsh-codegraph

中文 | English

DSH 设置 → 插件 里的「Codegraph」卡片:把代码图谱的状态、符号搜索、调用链搬进 GUI,并替你把 codegraph MCP 服务器的项目目录对齐好。

特性

  • `status --json` 字段分区呈现:initialized / version / projectPath / fileCount / nodeCount / edgeCount / lastIndexed / pendingChanges / languages / dbSizeBytes 分区铺开,原始 JSON 收进 `;符号下钻输出带行号的 verbatim 源码 + callers / callees / impact 三段关系。CLI 的过期信号(reindexRecommended / builtWithVersion / 提取器版本差 / worktreeMismatch`)会在面板上给出一行警告并把「重建索引」标成建议动作。
  • 索引两条路径、超时分两档:sync -- 增量、index [--force] -- 全量重建走 indexTimeoutMs(默认 600s,0 = 不限时);status / query / callers / callees / impact / node 走 cliTimeoutMs(默认 60s,0 = 不限时)。超时与取消都走「SIGTERM → 3s 清理窗口 → SIGKILL」的进程组升级链,POSIX 与 Windows 同一套语义;索引类操作进行中卡片有「取消」按钮,关闭页面也会中止 CLI。
  • 托管 MCP 服务器行:dsh-mcp-client 不声明 MCP roots,codegraph serve --mcp 只从 process.cwd() 向上解析 .codegraph/;插件在 ~/.dsh/cordis.patch.yml 维护 @deepseek-ai/dsh-mcp-client 行并写 config.cwd,改写经 watchUserPatches 热加载重建 MCP 连接。同一台服务器同一时刻只挂一个项目,其余项目用 projectPath 查询。
  • **索引判定取 .codegraph/*.db 且与 CLI 同口径向上解析**:从目标目录向上(到 git 根为止)找第一个带索引库的 .codegraph/,命中根即项目根——monorepo 子目录里的会话不再被误判「未索引」。只看目录存在会把 codegraph CLI 自身的安装目录 ~/.codegraph 判成项目索引,托管行随之落在未索引 cwd 上——实测该状态下 codegraph_explore 的 required 由 ["query"] 变为 ["query","projectPath"]。非真索引时不改写现有 cwd,卡片给出 indexState 与原因。
  • 写入纪律:对 ~/.dsh/cordis.patch.yml 的每次写都带 mtime+size 盖章复核(dsh-mcp 同款 CAS,≤3 次重读);复用 dsh-mcp 区块时只定点改 codegraph 行自己的 cwd: 一行,区块里的注释、其它服务器行、loader 合法的 override 条目逐字节保留;区块损坏时拒绝追加第二个区块(serverName 撞名会让 codegraph MCP 整体加载失败),把原因报给卡片。
  • 托管行 cwd 跟随活动会话:followSession(默认开)在会话切到有效索引项目时对齐托管行,否则回落到绑定路径;绑定路径由「设为默认项目」写入 settings 命名空间 codegraph 并把 followSession 置 false(显式指定优先于会话跟随)。会话上报失败会退避重试,不再一次抖动就永久失效。
  • 可选的 per-agent MCP 隔离(mcpScope: 'per-agent',默认 'managed'):默认模式下一台 MCP 服务器按时分复用服务所有项目(靠 projectPath 与跟随会话)。开启 per-agent 后,每个 agent 在自己的 Cordis scope 里挂一份独立的 dsh-mcp-client,cwd 固定为它自己会话目录解析出的索引根——多项目并行时不再共享一个全局 cwd,也不再需要「写盘 → 热加载 → 重建连接」那条链;全局托管行会被挂起(disabled: true,切回 managed 自动恢复)。代价是每个 agent 一个子进程(实测空 Node 基线约 40MB)。会话目录没有可用索引时该 agent 不挂(而不是回落到默认项目)——否则它会拿到别的项目的上下文,这正是「宣称避免、实现却没做到」的那类错误。机制实测与取舍见 docs/p0-plan.md。

挂载模式以持续生效为准,不是启动时的一次判断:宿主 loader 会并发创建插件入口,agents 服务可能比本插件晚就绪,所以插件在服务到货时会重新裁决并补挂(codegraph CG69)——不会因为一次启动时序就永久停在 managed。

  • 一键初始化:未初始化的目录在卡片上直接点「初始化索引」跑 codegraph init(两步确认)——index / sync 都要求项目先 init 过,此前这是唯一还要把用户赶回终端的一步。
  • systemPrompt 分两段,开关与门禁各自独立:plugin:dsh-codegraph(order 150)与 plugin:dsh-codegraph:usage(order 151);两段均以 --version 探测为前置,announceToAgent / usageGuidance 写 settings 命名空间后即时增删 section。

为什么要托管 MCP 的工作目录

DSH 的 MCP 客户端不声明 MCP roots 能力,codegraph serve --mcp 启动后只能从进程工作目录向上查找 .codegraph/。而 dsh web 经常从家目录启动——这时模型调用 mcp__codegraph__* 一律拿到:

No CodeGraph project is loaded for this session.
Searched for a .codegraph/ directory starting from: /Users/you

本插件解决这个问题:在 ~/.dsh/cordis.patch.yml 托管一行 codegraph MCP 服务器(codegraph serve --mcp,cwd = 默认项目路径)。默认项目路径变化(卡片「设为默认项目」或配置修改)即重写该行,DSH 的 watchUserPatches 热加载后自动把 MCP 服务器挂载到新项目。

行为细则:

  • 优先复用 @hyzyn/dsh-mcp 托管区块里已有的 codegraph 行(只补 cwd,其余字段含禁用状态不动),没有才写本插件自己的区块,避免 serverName 撞名。
  • 区块外的手工行只检测不碰(避免冲突)。
  • 目标路径必须有真索引才托管:.codegraph/ 里得存在索引库(.db)。只看目录存在是不够的——codegraph CLI 把自己的安装数据放在 ~/.codegraph(current -> versions/、bundles/、codegraph.lock,没有任何索引库),于是家目录会被误判成「已索引项目」,插件把 MCP 的 cwd 钉在家目录上并报告一切正常,而 codegraph status --json -- ~ 实际返回 initialized:false,工具照旧拿 "No CodeGraph project is loaded"。命中这种情况时插件不改写现有 cwd、不凭空建行,并在卡片上提示「默认项目 X 不是有效索引」+ 一键修复路径。
  • 多项目使用:一台 codegraph MCP 服务器同一时刻挂载一个默认项目;其它已索引项目可在工具调用里传 projectPath 查询,或回卡片一键切换。
  • 跟随语义:托管行 cwd = 「跟随开启 + 会话目录是有效索引」时取会话目录,否则取绑定路径。有效索引按 .codegraph/ 里存在索引库判定(不是目录存在),所以家目录那种 ~/.codegraph 安装目录不会把 cwd 带偏;项目被 uninit 后也会自动回落,不留陈旧状态。
  • 跟随由浏览器半体上报(页面加载即订阅活动会话,与设置面板是否展开无关):宿主侧没有「当前会话」这个信号,因此跟随只在有 GUI 页面打开时生效,其余情况用绑定路径。
  • 关闭方式:插件配置 mcpIntegration: false(会撤销本插件写入的托管行)。

CLI 面

卡片此前只覆盖 status/query/callers/callees/impact/node/sync/index/init,其余子命令只能去终端。现在补齐了旗舰与常用的那几个:

  • `explore`:与 MCP 的 codegraph_explore 同输出(相关符号源码 + 调用路径)。模型那条路走 MCP,人在卡片上此前够不着同一个能力——这有点荒谬,现在补上了。
  • `context`:为一个任务组装上下文(与 explore 面向「区域」的区别)。
  • `files`:索引里的文件结构(语言 / 符号数 / 大小)。
  • `affected`:由改动文件反向查出受影响的测试(实测本仓库:改 src/index.ts → 5 个测试文件)。这条对「我改了这里,该跑哪些测试」特别实用。插件刻意不替你猜改动列表(不读 git status、不猜编辑器状态):待测文件从哪来是上游流程的事,卡片只负责把你给的列表交给 CLI。
  • `uninit`:删除 .codegraph/——与 init 反向,是本卡片第二个破坏性动作,同样两步确认。实测必须带 `-f`:不带时 CLI 问 Continue? (y/N),而运行器没有 TTY、读到 EOF 就中止且不删除(安全但无效),所以确认由卡片负责。
  • `telemetry`:只读转达上游匿名用量统计的状态。刻意不给开关——那是用户的全局偏好(~/.codegraph/telemetry.json,由 CLI 自己的 telemetry on|off 管),插件替他翻等于越权改别人的全局设置。
  • 查询参数面板:query -k/--kind(类型过滤)、callers|callees -l/--limit(CLI 默认 20,会把大符号截断而卡片此前无从知道)。

kind 用自由文本而不是下拉枚举:上游加新 kind 时插件不必跟着改,填错 CLI 自己回空结果。

项目列表(一键切换)

一台 codegraph MCP 服务器同一时刻只挂一个项目,所以「换项目」是高频动作,而卡片此前只能手敲绝对路径。GET /projects + 卡片上一排胶囊按钮解决它。

候选只用界内的数据源:宿主 sessions.list() 的活跃会话 header.cwd,以及插件自己观察到的路径(/follow 上报、查过 /status 的目录、当前默认项目)。刻意不读 `~/.dsh/sessions/`——那里确实有历史项目路径,但那是 DSH 的内部存储格式(会话桶名是路径编码、日志是多帧 zstd),插件解析它会在 DSH 改格式时静默失效;实测按桶名解码 51 个目录,一个都没解对。宁可比用户记忆少几个项目,也不要一个「有时准有时不准」的列表。

每个候选现算 locateIndex:只有真索引才可作为切换目标;未索引的也列出但禁用(用户会想知道「这个项目还没索引」),点击无效。列表按「最近见过」排序,容量 50 条、淘汰最久未见者。切换复用「设为默认项目」那条链路(持久化 defaultPath → 关闭跟随 → 热切换 MCP)。

API

路由 · 方法 · 说明 /api/dsh-codegraph/status?path= · GET · 索引状态(JSON) /api/dsh-codegraph/query?q=&path=&limit= · GET · 搜索符号 /api/dsh-codegraph/callers?symbol=&path= · GET · 查调用者 /api/dsh-codegraph/callees?symbol=&path= · GET · 查被调用者 /api/dsh-codegraph/impact?symbol=&path=&depth= · GET · 查影响面 /api/dsh-codegraph/node?name=&path= · GET · 查符号/文件详情 /api/dsh-codegraph/sync · POST · 增量同步 { path } /api/dsh-codegraph/index · POST · 全量重建 { path }(要求项目已经初始化过,见下) /api/dsh-codegraph/init · POST · 在未初始化的目录跑一次 codegraph init(建 .codegraph/ + 首次索引),成功后重算 MCP 托管行;已索引目录回 409 /api/dsh-codegraph/default-path · GET · 绑定路径 defaultPath + 生效路径 effectivePath + sessionPath + followSession + 提示词开关 + cliAvailable / cliProbeError / cliProbeAt + MCP 托管状态(indexState 针对生效路径) /api/dsh-codegraph/follow · POST · 上报活动会话目录 { path }(空 = 无活动会话);宿主据此对齐托管行 cwd,非索引目录自动回落 /api/dsh-codegraph/settings · POST · 写开关 { announceToAgent?, usageGuidance?, mcpIntegration?, followSession? }(布尔)与 { mcpScope? }('managed' / 'per-agent',非法值 400),即时生效 /api/dsh-codegraph/default-path · POST · 设为默认项目 { path }(需 .codegraph/ 里有索引库),同步热切换 MCP /api/dsh-codegraph/reprobe · POST · 重跑一次 --version 探测,回 { cliAvailable, cliProbeError, cliProbeAt } 并同步 systemPrompt 门禁 /api/dsh-codegraph/files · GET · 文件结构 { path, files, raw }(codegraph files --json);旋钮 filter / pattern / maxDepth /api/dsh-codegraph/affected · GET · 受影响测试 { path, affected, raw }(codegraph affected --json -- );files 可重复传 /api/dsh-codegraph/explore · GET · 探索 { path, query, output }(codegraph explore,与 MCP 的 codegraph_explore 同输出,markdown);旋钮 maxFiles /api/dsh-codegraph/context · GET · 任务上下文 { path, task, output }(codegraph context,markdown);旋钮 maxNodes /api/dsh-codegraph/uninit · POST · 删除 `.codegraph/` { path }(codegraph uninit -f);未索引目录回 409;撤销的是索引所在的根 /api/dsh-codegraph/telemetry · GET · 只读转达上游匿名用量统计状态 { enabled, output } /api/dsh-codegraph/projects · GET · 已见项目列表 { projects, indexedCount, effectivePath }——候选来自活跃会话与插件观察到的 cwd(/follow 上报、查过 status 的路径、默认项目),每条现算索引态;monorepo 子目录归并到索引根 /api/dsh-codegraph/agents · GET · per-agent MCP 挂载台账 { mode, requested, effective, reason, fallback, mounted, live, agents }——每个 agent 挂没挂上、挂在哪个索引根、没挂的原因(只读) /api/dsh-codegraph/unlock · POST · 清挡住索引的陈旧锁 { path }(codegraph unlock,幂等:没锁时 exit 0) /api/dsh-codegraph/cancel · POST · 取消进行中的 CLI 调用 { path? }(合法 JSON body:{} = 全部;畸形 body 回 400,不会升级成「取消全部」);关标签页的断连也会自动中止对应调用 /api/dsh-codegraph/diagnose · GET · 收集诊断包 { path?, report }:report 是一段纯文本,含版本/平台、CLI 探测实测原文、索引状态、托管行与脱敏后的补丁区块原文、~/.codegraph 的 daemon 登记与日志尾、最近一次 CLI 失败、以及采纳率 /api/dsh-codegraph/metrics · GET · 采纳率 { path? }:不带 path 回全部项目 { summaries, since };带 path 回该项目 { project, summary, text, since }

所有路由均为 loopback-only,防止远程访问。reprobe 只认 POST:它会真的起一个子进程,不该由 GET 顺带触发。diagnose 只认 GET(只读),但它可能补跑一次探测,所以不会在写路径上被顺手调用。

诊断包的脱敏口径(diagnose):~/.dsh/cordis.patch.yml 是所有 MCP 服务器共用的文件,别家的行里可能有 headers / env 这类凭据,所以只摘 codegraph 相关的两个区块(本插件区块 + dsh-mcp 卡片区块),并按键名处理——id / name / serverName / transport / command / args / cwd / disabled 留值(这些正是排查要看的东西),其余键一律 `,且 headers: / env: 这类敏感键会污染整棵子树(连 authorization:` 的值一起抹掉,但键名保留)。新增字段默认脱敏(fail-closed)。

采纳率仪表

卡片上那行「采纳率」回答的是配置之外的问题:配好了之后,模型到底买不买账。

  • 数据来源:宿主既有的 session/event 事件流(只数 tool/call,与 dsh-agent-instructions / dsh-acp 同一个公开订阅面)。不数 tool/result——采纳率问的是「模型想不想用」,失败了也是想用(那是另一个问题,由诊断包回答)。
  • 分子/分母(两个口径,窄口径是主口径):codegraph 命中 mcp__codegraph__*;窄口径分母是「发现类」调用(grep / glob / search / find / list_dir),宽口径分母还包含 read / view / open。之所以两个都报:真实历史里 read 占 1956 次而 grep 只有 100 次,而 read 多半是「打开已知道要改的文件」——codegraph 替代的是「找东西」,把 read 算进主口径会把数字永久压在个位数(实测宽口径 2.2% vs 窄口径 28.8%)。
  • `bash` / 媒体 / 网络类不计入分母:模型用 bash 干的事大部分(跑测试、装依赖、git)与代码探索无关;read_image / read_pdf / web_search 同理(这一条是拿真实历史量过之后补的,见下)。
  • 实测数字见 docs/adoption-audit.md:从 177 个真实历史会话回溯——会话级 23%、窄口径 28.8%、最近三天 47%,并给出 codegraph 调用 query 的质量抽样(34/47 带具体符号名)。
  • 分母为 0 显示「还没有探索类调用」而不是 0%:「一次都没探索」与「探索了但全用 grep」是两回事。
  • 项目键 = 索引根(resolveIndexedRoot),与托管行 cwd、注入门禁同一口径;所以同一个仓库的多个会话(换会话、子 agent、重启宿主)会归并成一条,而不是散成一堆没有统计意义的小样本。未索引项目也会记,但文案会标明「这个数字不代表提示词效果」,且 /metrics 额外给 grouped.indexed / grouped.unindexed 两组合计——未索引项目里模型本就不该用它,混进整体会得出没有意义的数字。
  • 只在内存里,宿主重启即归零,since 会如实给出起点。不落盘是有意的:它是「现在要不要调提示词」的观测值,不是审计日志,落盘会把工具名与项目路径长期留在磁盘上。
  • 同一份数据也进诊断包(/diagnose),因为报告「codegraph 好像没效果」时,第一个要分清的就是「模型根本没用」还是「用了但结果不对」。

请求语义(v0.4.2 起):

  • POST 路由对畸形 / 超限 / 非对象的 body 一律 400——不会把「请求体没读出来」当成「没指定路径」而在默认项目上执行写操作。/cancel 也在这道门禁内:{}(合法 JSON、未指定 path)才是「取消全部」,畸形 body 不会顺带把别的项目正在跑的索引杀掉。
  • 路径必须真实存在且是目录:status / query / callers / callees / impact / node 对不存在的路径回 400(CLI 对不存在的路径会 exit 0 + 空结果,静默得像「没有匹配」);--limit / --depth 只收正整数(limit 上限 10000,depth 的上限由 CLI 自己钳到 10)。
  • 祖先口径:/default-path 的 POST 在 monorepo 子目录上会绑定索引所在的仓库根;对子目录调 /init 回 409(祖先已有索引,避免建出嵌套索引);/follow 拒绝不存在目录的路径上报。

兼容性(DSH / codegraph CLI)

  • DSH:宿主版本矩阵(每一档都注明验证方式,别把回归测试说成兼容性声明):
  • `0.1.7-rc.1`(上一轮适配基线,2026-09-23 实测):node scripts/verify-codegraph-host-contract.mjs --profile test --port 3087 --dsh-bin --runtime-store 42/42 通过——25 条路由全在(含 /projects、/metrics、/diagnose、/unlock、/files、/affected、/explore、/context、/uninit、/telemetry、/agents)、POST 门禁与 loopback 门禁成立、/diagnose 输出分段完整、浏览器半体产物可供给且含最新 UI、MCP 托管行按真索引写入 home 补丁、mcpScope 开关「切 per-agent → 全局行被挂起 → 切回 managed 恢复」全程可逆且真实 ~/.dsh/cordis.patch.yml 逐字节未变。新增的两项专门验 0.1.7 的 settings 迁移:启动后经插件自己的写路径把 mcpScope 切回 managed 基线、并把默认项目切到临时项目,两项都要求 HTTP 200(旧 API 下这两步只会 500)。
  • 验证方式说明:本机全局 dsh 若不是 rc.1,可用 --dsh-bin 指向一份独立安装的 rc.1,并用 --runtime-store 把复制出来的临时 profile 的 node_modules/@deepseek-ai/* 重指到该安装的运行时——否则那些链接仍指向全局旧安装,新 cohort 的兼容性 preflight 会把整套旧运行时行判成 incompatible 而全部禁用(宿主根本起不来,与本插件无关)。
  • 另一条已跑过的真机证据:隔离 DSH_HOME 启动 test profile(同样做运行时重指)→ 10 个插件全部 mounted、0 行 disabling profile plugin row,POST /api/dsh-codegraph/settings 在 per-agent / managed 之间往返都是 200。
  • `0.1.7-rc.2`(当前适配基线,2026-09-25 实测):cohort 从 rc.1 提到 rc.2,peerDependencies 与 dsh.engines.dsh 的下限同步提升。node scripts/verify-codegraph-host-contract.mjs --profile test --port 3087(本机全局就是 rc.2,无需 --dsh-bin / --runtime-store)42/42 通过——25 条路由、POST / loopback 门禁、/diagnose 分段、浏览器半体可供给、MCP 托管行写入隔离 home 补丁、mcpScope 在 per-agent / managed 间往返可逆且真实 ~/.dsh/cordis.patch.yml 逐字节未变。另两项:真宿主加载 test profile 时 10 个插件全部 mounted、0 行 `disabling profile plugin row`;工作区 pnpm -r typecheck 全绿、vitest 960/960、pnpm -r build 绿。本轮未改任何插件运行行为(只有兼容性声明、文档与 lockfile)。rc.1 → rc.2 的 lib 产物逐文件比对:dsh-tools 仅新增可选 PreToolDecision.displayReason(本仓不构造该决策)、dsh-session-query 仅 JSDoc、dsh-client-ui-theme 仅设计 token 增补,dsh-mcp-client / dsh-host-webserver / dsh-subprocess-local / dsh-credentials / dsh-base / dsh-settings / dsh-web-app / dsh-headless 逐字节相同;dsh-app-boot 删除了 skippedProfileBundles 并改了 generateConfigSchema 签名(去掉首参 binName),但那是 DSH 自己的 CLI 与 app-boot 之间的内部面,本仓无任何引用。
⚠️ 标注(本次未擅改)——两个测试总数不是同一次运行的产物:本条写的是 **rc.2 那一轮验证
时点的用例数(`960/960`);本轮项目级文档改造之后,当前值为全仓 56 files / 965 tests**
(packages/codegraph 本包 14 files / 317 tests)。两个数都不改——当前值以
ROADMAP.md 的「2026-09-25 复测」表为准。
本条的「25 条路由」同理:以命令现算为准,别把它当契约(现算命令见 ROADMAP 复测表)。
  • `0.2.0-rc.1`(同轮新增的第二档基线,2026-09-28 实测):不改任何运行代码,只把兼容范围写成两段(见下两条)。证据:① 用 0.2.0-rc.1 自己的判定器(dsh-app-boot 的 evaluatePluginCompatibility)对本仓 13 个可安装包逐一核对:单段 ^0.1.7-rc.2 时 12/12 全被拒(^0.1.7-rc.2 展开是 >=0.1.7-rc.2 <0.2.0-0,0.2.0-rc.1 恰好卡在 -0 上,includePrerelease: true 也救不回来),改成两段后逐 cohort 全部放行。② 隔离 DSH_HOME 在真 0.2.0-rc.1 宿主上:dsh plugin add 不需要任何豁免就装上 10 个插件,启动后 10 个全部 mounted、无 skipping profile bundle;verify-client-ui 5/5 PASS、verify-rss-opml 9/9 PASS(真实下载 + 回环)。③ 同一批包在 0.1.7-rc.2 宿主上回归同样全绿(client-ui 5/5、rss-opml 9/9)。④ 两档的插件 API 面逐文件比对:dsh-tools / dsh-client-ui-slots / dsh-client-locale / dsh-host-webserver / dsh-subprocess-local / dsh-client-modules / dsh-package-manifest 的 .d.ts 逐字节相同,plugins.row.config 槽契约相同,cordis 4.0.4 与 schemastery 3.18.4 同版本。
  • `0.2.0-rc.2`(同轮追加的第三档基线,2026-09-29 实测):不改任何运行代码,只把兼容范围追加第三段(见下两条)。注意这一段与前两档不同:0.2.0-rc.2 本来就被 `^0.2.0-rc.1` 覆盖(>=0.2.0-rc.1 )。② 隔离 DSH_HOME 在真 0.2.0-rc.2 宿主上:dsh plugin add 不需要任何豁免就装上 10 个插件;--dump-config 的 stderr 0 行、10 条 @hyzyn bundle 全在组合结果里;启动后 10/10 mounted、无 `skipping profile bundle`。③ 同一宿主上的三层真机验收:verify-client-ui 6/6 PASS(10 个 @hyzyn 客户端 bundle 真在 Chrome 里被加载、0 未捕获异常、0 error 级控制台输出、0 失败子请求)、verify-rss-opml 9/9 PASS(真实下载 + 导入回环 + 收尾复原)、verify-codegraph-host-contract 42/42 PASS(25 条路由、POST / loopback 门禁、/diagnose 分段、浏览器半体可供给、MCP 托管行写入隔离 home 补丁、mcpScope 在 per-agent / managed 间往返可逆,真实 home 的 cordis.patch.yml 逐字节未变)。④ rc.1 → rc.2 的插件 API 面逐文件比对:cli + web-app 的全部依赖包 166 个里只有 40 个有真实改动,本仓 import 的宿主包(dsh-base / dsh-tools / dsh-client-ui-slots / dsh-web-app / dsh-plugin-manager / dsh-mcp-client / dsh-settings / dsh-system-prompt / dsh-credentials / dsh-scope / dsh-session-query / dsh-launch-environment / dsh-agent / dsh-hmr 等)零代码变化;唯一与本仓相关的是 dsh-cordis-client-runner 的 slot 目录 97 → 98 条、只增不减(新增 sidebar.right.tab.files.actions),本仓注册的 4 个槽(plugins.row.config / settings.kit.item / settings.plugin.item / sidebar.right.pane.tab)逐字节相同。⑤ 工作区回归:pnpm -r build / pnpm -r typecheck 绿、pnpm test 92 files / 1484 tests 全过;本轮只动兼容性声明与文档,未动任何插件运行行为。
  • `0.2.1-alpha.1`(第四档基线,2026-10-03 实测):不改任何运行代码,只把兼容范围追加第四段(见下两条)。这一段与第三档同类,也是显式声明而不是修边界:0.2.1-alpha.1 本来就被 `^0.2.0-rc.2` 覆盖(>=0.2.0-rc.2 四档 × 13 条目全部放行。② 隔离 DSH_HOME 起真宿主(复制本机 test2 profile,--patch 固定端口):10/10 个 `@hyzyn` 插件 mounted、0 行 `disabling profile plugin row`、0 行 `skipping profile bundle`;本机真实 test2 profile 上再跑一遍安装期判定:dsh plugin --profile test2 add link:(10 个)全部返回 `Already up to date`、无 `incompatible-version`、不需要任何豁免(安装前与启动时各过一次)。③ 同一宿主上的四层验收:verify-client-ui --mode boot 5 PASS / 0 WARN / 0 FAIL、--mode full 17 PASS / 1 WARN / 0 FAIL(10 个 @hyzyn 客户端 bundle 真在 Chrome 里被加载、0 未捕获异常、0 error 级控制台输出、0 失败子请求;--mode full 另证 8/8 个插件的内联配置卡片都渲染出来(plugins.bundle.config + 行标记 plugins.row.config)与设置里「插件配置」行的 8 张 kit 卡片,唯一 WARN 是沙箱里 PTY 被拒 posix_openpt failed,不是回归)、verify-mcp-http 7 PASS / 0 FAIL(三种 streamable-http 响应模式 + 打不通时可读失败)、verify-rss-opml 9 PASS / 0 FAIL(真实下载 + 导入回环 + 收尾复原)、verify-codegraph-host-contract 42/42 PASS(25 条路由、POST / loopback 门禁、/diagnose 分段、浏览器半体可供给、MCP 托管行写入隔离 home 补丁、mcpScope 在 per-agent / managed 间往返可逆,且真实 ~/.dsh/cordis.patch.yml 逐字节未变)。④ 0.2.0-rc.2 → 0.2.1-alpha.1 的插件 API 面逐文件比对:CLI 包 20 个文件里只有 package.json 变;本仓 import 的宿主包(dsh-base / dsh-host-webserver / dsh-subprocess-local / dsh-client-locale / dsh-client-ui-slots / dsh-settings / dsh-session-query / dsh-mcp-client / dsh-system-prompt / dsh-launch-environment / dsh-client-ui-theme / dsh-headless 等)零代码变化;dsh-tools 只改 run_code 的参数顺序与文案、并删掉 ./invariant 子路径导出(本仓零引用,index.d.ts 逐字节相同),evaluatePluginCompatibility 语义等价;dsh-cordis-client-runner 的 slot 目录 98 → 100 条、只增不减(新增 plugins.add.actions / shell.bottom),本仓注册的槽全在。⑤ 运行时依赖版本漂移:这一档的宿主带 cordis 4.0.5-alpha.1 与 schemastery 3.18.5-alpha.1(前三档都是 4.0.4 / 3.18.4),但两份 tarball 除 package.json 外逐字节相同,且 app-boot 用 Symbol.for('schemastery') 判定原生 schema(不是 instanceof);本仓的 pnpm -r typecheck 正是重链到这两个新版本之后跑的,绿。⑥ 工作区回归:pnpm -r build / pnpm -r typecheck 绿、pnpm test 106 files / 1644 tests 全过、pnpm aggregate 无 diff;本轮只动兼容性声明、守卫与文档,未动任何插件运行行为。未跑:pnpm live-smoke(它把一次性 profile 复制到 ~/.dsh/profiles/live-smoke-*,本轮执行环境只允许写工作区,~/.dsh 写入被拒;需要时在本机普通终端补跑)。
  • `0.2.1-alpha.2`(第五档基线,2026-10-09 实测):不改任何运行代码,只把兼容范围追加第五段(见下两条)。这一段与第三、四档同类,也是显式声明而不是修边界:0.2.1-alpha.2 本来就被 `^0.2.1-alpha.1` 覆盖(同一个 0.2.1 版本元组,预发布之间不比大小),但照样走完「每加一档都必须实测」的全部动作。证据:① 用 0.2.1-alpha.2 自带的判定器(本机全局安装里的 dsh-app-boot,同一次安装的 getDshRuntimeVersion() 报 0.2.1-alpha.2)对本仓 13 个条目(12 个可安装包 + templates/hello)逐一核对:13/13 放行;node scripts/check-dsh-peers.mjs --with-app-boot(自动定位到那份判定器)五档 × 13 条目全部放行。② 本机真实 test profile 上跑安装期判定:dsh plugin --profile test add link:(10 个)全部返回 `Already up to date`、无 `incompatible-version`、不需要任何豁免;随后 dsh --profile test --patch 起真宿主(runtime 0.2.1-alpha.2):10/10 个 `@hyzyn` 插件 mounted、0 行 `disabling profile plugin row`、0 行 `skipping profile bundle`。③ 同一宿主上的验收:verify-client-ui --mode full 18 PASS / 0 WARN / 0 FAIL(10 个 @hyzyn 客户端 bundle 真在 Chrome 里被加载、8/8 个插件的内联配置卡片都渲染出来(plugins.bundle.config + 行标记 plugins.row.config)、设置里「插件配置」行的 8 张 kit 卡片、全局搜索命令面板往返真的有结果、终端面板 xterm 初始化完成、0 未捕获异常、0 error 级控制台输出、0 失败子请求;这一轮 PTY 没被拒,所以第四档那条 WARN 也不在)、pnpm live-smoke(= live-host-smoke --bootstrap --strict)18/18 PASS(两个宿主实例的授权阶梯、路由门控、工具清单、试连文案、宿主正服务的 client.js)——第四档那条「未跑」在这里补上了。④ 0.2.1-alpha.1 → 0.2.1-alpha.2 的插件 API 面逐文件比对:本仓 import 的宿主包里 dsh-mcp-client(MCP 托管行)/ dsh-settings / dsh-credentials / dsh-launch-environment / dsh-sandbox / dsh-scope / dsh-user-approval / dsh-agent / dsh-client-locale / dsh-subprocess 除 `package.json` 外逐字节相同;dsh-tools 里本仓用的 defineTool 导出行逐字节相同,改动只有 ToolPresentationMode 去掉 'both'(本仓零使用)与 PTC 内部桥 RunCodeBridgeOptions 多一个必填的 resolveWorkingDirectory(本仓没有实现方);客户端槽面只增不减(新增 conversation.chat.flow / conversation.chat.reasoning.body,零删除零改名,本仓注册的槽一个没被拿掉);dsh-host-webserver 与 dsh-web-app 的改动都在宿主配置面(监听地址只收环回或具体网卡、新增 TLS 配置、webRuntime → webStartup),本仓对这些名字零引用。⑤ 运行时依赖:这一档的宿主带的仍是 cordis 4.0.5-alpha.1 与 schemastery 3.18.5-alpha.1(与第四档同一对)。⑥ 工作区回归:pnpm -r build / pnpm -r typecheck 绿——**且是在 node scripts/link-dsh-runtime.mjs 把本仓 node_modules/@deepseek-ai/* 指到 alpha.2 那套之后跑的(即类型面就是 alpha.2 的 `dsh-tools`,不是仓里 devDeps 钉的旧档),`pnpm test` 139 files / 2420 tests 全过、`pnpm aggregate` 无 diff。⑦ 同轮还按宿主的升级绊线复核了 `docs/os-consent-plan.md` §2.1 的三条事实与 picker 口径:宿主这一档真的换了口径**(环回判定改用 dsh-host-webserver 的 isLoopbackHost()、新增 allowsRemoteAuthorities 一条),绊线按设计报红、复核记录写进该文 §2.1 末与 §4 第 2 条。
  • `0.2.1-alpha.5`(第六档基线,2026-10-17 实测):加档本身只改 `peerDependencies` 的范围串与文档(本包运行代码未动)。与第三~五档同属显式声明:0.2.1-alpha.5 本来就被 `^0.2.1-alpha.2` 覆盖(同一个 0.2.1 版本元组,预发布之间不比大小)。⚠️ 但它与前面几档不同——这一档带着真实的宿主行为变化进来,「对宿主行为零差异」那句话在这里不成立,所以证据里多了「本仓引用面」这一项(RELEASING.md 的加档规则也据此补了警告)。证据:① 用 0.2.1-alpha.5 自带的判定器(本机全局安装里的 dsh-app-boot,getDshRuntimeVersion() 报 0.2.1-alpha.5)对本仓 13 个条目(12 个可安装包 + templates/hello)逐一核对:13/13 放行;node scripts/check-dsh-peers.mjs --app-boot 六档 × 13 条目全部放行。② 真宿主组合:dsh --profile test --dump-config 里 10 个 @hyzyn bundle 全在,0 行 `disabling profile plugin row`、0 行 `skipping profile bundle`(stderr 另有 2 行是该 profile 自己 cordis.patch.yml 里两个不存在的 entry——devfront / devenv,与本轮无关、也不是禁用行)。③ 真宿主加载(本轮动了 preflight 会读的范围串,所以这条最要紧):pnpm live-smoke(= live-host-smoke --strict)18/18 PASS——两个一次性宿主实例都跑在 0.2.1-alpha.5 上,授权阶梯、路由门控、agent 工具清单、宿主正服务的 client.js 全过。④ 本仓引用面(0.2.1-alpha.2 → 0.2.1-alpha.5:18 个内联包的 130 个 d.ts 里 115 个逐字节相同):dsh-mcp-client / dsh-base / dsh-settings / dsh-client-locale / dsh-session-query / dsh-sandbox / dsh-launch-environment / dsh-headless / dsh-user-approval / dsh-subprocess 的 lib/ 逐字节相同;dsh-tools 里本仓用的 defineTool 未变;被删除或改签名的 HARNESS_SOURCE_SECTION / addHarnessSourceSection / createRunCodeTool 新增必填参 / probeLinuxScope 一族 / dsh-host-webserver 的 get port(): number | undefined / dsh-credentials-local 的 ignoredKeys——本仓全部零引用或零影响。⑤ 四条行为变化都已落进本仓(这才是这一档真正的内容):判定器字段名 peers → requirements(alpha.4)、顶层 engines.dsh 开始短路 peer(alpha.4)、凭据层级翻面(alpha.3)、macOS 原生 picker 换 JXA + 取消改由空 stdout 表达(alpha.5)——对应改动见 scripts/check-dsh-peers.mjs 头部、packages/env 的启动期预注入、docs/os-consent-plan.md §2.1。⑥ 工作区回归:pnpm typecheck / pnpm build / pnpm aggregate 绿、pnpm test 149 files / 2554 tests 全过(该数字含同轮另一条并行工作线的用例;本包自己此刻是 14 files / 328 tests)。
  • 未跑:本轮的 verify-codegraph-host-contract / verify-client-ui / verify-rss-opml——第六档的 host-contract 仍以第五档那次 42/42 为准,未在 alpha.5 上重跑。
  • `0.2.1-rc.2`(第八档基线,2026-10-19 实测):纯显式声明,本包运行代码未动(lib/ 产物零 diff)。0.2.1-rc.2 本来就被 `^0.2.1-rc.1` 覆盖(同一 0.2.1 版本元组,预发布之间不比大小),官方判定器实测放行。逐文件比对(rc.1 装临时树 + rc.2,304 个宿主包逐个 lib/):有差异的只有 9 个文件,且逐个查清后没有一处影响本仓——纯版本串 4 处(dsh-client-modules 的 commit hash、settings-account / settings-general / sidebar 的 0.2.1-rc.1 → rc.2);dsh-web 把 searchProvider / fetchProvider 改成 .volatile()(设置页可热改,本仓零引用这两个字段);dsh-web-search-deepseek 请求多带 thinking: {type:'disabled'} 且报错文案改指向新设置页;client-ui-settings-web-search 新增「Use the new Web Search API」开关(本仓不碰 dsh-client-store);dsh-cordis-client-runner 的 slot 占用表新增官方 deepseek-model-label 一条(本仓注册的槽未被动)。cordis / schemastery 仍是 4.0.5 / 3.18.5(与第六、七档同一对);包集合只多两个宿主自带包(dsh-client-ui-deepseek-model-label-1017 / dsh-web-deepseek-api),不改变本仓 import 的任何包。证据:① `node scripts/check-dsh-peers.mjs --app-boot