deepseek-coordination
Verifieddeepseek-coordination · v0.14.10 · Apache-2.0 · Web UI
A dual-face Cordis plugin for DeepSeek Harness: the host half owns a coordination SQLite database, seven model-facing tools and an HTTP surface; the web half mounts five composer dropdowns, a settings catalog and the service-level takeover dialog.
Install
dsh plugin add deepseek-coordination Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
deepseek-coordination
English · 简体中文 · 更新日志 · English changelog
一个面向 DeepSeek Harness 的双端 Cordis 插件:宿主端持有协调 SQLite 数据库、7 个模型可见工具和一组 HTTP 路由;浏览器端在输入框挂载五个下拉框,并提供设置目录与服务级接管对话框。
当前版本:v0.14.10(2026-10-18)。 本版新增右侧两个只读工作区面板(canvas 关系图、预览矩阵:Markdown 与数学、净化 HTML、Prism 代码、docx/xlsx/pptx、带预计进度的代理名册),并做了崩溃隔离——任一面板失败都不会拖垮宿主。它建立在 v0.12.0 的领域层与 v0.11.0 的设置/缓存代理之上。包版本、更新日志和发布 tarball 来自同一棵树;下文按源码核对,不以设计文档代替实现。
| 状态 | 含义 |
|---|---|
| 已接线 | 由 src/index.ts 注册,或被已组合的 service、路由、工具实际调用。 |
| 仅库 | 模块已实现并有测试,但当前 Turn 生命周期不驱动它,也没有路由/工具暴露。 |
| 设计/计划 | 只写在文档、配置或 schema 中,尚无运行时消费者。 |
这是什么
| 组成 | 位置 | 作用 |
|---|---|---|
| Bundle | cordis.patch.yml、package.json 的 dsh.bundle |
把一行 deepseek-coordination 插入 DSH profile 的 bundle 列表。 |
| 宿主端 | lib/index.mjs(ESM,Node) |
CoordinationService、coordination SQLite 库、7 个工具、14 条路由路径和 agent 钩子。 |
| 浏览器端 | lib/client.js(CJS,浏览器,__ModuleLoader__ 包装) |
React 18 + Material UI 9:输入框下拉框、设置页、接管对话框。 |
| 数据库 | 默认 %DSH_HOME%\coordination\coordination.db |
插件自有 SQLite 文件;63 张表、7 个触发器、63 个索引,schema 版本 24。从不打开 DSH 的 harness 库。 |
插件不替代 DSH 的 agent 循环。它记录协调状态、为每个任务锁定一个职能、提供输入框选择与文件元数据、在上下文压力越过阈值时请求 DSH 压缩,并把审批/提问转给人工对话框。
五个输入框控件是 权限、职能、交互、情景、验证。命令面(/goal、/plan、/feedback、/compact、/permission)是消息前缀,不占第六个下拉位。
安装与构建
| 要求 | 说明 |
|---|---|
| Node.js | package.json 声明 >=18,但宿主端使用 node:sqlite,必须使用带该模块的 Node 构建。本次验证使用 Node 24.21.0。 |
| npm | npm 11(随 Node 24 提供)按 package-lock.json 安装依赖,并驱动 scripts 与发布工作流。 |
| DSH | 装有 dsh CLI 的 DSH,且 profile 含 @deepseek-ai/dsh-base 与 @deepseek-ai/dsh-web-app。 |
| Peer 包 | 见下表;它们由 DSH 安装提供,不由本包携带。 |
cd deepseek-coordination
npm ci # 全新 checkout 也可用 `npm install`
npm run typecheck # tsc -p tsconfig.json --noEmit
npm test # tsdown 测试构建 + node --test
npm run build # 生成 lib/index.mjs、lib/index.d.mts、lib/client.js
npm pack # 先执行 prepack,生成可安装 tarball
npm run test:tsc 检查测试树;npm run pack:check 校验 tarball 中的运行时文件并检查宿主入口语法。仓库提交了 package-lock.json,npm ci 会精确复现它。
| Peer 包 | 范围 | 端 |
|---|---|---|
@deepseek-ai/cordis |
^4.0.1 |
宿主运行时 |
@deepseek-ai/dsh-host-webserver |
^0.1.0-rc.7 || >=0.2.1-rc.2 |
宿主路由 |
@deepseek-ai/dsh-client-runtime、-locale、-ui-slots、-ui-settings |
^0.1.0-rc.7 || >=0.2.1-rc.2 |
浏览器模块表 |
react |
^18.2.0 |
浏览器模块表;不会被打包 |
客户端半针对桌面运行时携带的 0.2.1-rc.2 客户端包(-locale、-ui-settings、-ui-slots、-ui-conversation)编译。@deepseek-ai/dsh-client-runtime 与 @deepseek-ai/dsh-host-webserver 没有发布 0.2.x,因此它们的 devDependency 仍是 0.1.0-rc.8,而 peer 范围接受宿主提供的 0.2.1+ 运行时。>=0.2.1-rc.2 接受之后的所有稳定版本,^0.1.0-rc.7 让 0.1 线仍可安装。
安装到 profile
安装打包后的 tarball,不要装 GitHub 源码:
npm pack
dsh plugin --profile <name> add .\deepseek-coordination-0.14.10.tgz
dsh --profile <name> --dump-config # 应出现 deepseek-coordination 层
不支持从 github:eloyhere/deepseek-coordination 直接安装。 lib/ 被 gitignore,而 bundle 只在 prepack(或显式 npm run build)时构建,npm 安装 git 依赖时不会执行这一步,安装后的包在激活时会因找不到 lib/index.mjs 报 ERR_MODULE_NOT_FOUND。打 tag 时触发的 GitHub Actions(.github/workflows/release.yml)会构建并把校验过的 tarball 附到 Release,请安装该产物。
npm 上的 latest 仍是旧的 0.1.9 dsh-os 线。 目前不要按包名安装。本地 checkout 在 npm run build 之后可以用 dsh plugin --profile <name> add . 链接。
dsh plugin add 会写入依赖和 bundle 行。如果 profile 里仍记录着别的包 spec,请替换它,不要假定新代码已经加载;用 dsh --profile <name> --dump-config 和已安装的 package.json 版本核对。
卸载或禁用
如果某个版本让 harness 无法进入,按下面顺序移除,然后重启:
- 从 profile 的 bundle 列表移除
deepseek-coordination(dsh plugin --profile <name> remove deepseek-coordination,或手动删除该行)。 - 在 profile 目录执行
pnpm remove deepseek-coordination。 - 如果
node_modules/deepseek-coordination仍存在,手动删除——部分卸载路径会留下目录。 - 执行
pnpm install重写 lockfile 与依赖树。 - 重启 harness。客户端 bundle 在启动时下发,仅刷新页面不会卸载旧插件。
只针对面板 UI 的免重装恢复:在 Web GUI 里设置 localStorage['coordination.panel.disabled'] = '1'(或 'true')并刷新,面板列不挂载任何内容,插件其余功能仍可用。改回 '0'(或删除该键)再刷新即可恢复。该开关只在挂载时读取一次,两个方向都需要刷新。
| 产物 | 格式 | 本次构建大小 |
|---|---|---|
lib/index.mjs |
宿主 ESM,platform: node |
2,380,317 B |
lib/index.d.mts |
宿主类型声明 | 9,095 B |
lib/client.js |
浏览器 CJS,包在 __ModuleLoader__.load() 中 |
3,332,193 B |
Material UI、emotion 与 react-dom 被打进 lib/client.js,React 本身来自宿主模块表。内联的 react-dom 是 18.3.1 线,与宿主的 React 18 匹配——npm 不会为 React 18 的 react 自动配一个 React 19 的 react-dom。客户端体积大是因为内联了 MUI 与 react-dom,不是因为多带了一份 React。
架构
宿主端(src/index.ts):
- 导出
name、inject = ['webServer', 'tools']和Config;apply()解析配置、创建 service、恢复被中断的 Turn,并通过ctx.effect安装所有 effect。 CoordinationService(src/host/coordination.ts)是 Cordis 的coordinationservice,持有数据库、module host 和已组合模块:session、selection、turn、profession、plan、goal、compact、directive、permission、takeover/gate、provider、cache、command。- SQLite 使用
node:sqlite的DatabaseSync,开启外键、5 秒 busy timeout,非:memory:时使用 WAL。触发器拒绝修改已锁定职能,orchestration_trace只允许追加。 /deepseek-coordination前缀路由返回 JSON{ ok, value | error };请求体上限 1 MiB;未知路径返回 404 路由信封。- 7 个工具通过
ctx.tools注册;三个输入框工具在POST /locale后重新注册,因为工具描述属于模型提示词。 - 钩子:
agent/pre-step打开协调 Turn 并运行压缩治理器;agent/turn-stopping以done关闭;agent/error以失败关闭;approval/request与user-questions/request进入接管闸门。60 秒定时器清理过期 lock 行。
浏览器端(src/client/index.ts):
inject = ['slots', 'locale'];package.json的dsh.client.inject另行列出四个模块表包。settings.section(order 50)挂载CoordinationSettings;conversation.input.left(order 40)挂载输入框控件,接管对话框作为兄弟节点。- 语言跟随
ctx.locale.getLocale().active,把zh*/en*归一为zh-CN/en-US,并上报POST /locale;宿主据此重建本地化工具描述。 - 客户端 bridge 使用同源
fetch:选择接口 3 秒超时,接管/语言接口 2 秒超时。宿主不可达时,五个选择回退到每会话的localStorage;权限预设永不离线写入。 - 输入框组件只按
sessionIdmemo;slot 每次击键也会传input,若按默认浅比较重渲染,emotion 会触碰<head>,导致宿主的弹层关闭。
deepseek-coordination/
├─ src/
│ ├─ index.ts # 宿主组合根:service、路由、工具、钩子
│ ├─ host/
│ │ ├─ coordination.ts # CoordinationService(Cordis service "coordination")
│ │ ├─ config.ts # Schemastery schema + resolveConfig()
│ │ ├─ database.ts # SQLite schema(24),63 表,触发器
│ │ ├─ dsh.ts # 窄 DSH port + Turn 生命周期
│ │ ├─ tools.ts # 7 个模型可见工具
│ │ ├─ chain.ts # 有界 Chain 投影引擎
│ │ ├─ node-registry.ts # NodeCard 注册表与 describe()
│ │ ├─ parse-service.ts # 工作区内的解析与读取服务
│ │ ├─ token.ts # TokenGovernor 与纯策略
│ │ ├─ takeover-gate.ts # 审批/提问瀑布闸门
│ │ ├─ selection.ts turn.ts profession.ts … # 选择、锁定、生命周期
│ │ ├─ bayesian.ts comprehension.ts organization.ts … # 库级引擎
│ │ └─ text/ # P0 解析器:JS/TS、CSS/HTML/MD、TeX、公式、依赖图
│ ├─ client/
│ │ ├─ index.ts # slots、语言 store、导航图标、接管 CSS
│ │ ├─ controls.ts # 五个输入框下拉框、tooltip、离线重放
│ │ ├─ settings.ts # 13 组、151 控件的设置目录页
│ │ ├─ takeover.ts # 审批/提问对话框
│ │ ├─ bridge.ts store.ts i18n.ts theme.ts selection-cache.ts
│ └─ shared/ # entity/enum/selection/i18n/error + 数据结构
├─ test/ # 24 个 *.test.ts + helpers.ts
├─ lib/ # 构建产物;gitignore,不手改
├─ cordis.patch.yml # 插入 profile 的 bundle 行
├─ scripts/check-pack.mjs # tarball 运行时文件校验
└─ .github/workflows/release.yml
输入框控件
| 控件 | 选项 | 行为 |
|---|---|---|
| 权限 | DSH 预设目录,或中性的“跟随 DSH” | 通过 ctx.permissionPresets 立即写入 DSH,不写入协调库。仅在本机“权限接管”开关打开时显示。 |
| 职能 | 决策 / 成果 / 资源 / 人员 / 任职 / 执行 | 活动任务内锁定;选择其他值会返回 PROFESSION_LOCKED,并打开重建/丢弃对话框。 |
| 交互 | 感情 / 思维 / 启发 / 收束 / 统筹 | 写入 selection 行,供下一 Turn 使用;活动 Turn 有未清除的目标时冻结。 |
| 情景 | 对话 / 写作 / 办公 / 研究 / 工程 / 迭代 | 写入下一 Turn;writing 位于“对话”和“办公”之间;情景策略由 Turn 流水线读取。 |
| 验证 | low / medium / high | 写入下一 Turn;活动 Turn 有 draft/scored/active 计划时冻结;三档都会检索。 |
- 每个控件和每个选项都有 tooltip。
tooltipVisibility单所有权 store 保证同一时刻只有一个输入框 tooltip 可见;位置在左/右,退出为 160 ms 淡出,淡出中的 popper 不接收指针事件。 - 下拉框第一次点击就打开菜单;pointerdown/mousedown 不改变状态,因此不会吞掉 Select 的点击。
- 锁定的职能触发器被禁用,其无障碍名称带锁定解释;选项
MenuItem带标签和提示,键盘与读屏用户无需悬停即可获得说明。 - 阶段冻结每轮从主机重新推导:Turn → 职能,Plan → 验证,Goal → 交互。下拉框禁用但仍可聚焦,aria-label/tooltip 说明恢复时机;服务端对冻结字段的改值返回
INVALID_STATE(重复写原值允许)。 - tooltip 受宿主
ui.showTooltip总开关约束;本机“显示提示”只能收窄,不能恢复宿主已关闭的 tooltip。 - 保存是乐观的,并做 120 ms 防抖。保存被拒绝时回退到最近一次宿主确认的状态;离线编辑在宿主恢复后重放。完全权限预设需要第二次显式确认。
- 宿主状态加载中时下拉框保持禁用并显示 loading;权限目录缺失就显示缺失,不编造预设。
验证预算由 Turn 流水线实际读取:
| 档位 | 检索预算(次数 × 条数) | 证据范围 | 交互范围 |
|---|---|---|---|
| low | 1 × 3 | 仅当前 Turn | 当前选择的交互 |
| medium | 2 × 5 | 当前会话 | 当前选择的交互 |
| high | 4 × 8 | 本地全部内容 | 全部交互 |
预算定义在 src/host/comprehension.ts 和共享的 ValidationBudgetByLevel 表。command=none 的 Turn 现在由 CoordinationService 驱动 Comprehension → Organization → Plan/score;检索仍受 allowWeb 和缺失 web 端口约束。
情景与写作档
| 情景 | 计划 | 大纲 | 修订 | 评分 | Bash | 联网 | 最大轮次 |
|---|---|---|---|---|---|---|---|
| 对话 | – | – | – | – | 否 | 是 | 1 |
| 写作 | 是 | 是 | 是 | 是 | 否 | 是 | 3 |
| 办公 | 是 | – | – | – | 否 | 是 | 1 |
| 研究 | 是 | – | – | – | 否 | 是 | 3 |
| 工程 | 是 | – | – | – | 是 | 是 | 3 |
| 迭代 | – | – | – | 是 | 否 | 是 | 3 |
“写作”要求大纲和至少一轮修订,必须有质量评分,不允许 bash,允许联网,最多三轮。通过阈值为 0.6,plan.ts 使用该写作阈值。Turn 流水线读取 require-* 标志、引用/证据、allowWeb 与 sandboxOnly;推理(thinking、reasoningEffort)归 DSH 所有——页面没有对应行,Turn 记录的是会话自身请求头里的值,而不是情景字面量。allowBash、maxIteration 和非写作阈值仍是没有 DSH 端口/消费者的延迟诊断项。
职能锁定与 Turn 生命周期
- 一个任务只有一个职能。
turn.create写入profession NOT NULL、locked = 1,并写一条职能记录和一条lock追踪事件。 - 只要存在未终结 Turn,
SelectionModule.set就以ProfessionLockedError拒绝改职能;HTTP 层返回 409PROFESSION_LOCKED,选择不会被修改。 POST /profession/rebuild把活动任务以abandoned结束,创建带新序号、新职能的替代 Turn,复制 prompt/command,并写rebuild+lock追踪事件。它要求存在活动 Turn。POST /profession/abandon结束活动任务但不创建替代;下一个任务可重新选择。两个重选开关都由resolveConfig强制为 true。- SQLite 再兜一层:
trg_turn_profession_lock、trg_profession_record_lock、trg_subtask_profession_lock在锁定时拒绝改职能;trg_turn_profession_unlock、trg_subtask_profession_unlock拒绝清除锁定标记。
| 钩子 | 作用 |
|---|---|
agent/pre-step |
确保协调 session,打开(或复用)当前 DSH Turn 对应的协调 Turn;同时在 next() 前运行 Token 治理器。 |
agent/turn-stopping |
以 done 关闭 Turn,下一 Turn 解锁职能。 |
agent/error |
以 failed 关闭 Turn。 |
| 宿主启动 | recoverInterrupted() 把所有未终结 Turn 标为 failed,原因 turn was interrupted by a host restart。 |
| 每 60 秒 | 删除超过 defaultLease 的已结束 lock 行。 |
done、failed、cancelled、abandoned 是不可逆终态。Turn 钩子只记录 Turn 元数据和一条空 prompt 行,不把 DSH 对话镜像进协调库。
领域体系与解析器
文件层不是一个解析器加一堆节点类型。每个体系在 src/host/text/ 下拥有独立模块,它能产出的节点类型由该体系的语义限定。所有输出都是 body-free 的 NodeMetadata:kind、显式 name/label、span 与 fragment 坐标——绝不包含正文、单元格值或段落内容。
| 体系 | 模块 | 可产出的节点 |
|---|---|---|
| tex | tex.ts |
definition、lemma、theorem、proof、property、axiom、corollary、claim、example、remark、calculation、condition(含 requires/meets 边) |
| book | book.ts |
book、part、chapter、article、section、person、venue——article 的唯一产出者 |
| markdown | markup.ts |
document、chapter、heading、paragraph、div、link、code、section、sheet、cell、range、condition |
| html | markup.ts |
document、section(<article> 映射于此)、chapter(<h1>)、link、code |
| js/ts | javascript.ts |
class、function、method、variable、interface、type、enumeration、namespace、importation、exportation、condition |
| java | java.ts |
module、class、interface、enumeration、method、variable、importation |
| python | python.ts |
class、function、method、variable、importation |
| c/c++ | c-family.ts |
module、class、function、method、variable、importation |
| rust | rust.ts |
module、class、enumeration、function、method、variable、importation |
| php | php.ts |
module、class、interface、enumeration、function、method、variable、importation |
| maven | maven.ts |
project、module、dependency(parent 是 label: 'parent' 的 dependency) |
| npm | npm.ts |
project、dependency、script(命令体永不入库) |
| office | office.ts |
docx:document、chapter、section、paragraph、link;xlsx:workbook、sheet、range、formula;pptx:document、slide;pub/accdb:无节点,只出一条 unsupported-format 诊断 |
| text | sentence.ts |
sentence 切分与 line 区间(坐标,不是节点) |
跨体系恒定规则:
- 语言细分放
label,不新增 kind。 Rust 的impl是kind: 'class'+label: 'implementation';C++ 的struct/enum、PHP 的trait同理。NodeKind保持为小而封闭的联合。 - 只描述、不操作。 任何解析器都不写文件、不构建、不安装、不执行宏、不联网。"创建项目"若真需要,是 DSH 权限模型下的 shell 命令,绝不是插件工具。
- 只认显式证据。 不从正文推断标题层级、不编造版本号、缺失关系只报告不按位置配对;异常输入返回诊断且绝不抛异常。
- 有界。 ZIP 条目 ≤ 2000、单条解压 ≤ 64 MiB、总计 ≤ 256 MiB、XML 嵌套 ≤ 128、节点 ≤
MAX_METADATA_NODES(20 000),并遵守 token/时间预算。 SUMMARY.md由 book 解析器处理(文件名优先规则);README.md等仍走 Markdown。
图通过 coordination_parse、coordination_describe、coordination_chain 回答"工作区里有什么"。chain 查询在所有返回节点的动作上支持可选 kind(去空格、大小写不敏感),因此"所有 lemma""所有 chapter"是一次调用而不是全量扫描。目录节点从无歧义的工作区路径派生并以 contains/part_of 连接;不变量是**part_of(子→父)精确、contains(父→子)尽力而为**。
工作区面板
两个只读面板停靠在右侧并纵向堆叠:关系面板(文件/职能/依赖/导入/导出)与工作面板(文件/大纲/依赖/导入与导出/预览)。每个面板保留 48px 标题栏(菜单、搜索、最小化/还原),展开一个会把另一个顶上去。关系 tab 用原生 canvas 画关系图(方向流动、环脉动、hover tooltip、点击选中);职能 tab 列出主代理与子代理、预计进度环,并可打开只读消息页。预览 tab 渲染 Markdown/数学、净化后的 HTML、docx HTML、Prism 高亮代码、xlsx 表格与 pptx 卡片——全部只读、有界、no-store。状态只在 localStorage 存 { openLeft, openRight, width },视图是 hash 路由(#coordination/panel/right/<view>?path=…)。打包体积约 +2 MB(KaTeX、react-markdown、Prism、MUI icons),gzip 后约 700 KB。
设置与配置
设置页由 src/shared/selection.ts 的 SETTINGS_CONTROLS 生成:13 组、139 个控件、122 个可写宿主行,覆盖 155 个配置叶子。其中 12 个情景叶子(scenes.*.thinking、scenes.*.reasoningEffort)留在 schema 里但没有对应行:推理由 DSH 的模型页负责,这里再放一套会形成两个真相来源。18 个顶层配置键包含 database 与 databasePath,这两个键故意不投影、不可写。
| 组 | 默认值 / 范围 | 写入路径 |
|---|---|---|
session |
maxTurnPerSession=1000、defaultLease=30000 ms、clarifyTimeout=30000 ms |
宿主行,经 /config |
profession |
enabled/lock/rebuild/abandon 强制 true;default 镜像 defaults.profession;audit/errorHint true |
强制项只读;audit/errorHint 为宿主行(audit 无消费者) |
plan |
threshold 0.5、maxRebuild 3、8 个权重和为 1、8 个维度阈值 |
宿主行;保存后权重重新归一 |
orchestration |
maxReject 2、maxSubTask 32、groundTruth true、timeout 5000 ms、shell ground truth false、modelRouting true |
宿主行;groundTruthTimeout 无消费者 |
cache |
enabled true、ttl 60000 ms、fingerprintWindow 50、effortWarning true |
宿主行;enabled/effortWarning 控制缓存代理的改写开关,ttl 用于模型目录缓存,fingerprintWindow 无消费者 |
bayesian |
prior 0.5、stopEntropy 0.05、infoGain 0.01、maxRoutes 12、calibrationFloor 0.1 |
宿主行;calibrationFloor 无消费者 |
comprehension |
stageTimeout 30000 ms、searchEnabled true、maxEvidence 8、highHistoryWindow 50 |
宿主行 |
compaction |
auto true、soft 0.70、hard 0.88、reserve 2048、recentTurns 6、cooldown 30000 ms、maxPerSession 20 |
auto/soft/hard 为宿主行;结构字段只读 |
ui |
toolbar true(废弃别名)、showTooltip true、takeover false、refreshInterval 5000 ms、compact true、locale auto |
宿主行 + 两个本机开关;toolbar/locale 只读 |
scenes |
六个情景各 9 个可编辑行(思考/effort 仅保留在 schema);写作阈值 0.6 |
宿主行;只有写作评分阈值被 plan.ts 读取 |
parser |
enabled true、1 MiB、200k tokens、depth 256、2 s、cache 256、graph 20k/100k |
enabled + 四个上限为宿主行;depth 与 graph 上限仍是计划 |
chain |
depth 8、links 50、tokenBudget 1500 |
宿主行 |
tools |
maxResultBytes 0 |
计划/只读;未被消费 |
defaults |
职能 execution、交互 think、情景 dialogue、验证 medium、命令 none |
宿主行 |
GET /deepseek-coordination/config 返回 { values, defaults };defaults 来自 resolveConfig(undefined),页面不复制默认字面量。POST /config 接收扁平点路径补丁(如 { "plan.threshold": 0.7 }),按目录和真实 Schemastery schema 校验,写入活动配置,重新执行不变量(权重归一、别名镜像、强制开关、压缩阈值夹取),在压缩变化时重建 TokenGovernor,最后持久化实际写入的路径。
持久化落在宿主自己的覆盖文档 <数据库目录>/config.overrides.json:一个扁平的 { "plan.threshold": 0.7 } 映射,原子写在协调数据库旁边,下次启动时覆盖在 profile 配置之上。这里没有用 DSH 的 ctx.settings.update——它只接受 .volatile() schema,而 volatile 节点会把每个解析值变成引用包装,本宿主无法在不重写所有配置读取的前提下接受。因此 persisted: true 表示“已存入协调宿主自己的文档”;只命中 ignored 行的写入返回 false。未知键、databasePath、database 会以 INVALID_ARGUMENT 拒绝;已知但不可写的键进入 ignored,绝不写入。POST /config/compaction 保留为旧的 auto/soft/hard 兼容小路由。
设置页整体使用 Material UI:每组一个默认展开的 Accordion,行放在 List 中;六个情景各一张 Table(页面没有情景下拉框);布尔用 Checkbox,枚举用 RadioGroup,所有数字都用 TextField type="number"(含权重与比例,显示时按该行 step 取整);Chip 只表示 主机 / 本机 / 强制 / 只读 / 计划中 状态和连接状态。完全权限确认是一个位于 tooltip 层之上的 MUI Dialog 模态框,不再是内联色块。没有保存按钮,控件在 change/blur 时提交。强制行画禁用控件,只读行显示值,计划行明确说明当前运行时尚未读取。设置导航图标是宿主 shell 的限制所致:section 只接受 { id, order, label },插件只能用 CSS mask 给导航按钮补一个 Material sync 图标。
服务级接管
- 打开
ui.takeover后,TakeoverGate监听 DSH 的approval/request与user-questions/request瀑布。它写一条持久 pending 行、发出takeover/pending,等待客户端路由送来人工答案。 - 监听器用
{ prepend: true }注册。DSH 自己的审批/提问 UI 监听同一个瀑布,注册晚的监听器在 GUI 认领请求后永远不会执行。注意:部署的审批策略为never时,DSH 在进入瀑布之前就直接拒绝,此时无论开关如何都不会出现对话框。 - 超时、请求 signal 中止、显式取消或插件卸载都会调用
next();闸门绝不把“没人回答”变成allowed-once或编造的问题答案。unavailable是宿主 fail-closed 的结果,不作为用户决定接受。 - 对话框只在以下条件同时成立时每 1 秒轮询
/takeover/pending:本机“权限接管”开关打开、session id 真实、页面可见。它渲染第一条 pending 请求;队列多于一条时显示1 / N徽标。 - 审批提供 仅本次允许 / 拒绝 / 取消;只提交
allowed-once与rejected,取消/Esc/点背景走 cancel 路由。提问按单选渲染 radio、多选渲染 checkbox,并允许每题自由文本;所有问题答完前提交按钮禁用。 - 两个开关都需要:宿主
ui.takeover(默认false)负责打开闸门,本机“权限接管”负责让对话框轮询。宿主开关关闭时闸门立即委托,没有窗口可显示。
缓存代理(实验性,默认关闭)
DSH 在任何 adapter 看到请求之前就把它冻结了:buildRequest() 深冻结消息并 Object.freeze 请求,而 llm/stream 瀑布的兜底闭包持有这个对象。唯一还能重建出站内容的位置是 adapter 边界,所以插件注册一个额外 provider 路由(coordination-proxy),把每个请求重建后转发给真实 provider。
接线(src/index.ts、src/host/proxy-adapter.ts、src/host/model-brand.ts):
agent/request把路由改成coordination-proxy并记住真实路由。只有白名单内的 provider(deepseek、deepseek-account、deepseek-official)会被拦截,其他 provider 保持宿主选择的路由。- adapter 用真实 provider 调
ctx.llm.stream转发,传输、重试、归因头和用量统计仍由真实 adapter 负责。 ctx.llm.resolveModelInfo(provider, model)提供宿主的模型身份,再分类到内置缓存品牌(DeepSeek、OpenAI、Anthropic、Google、xAI、Moonshot、Qwen、Zhipu、Mistral、Meta、未知),带automaticPrefixCache、explicitCacheBreakpoints、minPrefixTokens、effortBreaksCache、stripVolatileHelps。- 只有当该家族有自动前缀缓存且
cache.enabled打开时才改写出站内容:CacheModule.decorate把固定内容前置,并把易变片段(ISO 时间戳、UUID、长 hex、"当前时间"措辞)替换为稳定占位符。显式断点家族(Anthropic)永不改写,因为那里决定复用率的是标记而不是字节。仅供 UI 的cacheWarning字段会在转发前剥掉。
安全模型——代理能看到所有 prompt,因此设计成无法成为泄露通道:
- 不接触凭据(无
ctx.credentials、无 token、无Authorization、无 API key); - 无出网能力(模块零 import,没有
fetch/http/net;转发是进程内调用); - 不落盘内容——
proxy.log只收结构化事件({event, provider, model, brand, messages, chars, tools, reason}),绝无 prompt 或回答正文; - 白名单在路由监听器和 adapter 双侧强制;
- 拒绝/失败信息只报路由名;
- 默认关闭、可逆。
启用方式:创建 %DSH_HOME%\coordination\proxy.enable(或 DSH_COORDINATION_PROXY=1)后重启。GET /deepseek-coordination/proxy/status 报告计数与标识:available、enabled、decorate、upstream、upstreamProvider、model(品牌与策略)、mode、metrics、prefix、cacheStrategy、forwards、refusals。删掉开关文件重启即回到普通链路。
观测与改写(measure 与 normalize)
接上代理与改写请求是两个独立开关。代理启用后默认是 measure:只恢复真实 provider 路由,从不调用缓存装饰器。要让装饰器运行,需创建内容为 normalize 的 proxy.mode 文件、创建 proxy.rewrite 标记文件,或设置 DSH_COORDINATION_PROXY_REWRITE=1,然后重启。
两种模式下 proxy.log 每次转发都会多一行 prefix({ prefix: '<16 hex>', stable }),指纹变化时再写一行 prefix-churn({ from, to })。metrics 聚合最近 100 次请求(requests、samples、inputTokens、cachedInputTokens、hitRate;DSH 的 cacheReadTokens 以 cachedInputTokens 呈现)。读取真实会话基线:用服务 Web GUI 的端口执行 curl http://127.0.0.1:<port>/deepseek-coordination/proxy/status。
与按 provider id 精确匹配的宿主模块的交互
改写对所有 agent/request 消费者可见:代理接入期间,请求里记录的路由是 coordination-proxy,而不是真实 provider。因此 /proxy/status 在 upstream 之外还暴露 upstreamProvider(改写前捕获的真实 provider)。按 provider id 解析凭据或账户状态的宿主模块在这种改写下可能出错;已知案例是宿主 web-search 的账户回退,需要宿主侧修复(0.2.1-rc.2 补丁已包含)。ProxyService.compatibility() 以静态数据携带同一份清单,代理不探测宿主。
自动压缩与 Token 治理器
TokenGovernor 估算上下文(CJK 感知的 O(n) 估算器,带 256 项有界 memo),按 contextWindow - reserve 规划预算,并在 agent/pre-step 利用率越过阈值时请求 DSH 的 compactIfNeeded。默认:soft 0.70、hard 0.88、reserve max(2048, 窗口的 8%) 且不超过窗口 25%、recentTurns 6、cooldown 30 s、maxPerSession 20。
soft 阈值可按 0.30–0.90 的比例编辑;hard 为 0.30–0.98,且必须严格高于 soft。旧界面里的 60/70/80/88% 仍是合法取值(0.60、0.70、0.80、0.88),只是目录页现在用 0.01 精度的数字输入框,而不是四项选择器。compaction.auto=false 只关闭自动压缩,不约束显式 compact 命令。
失败封闭是刻意设计:钩子绝不阻塞 step;压缩 port 缺失、返回 null/undefined、超时、抛错、每会话额度用尽或 cooldown 未到,全部按 no-op 处理,agent 继续执行 next()。治理器在构造时快照策略,所以设置热更新会同时重建它。实际安全范围和替换由 DSH 负责,本插件只决定何时值得请求。
文件工具、解析器与 Chain
文件层以四个模型可见工具接线,加上三个输入框/职能工具,共 7 个:
| 工具 | 用途 |
|---|---|
coordination_parse |
解析工作区文件或给定文本。path/text 二选一;返回引用、诊断、字符集、截断标记和节点卡片,绝不返回文件正文。 |
coordination_describe |
按 handle 返回一个 NodeCard,可选 summary/description/usage/purpose;不返回正文。 |
coordination_chain |
查询一条有界关系链:dependencies、dependents、satisfies、satisfied_by、baseline、prerequisites、unmet、path、cycle。深度 ≤ 8、链接 ≤ 50、约 1,500 tokens;不返回 AST 或图对象。 |
coordination_read |
唯一返回文件内容的工具。读字节范围(end 不含)或行范围(1 基,end 含),单次上限 256 KiB / 2,000 行 / 16k tokens。 |
P0 解析器是词法级、确定性的:JS/TS/JSX(import/export/require/动态 import/符号/注释)、CSS/HTML/Markdown(@import/url()、script/link/img、链接/图片/标题/front matter/fenced div/公式)、TeX(定理类环境、证明、label/ref、数学、\input/\include)、通用公式(Markdown/TeX/表格/MathML/OMML/unknown;不求值)、词法依赖图(外部过滤、拓扑序、环、层级、孤立点)。畸形输入产生诊断和 truncated,不崩溃。
每个解析节点都是蓝图式元数据:path、UTF-8 byteStart/byteEnd、1 基 lineStart/lineEnd、sha256:<hex> 指纹和 charset;没有 text、body、content 或 AST。NodeCard 与 ChainLink 同样如此。coordination_read 是唯一内容路径;它拒绝绝对路径、UNC、目录穿越和符号链接逃逸,拒绝 NUL/二进制/不可解码内容,并对返回内容做哈希。
| 上限 | 值 |
|---|---|
| 单次解析源文本 | 1 MiB;只解析前缀并标 truncated |
| 单次解析 token | 200,000 |
| AST 深度 | 256 |
| 解析墙钟时间 | 2,000 ms |
| 诊断保留 | 100 条 |
| 单次解析节点元数据 | 20,000 |
| 依赖图 | 20,000 节点 / 100,000 边,超限 fail-closed |
| 公式 | 单次扫描 5,000 个 / 512 条引用 |
| 解析缓存 | 256 文件,按 path+mtime+size+parserId 做 LRU |
| 读取范围 | 256 KiB / 2,000 行 / 16,000 估算 token |
| Chain 投影 | 深度 ≤ 8 / 链接 ≤ 50 / 约 1,500 tokens;50k 节点 / 200k 边 fail-closed |
parser.enabled 在注册文件工具时读取;parser 各上限与 chain.depth/links/tokenBudget 在惰性工具运行时创建 ParseService/ChainEngine 时捕获。因此 /config 的热改不会调整已创建的运行时,要到下一次注册(目前是语言切换或重启)才生效。工作区根默认回退到 process.cwd(),因为插件入口没有拿到 DSH 的 workspace root;cwd 不是工作区的宿主必须通过工具选项传入根目录。
没有写入或刷新工具。 v24 的写入契约需要 expectedFingerprint 冲突检测、事务式重解析与范围重算、显式 charset/BOM 变更以及 watcher/rename 策略;这些都没有实现,贸然提供 writer 会覆盖并发编辑或返回过期范围。coordination_write 与 coordination_refresh 属于设计/计划。
数据结构
src/shared/ 的共享库是纯 ES2022:hash/LRU、节点、二叉树/B+ 树/红黑树、堆、栈/双端队列/队列、有序 map/set/skip list、AST/TokenStream、有向图、Trie、稀疏网格。当前运行时实际使用:
LruList:token 估算 memo、解析缓存、节点注册表、Chain 投影缓存。BinaryHeap:selectContext,保持原全序(value 降序 → created 升序 → id 字节升序 → 输入下标)。RedBlackTree前驱/后继:数值一致性(numeric agreement);精确 argmax 证明在采用文档中。OrderedSet:comprehension/emotion/permission 的首次出现去重;Queue:权限命令 tokenizer。DirectedGraph:依赖图,以及每个 Chain relation 一张图。
第二轮采用有随机化等价测试,也有一个诚实 caveat:在 20k chunk 的 wide 合成用例里,堆并不比 V8 sort 快(17.7 ms 对 15.9 ms);它在预算提前填满时占优,且结果不劣于 sort。SparseGrid 与 BPlusTree 只在共享库里有回归测试,没有接到运行时路径;不要据此认为它们在用。
工具与路由
| 工具 | 返回 |
|---|---|
coordination_selection |
读取或修改某 session 的五个选择(含 command 与 DSH preset);返回带锁、预设和配置投影的 SelectionState。 |
coordination_profession |
读取当前职能记录;仅在活动任务之前/不存在时设置职能。任务已锁时返回 PROFESSION_LOCKED,不改选择。 |
coordination_professionlock |
读取活动 Turn 的锁定状态/职能,或指定序号 Turn 的状态。 |
coordination_parse |
无正文的解析投影(引用、诊断、字符集、截断、卡片)。 |
coordination_describe |
无正文的 NodeCard 和请求的元数据字段。 |
coordination_chain |
只返回有界 ChainProjection。 |
coordination_read |
唯一返回内容的工具;有上限的字节/行范围。 |
所有路由都在 /deepseek-coordination 下,共 14 条路径 / 16 个方法处理器:
| 方法 | 路径 | 作用 |
|---|---|---|
| GET | /ping |
存活检查;返回插件名、pong、当前语言。 |
| POST | /echo |
回显 JSON body(诊断用)。 |
| GET, POST | /selection |
读 SelectionState;修改五个选择。 |
| GET | /presets |
投影 DSH 权限预设目录。 |
| POST | /permission |
立即应用一个 DSH 预设。 |
| GET, POST | /config |
读 { values, defaults };应用扁平配置补丁。 |
| POST | /config/compaction |
auto/soft/hard 的兼容补丁。 |
| GET | /takeover/pending |
某 session 的待处理审批/提问窗口。 |
| POST | /takeover/answer |
提交合法人工答案;非法答案保持窗口。 |
| POST | /takeover/cancel |
取消窗口并交还宿主默认处理。 |
| POST | /profession/rebuild |
结束活动任务,用新职能创建替代 Turn。 |
| POST | /profession/abandon |
结束活动任务,不创建替代。 |
| POST | /locale |
上报客户端语言;重新注册本地化工具描述。 |
| GET | /state |
选择 + 协调指标(session、turn、plan、subtask、trace、takeover、schema)。 |
没有 coordination_write、coordination_refresh、outline、findSymbol、searchSummary、commentsOf 工具。四个文件工具的说明目前是内联中英双语,因为共享 ToolName 表还没有它们的键;三个输入框/职能工具使用共享 copy 表。
数据、隐私与安全
- 插件使用独立 SQLite 库,从不打开 DSH 的
harness库。默认路径是%DSH_HOME%\coordination\coordination.db;database/databasePath只属于部署配置,既不投影也不允许/config写入。 - 解析节点、
NodeCard、ChainLink及其工具结果都不带文件正文、附件或 AST。只有coordination_read返回文本,而且只返回请求的范围。 - 预览是唯一承载正文的路由:仅工作区相对路径、正文上限 64 KiB、由用户主动触发、
no-store、不缓存也不写日志。其余/graph/*路由保持 body-free。Office 预览在服务端转换并限制大小,不写任何文件。 - 工作区面板是只读的:不编辑、不重命名、不删除、不新建、不构建;唯一的非 GET 图路由是
POST /graph/rebuild,它只重新解析已索引路径。 - 文件路径必须是工作区相对路径;绝对路径、盘符路径、UNC、
..逃逸与符号链接逃逸在读取前拒绝。二进制/NUL/不可解码内容直接拒绝,不猜。 - Turn 钩子只写 Turn 元数据和一条空 prompt 行,不镜像 DSH 对话,也不把文件内容复制进协调库。
- 权限预设立即写给 DSH(完全权限需二次确认),从不进入离线 localStorage;离线重放只覆盖五个已存选择字段。
- 可选的缓存代理不新增传输层和凭据面:它通过 DSH 自己的
ctx.llm.stream转发,只记录结构化计数,并拒绝白名单之外的 provider。 - 接管绝不自动批准。超时、中止、取消、卸载都以
next()委托;只有通过 answer 路由送达的人工决定才会结算窗口。 - 失败封闭是明确的:非法接管答案保持窗口;
PROFESSION_LOCKED不改选择;解析不确定标unverifiable;解析/图溢出标truncated;压缩失败是 no-op;Chain 的unknown不会自动通过。 - 协调库保存任务/审计行(包括
prompt与追踪行),不保存凭据或 provider secret。配置投影中没有 DSH 凭据、token 或 provider key。
测试、质量与限制
| 检查 | 结果 |
|---|---|
npm test |
615 tests,614 pass,1 skipped(仅 GC),0 fail,Node 24.21.0 上约 3 s |
npm run typecheck / npm run test:tsc |
通过 |
npm run build |
宿主 + 客户端 bundle 通过;MUI 的 "use client" directive 警告不致命 |
npm run pack:check |
OK:运行时 tarball 含四个发布文件 |
| 发布 CI | .github/workflows/release.yml 运行 typecheck、test:tsc、测试、pack:check,并在 v* Release 附上 tarball |
解析器与结构的数字是守卫,不是承诺:100k 字符解析测试保持在 3 秒内;数据结构报告记录 B+ 树 10k 插入 8.3/9.8 ms、skip list 10k 插入 6.4 ms、红黑树 10k 随机 set/delete 13.5 ms、稀疏网格 10k 单元 5.1 ms、hashString 100k 次 4.3 ms;复杂度审计记录 Turn 序号从 COUNT(*) 改为 MAX(ordinal)(2,000 条语句约 65–69 ms → 约 12–14 ms)以及路由排序(200 × 12 条路由的 2,400 次 posterior 查询降到 200 次)。
已知限制,直接列出而不是隐藏:
- 持久图有边界。
node/edge/snapshot已是 schema 24,跨运行时保留;但重水化只取最新 10k 节点 / 50k 边,删除的符号不会清理,超出窗口的旧 handle 返回NOT_FOUND。 - 领域提取在解析器层,部分投影仍不完整。 Book/Article/Part/Chapter/Section/Paragraph、Person/Venue、Condition、Sheet/Cell/Range、TeX 证明状态和 CSV/TSV 表格已产出并投影到节点卡;docx/xlsx/pptx 现在通过有界 ZIP+OOXML 读取器解析,Publisher/Access 明确不支持。
partOf/condition边已可通过coordination_chain查询;跨文件 Book 组合与 Sign 提升仍未实现。 - 核心 Turn 流水线已接线,更宽的引擎没有。
command=none的 Turn 会运行 Comprehension → Organization → Plan/score,以及需要的 Predict 行;有端口的 scene 策略会生效。独立 Emotion/Bayesian/Cognition/Orchestration/Learning/Resource/Score、计划重建循环和 decompose/dispatch/verify/synthesize 仍是库级。thinking、allowBash、maxIteration和非写作阈值记录为延迟诊断,不伪造。 - 配置上限已生效。
parser.maxAstDepth、两个依赖图上限和tools.maxResultBytes都有消费者;parser/chain 上限会 retune 已存在运行时,不再留下陈旧运行时。 - 工作区根已解析,不再硬编码。 工具参数 → service 根 → host 根 → sandbox policy 根 → 最后才回退
process.cwd()。 parser.enabled仍需重新注册。 其他文件层上限会即时 retune;关闭 parser 会立即失败关闭,但从工具列表增删四个工具仍要等下一次注册(重启)。- 没有浏览器 E2E。 UI 由类型、构建、settings-catalog 测试以及 takeover/bridge/集成测试覆盖;视觉验证仍是人工/截图。
- Node 引擎为
>=22.13.0。node:sqlite从 22.13.0/23.4.0 起不再需要实验开关;CI 与本次验证使用 24.21.0。 - profile 里可能仍记录着旧的包 spec。 请核对实际安装的版本(
dsh --profile <name> --dump-config或 profile 的package.json);安装 0.11.0 tarball 或链接已构建的 checkout,不要假定上一次安装已被升级。 - 缓存代理是实验性的、默认关闭。 品牌 profile 是策略默认值而不是实测值,且只对自动前缀缓存的家族改内容;在已经稳定的会话上收益可能接近零——这正是它藏在开关和状态端点后面的原因。
仓库历史与许可
0.1.x 是另一个项目 dsh-os,已不在分支顶端;0.2.0 变成 DSH 插件 bundle;0.3–0.6 的 UI 从 Angular 经 MDUI、纯 React 迁到 Material UI;0.7–0.9 加入五个下拉框、i18n、职能锁定、接管、压缩、P0 解析器、领域模型和共享数据结构;0.10.0 加入设置目录、四个文件工具、第二轮数据结构采用和可验证 tarball;0.11.0 加入持久设置、推理交还 DSH,以及带品牌策略的实验性缓存代理;0.12.0 加入按体系拆分的领域层、chain kind 过滤、目录层与 NodeRecord。旧状态仍在 git 历史和 tag 中。
Apache-2.0 © 2026 Eloy Kim。见 LICENSE。源码:github.com/eloyhere/deepseek-coordination。