dsh-loopx-plugin
loopx-project/loopxLoopX——面向长周期 Agent 的提供商中立、本地优先状态内核与控制平面:在 DeepSeek Harness 执行层之上持久化 Goal、Todo、门禁、证据、配额、恢复与交接状态;插件负责引导安装 CLI 与技能、准入有界的同会话续跑,并为精确绑定的工作循环提供本地 GoalBar。
dsh-graph 是面向 DeepSeek Harness (DSH) 的目标看板插件。它将大模型智能体(Agent)的工作流组织为基于图的目标管理(Graph-based Goal Management)。
本插件采用一体化单包分发(npm 包名 dsh-graph),同时集成两大核心能力:
graph_* 工具,并暴露 /api/dsh-graph* REST API(支持看板投影、目标详情查询与写操作);conversation.view 槽位)的浏览器二维泳道看板,提供直观的可视化交互与实时追踪。数据以本地纯文本与事件流形式存储于工作区的 .dsh-graph/ 目录,Git 友好、天然支持协同对账与审计追踪。
在 DSH 环境中运行以下命令即可安装:
dsh plugin --profile <profile-name> add dsh-graph
当前版本:v0.22.0。环境要求:Node.js ≥ 22(包内预编译 core 运行时)。宿主提供的核心包(@deepseek-ai/cordis、@deepseek-ai/schemastery、@deepseek-ai/dsh-settings)以 peerDependencies + peerDependenciesMeta.optional(DSH 生态惯例)声明,由 DSH 宿主环境提供,安装不产生 peer 告警;yaml 为插件自带运行依赖(声明在 dependencies 中),避免产生重复的核心包实例。
宿主兼容范围:engines.dsh 与 peerDependencies["@deepseek-ai/dsh-settings"] 同步声明 >=0.1.5-rc.2 <0.2.2-0(上界 -0 排除 0.2.2 的一切预发布与正式版;整条 0.2.1 线已在范围内),engines.dsh 供 dsh-market 等宿主感知型市场在卡片展示与安装/更新预检中读取。⚠️ 宿主的安装/启动门禁只读 peerDependencies、不读 engines.dsh,故两处必须同步放宽(只改 engines.dsh 无效)。实测通过的宿主:0.1.6-alpha.2 ~ 0.1.7-rc.2、0.2.0-rc.1、0.2.0-rc.2(负责人 2026-09-30 真机复验)、0.2.1-alpha.2(2026-10-09 隔离实例无豁免复验)。该区间顺带覆盖的 0.1.8 系在 npm 上从未发布(0.1.7-rc.2 之后直接跳版到 0.2.0-rc.1),故为空集,不构成未实测声明。
平台状态:
| 平台 | 本版状态 |
|---|---|
| Linux / WSL2 | ✅ 已实测通过(本版全量测试 fail 0;H 系列共享探针在本版全绿) |
| 原生 Windows | ⏳ 本版结论一律以台账为准:真机轮次结论(逐版本、逐宿主代际)登记在 platform-gate §7 回填表(该文档不随包发布)—— 本行不预称任何轮次结果,也不得读出「已通过」。台账中已完成的历史轮次示例:v0.20.1(2026-10-10,win32/x64 + Node v24.21.0,宿主 0.2.0-rc.2 与 0.2.1-alpha.2 两代际,T1–T5 = 通过 15 / 失败 0 / 告警 1、T3 = 32/32 步)—— 历史轮次只覆盖安装 / 启动 / REST 层,且只覆盖该版本的产品代码,不替代本版结论。 |
| macOS | ⏳ 本版未验证:v0.22.0 未在原生 macOS 上执行真机门禁 ⇒ 不得读出「已通过」;结论同样以 platform-gate §7 回填表(不随包发布)为准。历史记录见 platform-gate §7.4 / §7.7,只覆盖各自版本的产品代码。 |
三平台使用同一安装包。已知限制:① macOS 默认文件系统 APFS 大小写不敏感 —— 仅大小写不同的目标编号 / 版本泳道会落到同一实体,请勿只用大小写区分;② macOS 上若工作区路径经显式传入且含符号链接(如位于 /tmp、/var 之下),会被拒绝并报 graph root symlink is not allowed;由 process.cwd() 推导的路径不受影响。
最新亮点(v0.22.0)
icon.svg);并在使用说明中完整披露代码评审变更规模统计中非产品路径(如 docs/*)的白名单判定与计算规则,消除认知与策略偏差。完整变更史见 CHANGELOG。已发布版本支持通过 npm 与 dsh-market 生态分发。
现象(外部报告):升级宿主后 Web GUI 冷启动报
web boot: N entry did not activate / <插件名>: failed —— 插件的 host 半边(graph_* 工具)正常、热加载也正常,只有浏览器半边不激活。
成因:dsh.client.inject 不是「名录」,而是加载顺序边。浏览器端 loader
(@deepseek-ai/dsh-client-modules/lib/client.js)只对 inject 中已存在于客户端清单的包名做前置加载,
名字不在清单里就静默跳过。而 @deepseek-ai/dsh-client-runtime 自 dsh 0.2.x 起不再随宿主分发
(在 dsh 0.1.5-rc.2 / 0.1.7-rc.2 / 0.2.0-rc.2 / 0.2.1-alpha.2 的安装树里均无此包),但它的包清单仍声明 dsh.client:
升级过程中若这个旧条目/旧副本残留在 profile 里,它会重新变成一条客户端清单行;该行的加载失败会被级联成
client-modules: "<插件>" not loaded because dependency "…" failed,把任何仍声明它的插件一起拖死。
干净安装没有这一行,所以不复现。dsh-graph 已删除该声明(即使残留仍在,本插件也不再是它的消费者)。
首选动作:把 dsh-graph 升级到含本修复的版本即可,残留无需处理。 含本修复的版本已不再声明该死引用 ⇒ 无论 profile 里是否还残留旧副本,本插件都不再是它的消费者,也不会被它拖死。下面的自检只是「想确认现状」时的只读排查;清理残留是可选的进阶动作。
自检(全部只读;下述命令已在隔离实例 dsh 0.2.0-rc.2 / 0.1.7-rc.2 上实测):
把 <DSH_HOME> 换成你的 DSH home:web 版默认是 ~/.dsh(也可由 DSH_HOME 环境变量指定);桌面版是另一套路径,请以该壳的配置/日志里显示的 profile 目录为准。
# ① 本插件的声明 —— 期望 3 项,且不含 dsh-client-runtime
node -e 'console.log(JSON.stringify(require(process.argv[1]).dsh.client.inject))' \
"<DSH_HOME>/profiles/web/node_modules/dsh-graph/package.json"
# ② profile 清单里是否还列着它 —— 期望输出 0
# (注意:grep -c 在计数为 0 时退出码是 1,看打印出的数字即可,不要看退出码)
grep -c dsh-client-runtime "<DSH_HOME>/profiles/web/package.json"
# ③ 整个 DSH home 内是否还有名为 dsh-client-runtime 的目录 —— 期望无输出
find "<DSH_HOME>" -type d -name dsh-client-runtime 2>/dev/null
# ④(可选,仅在 Web 版且实例正在运行时)客户端清单里是否还有该行 —— 期望无输出
# 「dsh web」启动行会打印带 token 的 URL,把它原样填进 <URL>
curl -sL -b "" "<URL>" | grep -o '"@deepseek-ai/dsh-client-runtime"' | head -1
隔离实例实测结果(dsh 0.2.0-rc.2):
["@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-primitives","@deepseek-ai/dsh-client-ui-sidebar-right"]0curl 返回 200、35125 字节(同一命令对清单里确实存在的行名如 @deepseek-ai/dsh-client-ui-settings 能打印出来)1;在 profile 里放入名为 dsh-client-runtime 的目录后 ③ 确实打印出该路径(随即移除)清理(可选,先备份):仅当你确实想清干净、且 ②非 0 或 ③有输出时:先备份 profile,再删除 ③ 打印出的残留目录(并在 ② 命中的清单里去掉对应条目),然后重启宿主 —— 客户端包元数据在激活期缓存,增删客户端插件必须重启才生效。风险提示:在 live profile 上直接删目录/改清单有改坏环境的风险;更稳妥的替代是重装同版本 dsh-graph 或新建一个干净 profile。自行清理前务必留备份,异常时用备份复原。
未验证:无桌面壳环境可用(@deepseek-ai/dsh-desktop 在 npm 为 E404)。上述现象与成因链引用外部报告与宿主源码,
不声称已复现桌面壳症状。
draft → planning → collecting → ready → in_progress → review → delivered(任何阶段均可标记进入 blocked 阻塞状态)。empty → collecting → filled → reviewed 闭环生命周期,为执行子代理提供精确的上下文种子。HANDOFF.md,换会话后新 Supervisor 可幂等认领上下文并快速接管。prefers-reduced-motion 无障碍降级);弹窗拖拽防误关。想先弄懂「dsh-graph 为什么这样组织开发」——目标生命周期与状态机、判据门禁与人工 gate、派发与隔离三件套、 attempt 与结果面、独立复核与留痕真源、版本泳道与发布红线、记忆分级、事件先行——请读 设计哲学(中文): 它逐条给出代码真源引用,并附 archify 交互式图(内联静态预览 + 可交互 HTML);图只画今天已实现的 状态流转与角色分工,图内不含文件、函数或内部产物名,1.0 路线见文档 §13。
dsh-graph 为 Agent 提供了完善的工具链(共 51 个 graph_* 工具),按功能划分为以下分类:
| 分类 | 工具名称 | 核心说明 |
|---|---|---|
| 目标生命周期 | graph_create_goal | 创建目标(默认放入 Backlog,可指定版本) |
graph_rename_goal | 重命名目标标题 | |
graph_set_description | 设置/更新目标描述正文(Markdown) | |
graph_set_directive | 为下一次 Attempt 注入补充指令与边界要求 | |
graph_set_goal_type | 设置目标类型(feature / bug / task / improvement / patch / chore) | |
graph_set_goal_tags | 设置目标标签列表(最多 20 个,乐观并发) | |
graph_amend_goal | 记录对目标的修订补充,可自动同步至描述 | |
graph_transition | 推进目标状态机迁移(进入 blocked 需附原因) | |
graph_postpone_goal | 暂缓目标,移回 Backlog 并置为 draft | |
graph_archive_goal | 归档已完成或已废弃的目标 | |
graph_unarchive_goal | 从归档中恢复目标 | |
graph_delete_goal | 安全删除已归档的目标 | |
graph_clean_worktree | 清理已验证的 worktree(用户确认后执行) | |
graph_list_worktrees | 查询 Git worktree 清理候选(只读,不自动删除) | |
| 目标关系 | graph_set_relation | 标记/解除目标间关系(取代/调整/补充/相关,可增可删;幂等,拒绝替代环) |
| 质量判据 | graph_set_criteria | 登记目标验收判据(严格在执行前设定) |
| 上下文卡片 | graph_add_card | 创建上下文卡片占位(text / file / image / data) |
graph_bind_collect_card | 绑定收集子代理,卡片状态转为 collecting | |
graph_fill_card | 填充卡片内容并生成看板简要摘要 | |
graph_review_card | 复核卡片内容(filled → reviewed) | |
graph_delete_card | 删除未在收集中的卡片 | |
graph_convert_card_to_shared | 将自有卡转换为共享卡(放入共享池) | |
graph_convert_card_to_owned | 将共享卡收回为自有卡(独占) | |
graph_attach_shared_card | 把共享池既有共享卡挂载到目标(复用已收集上下文,仅 owner/主管) | |
graph_detach_shared_card | 解除目标对共享卡的引用(卡仍留池;collecting 拒绝) | |
graph_list_shared_cards | 只读列出共享池共享卡(id/title/status/refs) | |
| 附件管理 | graph_store_attachment | 存储文件附件到目标 |
graph_delete_attachment | 删除目标附件 | |
| 排期管理 | graph_move_goal | 在 Backlog、独立目标与版本之间移动排期 |
| 执行与返工 | graph_start_attempt | 派发执行 Attempt,启动并绑定可续轮子代理 |
graph_record_attempt_handoff | 记录前序 Attempt 的返工约束与排查基线 | |
graph_unbind_goal_child | 安全解绑目标执行子代理 | |
graph_abandon_attempt | 放弃陈旧或失联的 Attempt | |
graph_start_review | 为既有执行 Attempt 派发独立评审子代理(只读;不新建 attempt、不覆盖作者结果;结论独立落盘) | |
| 配置管理 | graph_get_settings | 查询当前 workspace 项目配置及合法枚举元信息 |
graph_update_settings | 结构化更新当前 workspace 项目配置(支持 patch) | |
| 记忆管理 | graph_memory_add | 写入按需/常驻记忆条目 |
graph_memory_recall | 按关键词检索记忆 | |
graph_memory_remove | 删除指定记忆条目 | |
graph_memory_replace | 替换已有记忆条目内容 | |
| 状态汇报 | graph_report_status | 汇报当前 Attempt 进度(看板卡片实时显示) |
graph_report_supervisor_status | Supervisor 汇报全局工作状态(顶部状态栏动画) | |
| 评审裁决 | graph_resolve_accept | 裁决交付验收(verdict: accept / object) |
| 协作与交接 | graph_add_comment | 向目标追加可追溯的讨论与反馈历史 |
graph_write_results | 人工写入 attempt 完成摘要(source=manual + 写入者标注;无子代理的轻量改动兜底) | |
graph_refresh_results | 重写 results.md:零 LLM 兜底拼装,或采用专用摘要子代理/人工产出的 content(旧版自动归档;支持批量 goals[]) | |
graph_handoff | 生成跨会话交接文档 HANDOFF.md | |
graph_claim_supervisor | 新会话接管 Supervisor 并更新会话元数据 | |
graph_help | 输出插件功能说明与 51 个工具速查清单 | |
| 数据与校验 | graph_validate | 执行全量不变式检查(状态、依赖环、卡片引用) |
graph_rebuild | 从事件流完全重建目标状态并与元数据对账 |
内嵌于 DSH Web 界面:
graph_get_settings / graph_update_settings 配置项目实际的 review.regions / review.contract_paths / review.non_product_prefixes,未登记区域安全升级 strict,空契约默认 M1 不触发;右侧栏的「看板」与会话页的「看板」页签是同一份实现——同一个看板组件、同一套头部与窄档逻辑,两侧完全一致(零 host 门控),任选其一即可。
⋯ 工具:刷新 / 标签筛选 / 记忆 / 项目知识库(共享条目)/ 看板设置 / 已归档。工具条按头部实测宽度装不下自动折叠为这一项(不是写死的窗口断点)。[🏷️] 版本管理:角落的方形图标按钮(可访问名称为「🏷️ 版本管理」),点开版本管理抽屉;紧邻其右是同一行等高的 创建版本。[+](新建目标)左侧,切换当前显示的泳道(具体版本 / Backlog / 独立目标)。clientWidth,不是窗口宽度、也不是浏览器视口宽度):
≥ 480px(宽档):多泳道横向并排,各版本 / Backlog / 独立目标可同时查看;< 480px(单泳道档):阶段列由横向并排改为纵向堆叠,泳道内容由版本选择器决定(具体版本 / Backlog / 独立目标三选一;工作区一个版本都没有时,默认落点就是「独立目标」,选择器当前项显示「独立目标」);该档没有版本折叠开关(收起来等于空板),并同时把工具条强制折叠为「⋯ 工具」、隐藏 DEBUG 行。< 480px:宿主页签的宽度由页签布局模式决定,不是拖出来的——实测(1600px 视口)单页签 719px、页签上的 分栏 之后每页签 359px(该档随窗口宽度变化)、全屏 799px。所以默认单页签宽度(719px)落在宽档,此时不会出现单泳道;需要单泳道档时用页签上的 分栏,或把窗口收窄到看板面板实测宽度 <480px。进入后一眼可验:六个阶段块纵向堆叠,且泳道标题右侧出现版本选择器(当前项为具体版本 / Backlog / 独立目标)。效果截图见仓库 screenshot/sidebar-kanban.png(虚构演示数据 nebula-notes,右侧栏宽度落在 < 480px 单泳道档);本 npm 包不包含仓库的 screenshot/ 目录,故此处只给出仓库路径。
子代理默认 provider / model、推理档位、执行模式、提示词语言与补充提示词是 profile 级全局默认 —— 写入当前 DSH profile、跨 workspace 生效,且 workspace 的 project.yaml 明确配置与单次派发参数都更优先。
同一个设置页在两个入口都能打开,两处是同一份实现、读写同一份 profile 配置:
dsh-graph 绑定)。宿主未提供某个位面时(精简 profile / 旧宿主),对应席位自动不出现,看板与工具不受影响;两处都取不到设置服务时,页面如实提示当前 profile 未暴露设置服务,而不是假装已启用。配置文件落在 profile 的设置文档里(0.2.0 系宿主为 profile 的 cordis.patch.yml 中 dsh-graph-host 条目,0.1.6 系宿主为 $DSH_HOME/settings.yaml 的 dsh-graph 命名空间)。
插件数据保存在当前工作区下的 .dsh-graph/ 目录:
events.jsonl 组成;events.jsonl 记录每一次状态流转与操作,是唯一事实来源,可通过 graph_rebuild 随时对账;.dsh-graph/ 根目录。dsh-graph is a goal-oriented kanban plugin for DeepSeek Harness (DSH), bringing Graph-based Goal Management into Agent workflows.
Distributed as a single unified package (npm package name: dsh-graph), it provides both halves out-of-the-box:
graph_* tools to DSH Agents covering the entire goal lifecycle, along with /api/dsh-graph* REST endpoints for board projections, goal details, and mutations;conversation.view slot) for intuitive visualization and real-time tracking.All data is stored locally as human-readable files and an append-only event log under .dsh-graph/, making it Git-friendly, easily auditable, and collaborative.
Install the plugin using the DSH CLI:
dsh plugin --profile <profile-name> add dsh-graph
Current version: v0.22.0. Requirements: Node.js ≥ 22 (includes the precompiled core runtime). Core packages provided by the DSH host (@deepseek-ai/cordis, @deepseek-ai/schemastery, @deepseek-ai/dsh-settings) are declared under peerDependencies with peerDependenciesMeta.optional (standard DSH ecosystem convention) and provided by the host runtime without peer warnings; yaml is retained in dependencies as a plugin-specific runtime dependency, preventing duplicate core package instances.
Host compatibility range: engines.dsh and peerDependencies["@deepseek-ai/dsh-settings"] declare >=0.1.5-rc.2 <0.2.2-0 in lockstep (the -0 upper bound excludes every 0.2.2 prerelease and final release; the entire 0.2.1 line is now inside the range); engines.dsh is what host-aware markets such as dsh-market read for card display and install/update pre-flight. ⚠️ The host's install/boot gate reads peerDependencies only, never engines.dsh, so both fields must be widened together (widening engines.dsh alone has no effect). Hosts verified: 0.1.6-alpha.2 through 0.1.7-rc.2, 0.2.0-rc.1, 0.2.0-rc.2 (verified by the maintainer on 2026-09-30), and 0.2.1-alpha.2 (verified on 2026-10-09 in an isolated instance with no version exemption). The 0.1.8 line incidentally covered by that range was never published on npm (versions jump straight from 0.1.7-rc.2 to 0.2.0-rc.1), so it is an empty set and adds no unverified claim.
Platform status:
| Platform | Status for this release |
|---|---|
| Linux / WSL2 | ✅ Verified on-device (this release's full test suite: 0 failures; the shared H-series probes are all green on this release) |
| Native Windows | ⏳ The verdict for this release is the ledger, and only the ledger: on-device round results (per version, per host DSH generation) are recorded in the platform-gate §7 backfill table (that document is not shipped inside the package) — this row pre-claims no round result and must not be read as "passed". Example of a completed historical round in that ledger: v0.20.1 (2026-10-10, win32/x64 + Node v24.21.0, host DSH generations 0.2.0-rc.2 and 0.2.1-alpha.2; T1–T5 15 passed / 0 failed / 1 warning, T3 32/32 steps) — historical rounds cover the install / boot / REST layers only and only that version's product code, so they do not substitute for this release's verdict. |
| macOS | ⏳ Not verified for this release: v0.22.0 has not run the on-device gate on native macOS ⇒ must not be read as "passed"; the verdict likewise comes from the platform-gate §7 backfill table (not shipped in the package). Earlier records are in platform-gate §7.4 / §7.7 and cover each of those versions' product code only. |
All three platforms share the same package. Known limitations: (1) APFS, the macOS default, is case-insensitive — entries that differ only by case resolve to the same entity, so do not rely on case alone to distinguish goal ids or version lanes; (2) on macOS a workspace path that is explicitly supplied and contains a symlink (e.g. under /tmp or /var) is rejected with graph root symlink is not allowed; paths derived from process.cwd() are unaffected.
What's new (v0.22.0)
shared_cards module is deactivated in the workspace, eliminating configuration deadlocks.icon.svg) matching DSH plugin specifications; thoroughly documents non-product path rules (such as docs/* counting as 0 product lines) in the usage guide to align user expectations with code review policies.See the CHANGELOG for the full history. Official releases are distributed via npm and the dsh-market ecosystem.
Symptom (externally reported):
after upgrading the host, a Web GUI cold start reports web boot: N entry did not activate / <plugin>: failed — the plugin's
host half (graph_* tools) works and hot reload works, but its browser half never activates.
Cause: dsh.client.inject is not a directory listing, it is a load-order edge. The browser-side loader
(@deepseek-ai/dsh-client-modules/lib/client.js) preloads only those inject names that already exist in the client
manifest, and silently skips the rest. @deepseek-ai/dsh-client-runtime is no longer shipped with the host since dsh 0.2.x
(absent from the install trees of dsh 0.1.5-rc.2 / 0.1.7-rc.2 / 0.2.0-rc.2 / 0.2.1-alpha.2), yet its package manifest still declares dsh.client.
If an old entry/copy of it survives an upgrade in your profile, it becomes a client manifest row again; that row's load failure
cascades into client-modules: "<plugin>" not loaded because dependency "…" failed, dragging down every plugin that still declares it.
A clean install has no such row, which is why it does not reproduce. dsh-graph has dropped that declaration — even if the
residue is still present, this plugin is no longer one of its consumers.
Preferred action: just upgrade dsh-graph to a build that contains this fix — the residue needs no handling. A fixed build no longer declares the dead name, so whether or not an old copy survives in your profile, this plugin is no longer one of its consumers and can no longer be dragged down by it. The self-check below is only a read-only way to inspect the current state; cleaning up the residue is an optional advanced step.
Self-check (read-only; every command below was measured on isolated instances of dsh 0.2.0-rc.2 / 0.1.7-rc.2):
Replace <DSH_HOME> with your DSH home: for the Web build it defaults to ~/.dsh (or wherever DSH_HOME points); the desktop build
uses a different path — take the profile directory shown in that shell's configuration or logs.
# (1) This plugin's declaration — expect three names, without dsh-client-runtime
node -e 'console.log(JSON.stringify(require(process.argv[1]).dsh.client.inject))' \
"<DSH_HOME>/profiles/web/node_modules/dsh-graph/package.json"
# (2) Does the profile manifest still list it? — expect the number 0
# (note: `grep -c` exits 1 when the count is 0 — read the printed number, not the exit code)
grep -c dsh-client-runtime "<DSH_HOME>/profiles/web/package.json"
# (3) Is there still a directory named dsh-client-runtime anywhere under the DSH home? — expect no output
find "<DSH_HOME>" -type d -name dsh-client-runtime 2>/dev/null
# (4) Optional, Web build with a running instance only: is it still a row in the client manifest? — expect no output
# `dsh web` prints a tokenized URL on startup; paste it verbatim as <URL>
curl -sL -b "" "<URL>" | grep -o '"@deepseek-ai/dsh-client-runtime"' | head -1
Measured on an isolated instance (dsh 0.2.0-rc.2):
["@deepseek-ai/dsh-client-ui-settings","@deepseek-ai/dsh-client-ui-primitives","@deepseek-ai/dsh-client-ui-sidebar-right"]0curl returned 200 with 35125 bytes (the same command does print a row name that is present in the manifest,
e.g. @deepseek-ai/dsh-client-ui-settings)1 on a manifest file that really contains the name, and
(3) did print the path after a directory named dsh-client-runtime was deliberately placed into the profile (removed again immediately).Cleanup (optional, back up first): only if you really want a clean slate and (2) is non-zero or (3) has output: back up the profile first, then remove the residu