@yangdcm/dsh-expert-team 1.3.11 → 1.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +46 -0
- package/README.en.md +95 -23
- package/README.md +100 -28
- package/client.js +48 -14
- package/lib/command.js +91 -17
- package/lib/interception.js +6 -4
- package/package.json +7 -3
- package/skills/expert-team/SKILL.md +31 -30
- package/skills/expert-team/assets/templates/SUMMARY.md +2 -2
- package/skills/expert-team/assets/templates//344/273/273/345/212/241/347/234/213/346/235/277.md +1 -1
- package/skills/expert-team/references/EFFICIENCY.md +1 -1
- package/skills/expert-team/references/LOGGING.md +7 -7
- package/skills/expert-team/references/PERSIST.md +9 -9
- package/skills/expert-team/references/PIPELINE.md +1 -1
- package/skills/expert-team/references/ROLES.md +32 -32
- package/skills/expert-team/references/WORKSPACE.md +19 -19
- package/skills/expert-team/references/workflow.team.js +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,52 @@
|
|
|
3
3
|
本包遵循[语义化版本](https://semver.org/lang/zh-CN/)。dsh 宿主版本线的对应关系写在
|
|
4
4
|
`package.json` 的 `engines.dsh` 与 `dsh.compatibility` 里,插件市场按它判断"这个插件跟你的宿主兼不兼容"。
|
|
5
5
|
|
|
6
|
+
## 1.3.13
|
|
7
|
+
|
|
8
|
+
**渐进式状态:`/state` 支持 `?section=`,首屏与会话体量解耦**(性能修复 #3)
|
|
9
|
+
|
|
10
|
+
- 背景(真机实测,`DSH_EXPERT_TEAM_STATE_PROFILE=1` 分步):完整负载里 `subs` **1 327–1 388 ms**、
|
|
11
|
+
`roles` **638–869 ms**、`runs+select` 15–21 ms、`tail` 3 ms;重会话(20+ 子代理、仍在活跃写日志)
|
|
12
|
+
总耗时 **2.1–6.2 s**,成本随「子代理数 × 日志体量」线性增长。
|
|
13
|
+
- 新增 `?section=summary|people|feed|artifacts`(逗号可多选;**缺省或 **ALL** = 完整负载、字段语义不变**
|
|
14
|
+
⇒ 老客户端/脚本/既有测试不受影响):
|
|
15
|
+
- `summary`(首屏用):阶段/进度/计数/违规/告警 —— 真机 `snapshotRun` 中位 **0.3 ms**;
|
|
16
|
+
- `people`:子会话清单/角色/成员时间线(最贵的那两块就在这里);
|
|
17
|
+
- `feed`:每 agent 最近事件;`artifacts`:`RUN.log` 尾 + 工作区改动文件。
|
|
18
|
+
- **上限 + 如实降级**:角色解析硬上限 `DSH_EXPERT_TEAM_MAX_ROLE_SUBS`(默认 60;超出部分如实进
|
|
19
|
+
`degraded: "roles:<n>"`);people 软期限 `DSH_EXPERT_TEAM_PEOPLE_DEADLINE_MS`(默认 2500 ms;超时跳过
|
|
20
|
+
workflow 元数据并记 `wf:deadline`)。响应新增 `sections`/`degraded`/`degradedReason`(缺块 ≠ 空数据)。
|
|
21
|
+
- **副作用修正**:`summary` 路径不再触发「派工即登记」写 `STATE.json`(原先每轮询都可能因空 `subById`
|
|
22
|
+
算出"清空成员"而覆写工件)⇒ **摘要轮询是纯读**。
|
|
23
|
+
- 客户端:**一快一慢双订阅**(`summary` 按 `pollMs`;`people,feed,artifacts` ≥ 6 s)+
|
|
24
|
+
`mergeStatePayload` **合并**(摘要那一拍不再抹掉已知成员);`stateHub` 改为**按 URL 各自计时**
|
|
25
|
+
(`stateHubBaseOf/DueAt`,tick 只发已到期的 URL)—— 旧实现"全局取最小 base、每 tick 拉全部 URL"
|
|
26
|
+
正是"慢端点被快钟拖着跑"的根源。
|
|
27
|
+
- 口径对齐(同批):`templates` 常量与两个 e2e 的"run 必需文件"清单**语义不同、关系确定**,
|
|
28
|
+
由 `artifact-ownership.test.mjs` 的 **E 断言**钉死(常量 ≡ 模板目录;e2e 清单 ≡ 常量 ∪ `RUN.log.md`);
|
|
29
|
+
README/README.en/llms 写实 `METRICS.md` 的来源与位置(**`team/` 根的聚合快照**,`/team learn` 产出),
|
|
30
|
+
并补一句 **R1 工件归属**(工件由产出它的角色自己写;lead 只读门控)。
|
|
31
|
+
- 验证:`npm run test:all` EXIT=0(**82 个测试文件**;新增 `state-sections.test.mjs`);
|
|
32
|
+
变异目录随源码文本**同提交**更新(`M70` 的 `find` 串)。
|
|
33
|
+
|
|
34
|
+
## 1.3.12
|
|
35
|
+
|
|
36
|
+
**面向"可被 AI 检索与引用"的文档与元数据补齐**(纯文档/元数据,代码与 1.3.11 完全相同)。
|
|
37
|
+
|
|
38
|
+
- README(中英各自对等)新增:
|
|
39
|
+
- **速览 / At a glance** 事实表:包名 · npm · 仓库 · 宿主版本 · 运行时依赖 · Node · License · 一行安装 · 一行上手 · 过程产物路径 · 本机数据路径;
|
|
40
|
+
- 一句**同义词行**(dsh 插件 · DeepSeek Harness 多智能体 · agent team · orchestration · 角色化 subagent · DAG 并行 · 阶段门控 · 质量门禁 · 工件留痕)—— 自然成句,不堆砌关键词;
|
|
41
|
+
- **常见问题 / FAQ**:用自然语言问句覆盖「是什么 / 与"一个 agent 硬做"的区别 / 是否必须装别的插件 / 支持哪些 dsh 版本 / 数据放在哪 / 怎么卸载 / 会不会联网 / 支持哪些模型 / 不切 preset 能否用 / 打开很慢怎么办」;
|
|
42
|
+
- **术语 / Glossary**:12 角色 ↔ 代码 id、9 阶段 ↔ id(以 `lib/vocab.js` 为唯一真源)、主要工件清单;
|
|
43
|
+
- 主要小节标题改为**双语**(安装 / 依赖与推荐插件 / 快速上手 / 自定义预设 / 常见问题 / 术语)。
|
|
44
|
+
- **图片 alt 文本**全部换成带关键词的描述性文案(此前偏短 —— alt 是可机读文本,不该白丢)。
|
|
45
|
+
- 「快速上手」的命令块改为 **14 行速查表**(命令 + 一句话用途),措辞以 `lib/command-parse.js` 的 `USAGE` 为真源。
|
|
46
|
+
- 新增仓库根 [`llms.txt`](https://github.com/yangdcm/dsh-expert-team/blob/main/llms.txt):按 [llmstxt.org](https://llmstxt.org) 约定给 LLM 用的索引 —— 事实块(可逐条核对)+ 文档链接(只列仓库里真实存在的文件)+ 诚实边界;README 顶部加一行指路。
|
|
47
|
+
- `package.json` `keywords` 补 4 个:`role-based` / `spec-driven` / `dag` / `agentic-workflow`(`description` 未动)。
|
|
48
|
+
- GitHub 仓库 topics 补齐:`ai-agents` / `agent-orchestration` / `multi-agent-systems` / `llm` / `cordis` / `code-generation` / `workflow-automation`。
|
|
49
|
+
|
|
50
|
+
> 两条**没有做**的(有意):不写隐藏关键词/meta 标签(GitHub 会剥掉,且属取巧);不写夸大话术 —— 本节所有事实都能在仓库或实测里核到。
|
|
51
|
+
|
|
6
52
|
## 1.3.11
|
|
7
53
|
|
|
8
54
|
**`/state` 的 `subs` / `roles` 两段收口 —— 这次不靠预测,靠 1.3.10 留下的分步计时开关实测定位。**
|
package/README.en.md
CHANGED
|
@@ -6,7 +6,7 @@ English | [中文](README.md)
|
|
|
6
6
|
[](https://github.com/yangdcm/dsh-expert-team/blob/main/LICENSE)
|
|
7
7
|
[](https://github.com/yangdcm/dsh-expert-team/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
> **One sentence in, a gated team delivery out.** `/team build a payments module with login`
|
|
12
12
|
> assembles a 12-role expert team and runs
|
|
@@ -22,6 +22,25 @@ A plugin for [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)
|
|
|
22
22
|
|
|
23
23
|
> Zero runtime dependencies. Recommended: also install **Hindsight** (cross-project memory) — see [Dependencies and recommended plugins](#dependencies-and-recommended-plugins).
|
|
24
24
|
|
|
25
|
+
## At a glance
|
|
26
|
+
|
|
27
|
+
| Item | Value |
|
|
28
|
+
|---|---|
|
|
29
|
+
| Package | `@yangdcm/dsh-expert-team` (public npm package) |
|
|
30
|
+
| Repository | <https://github.com/yangdcm/dsh-expert-team> |
|
|
31
|
+
| Host | [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) ≥ **0.1.5-rc.1** (`web` profile) |
|
|
32
|
+
| Runtime dependencies | **none** (`dependencies: {}`; `lib/` imports only sibling files and Node builtins) |
|
|
33
|
+
| Node.js | ≥ 20 |
|
|
34
|
+
| License | MIT |
|
|
35
|
+
| Install (one line) | `dsh plugin --profile web add @yangdcm/dsh-expert-team` |
|
|
36
|
+
| First run (one line) | switch the session to "Expert team mode" → `/team build a payments module with login` |
|
|
37
|
+
| Process artifacts | `<your workspace>/team/<run-id>/` (`SPEC.md` · `PLAN.md` · `TASKS.json` · `REVIEW.md` · `TEST.md` · `SUMMARY.md` …) |
|
|
38
|
+
| Machine-local data | `$DSH_HOME/expert-team/` (`settings.json` · `LEARNINGS.md` · `session-runs.json`) |
|
|
39
|
+
|
|
40
|
+
> A dsh plugin · a DeepSeek Harness multi-agent (agent team) orchestrator: role-based subagents · DAG parallelism · staged gates · quality gates · artifact trail.
|
|
41
|
+
>
|
|
42
|
+
> Index for LLMs and retrieval: [`llms.txt`](https://github.com/yangdcm/dsh-expert-team/blob/main/llms.txt)
|
|
43
|
+
|
|
25
44
|
## Who it is for
|
|
26
45
|
|
|
27
46
|
**A complete engineering department for small teams and solo builders** — no hiring, no assembling a team:
|
|
@@ -49,7 +68,7 @@ solo developers shipping a complete project · any long task where "someone inde
|
|
|
49
68
|
|
|
50
69
|
<sub>One honest boundary: this is **not** a human team — product-level and scope-level decisions remain yours.</sub>
|
|
51
70
|
|
|
52
|
-

|
|
71
|
+

|
|
53
72
|
|
|
54
73
|
<sub>Figure 1: the 9-phase gated pipeline. `spec-review` is a **hard gate** — if the SPEC.md
|
|
55
74
|
"boundaries and prohibitions" section is empty, the run does not advance (`lib/interception.js`).
|
|
@@ -71,9 +90,10 @@ $ /team build a payments module with login
|
|
|
71
90
|
├─ implement backend … fan out along the DAG → edits your workspace directly
|
|
72
91
|
├─ review sec·reviewer independent review (cross-model) → REVIEW.md
|
|
73
92
|
├─ test qa repro, coverage, regressions → TEST.md
|
|
74
|
-
└─ deliver docs wrap-up
|
|
93
|
+
└─ deliver docs wrap-up → SUMMARY.md
|
|
75
94
|
|
|
76
95
|
Persisted throughout: TASKS.json · ROSTER.json · STATE.json · AUTHORITY.md · RUN.log.md
|
|
96
|
+
Aggregate snapshot: `METRICS.md` lives at the **`team/` root** (produced by `/team learn`; not a run-dir template)
|
|
77
97
|
On disk at: <your workspace>/team/<run-id>/
|
|
78
98
|
```
|
|
79
99
|
|
|
@@ -96,32 +116,32 @@ answer them:
|
|
|
96
116
|
|
|
97
117
|
## What it looks like in action
|
|
98
118
|
|
|
99
|
-

|
|
100
120
|
|
|
101
121
|
<sub>Figure 2: **the full-screen canvas**. Look at the phase bar and progress, the roster (who is running, on which model), and the four views (people / tasks / artifacts / board) — one layer above the floating panel.</sub>
|
|
102
122
|
|
|
103
|
-

|
|
104
124
|
|
|
105
125
|
<sub>Figure 3: **the task dependency graph** — 11 tasks advance in parallel along the dependency DAG; 7 completed, 4 failed. A failure triggers `repair` plus **independent re-verification** (`repair-1 → review-2 → repair-2 → review-3`) until it passes or is honestly marked as needing revision — this is what the "rework does not converge" hard gate looks like in a real run.</sub>
|
|
106
126
|
|
|
107
|
-

|
|
108
128
|
|
|
109
129
|
<sub>Figure 4: **gate violations**. Look at the banner at the top — the violation and its refusal reason
|
|
110
130
|
(e.g. "SPEC.md's boundary section has entered `implement` but still has no 'expected rejection' row")
|
|
111
131
|
is decided by `lib/interception.js`, hooked onto the host's `tools/post-execute` waterfall, and surfaced
|
|
112
132
|
immediately. This is code, not a prompt reminder.</sub>
|
|
113
133
|
|
|
114
|
-

|
|
115
135
|
|
|
116
136
|
<sub>Figure 5: **the roster**. Look at the member list — who is running, on which model, and what it is doing;
|
|
117
137
|
expand a member for its tasks and artifacts. Models are configurable per role; heterogeneous models are used for cross-checking.</sub>
|
|
118
138
|
|
|
119
|
-

|
|
120
140
|
|
|
121
141
|
<sub>Figure 6: **phases and artifacts**. Look at the phase bar and the preview pane — the current phase, the phases
|
|
122
142
|
already passed, and the actual body of the artifact written in that phase (artifacts are the single source of truth; the overlay is just a view of them).</sub>
|
|
123
143
|
|
|
124
|
-

|
|
144
|
+

|
|
125
145
|
|
|
126
146
|
<sub>Figure 7: **settings**. Look at the official `Settings → Expert team` page — 18 settings, Chinese labels,
|
|
127
147
|
**saved on change and applied immediately** (caps, rounds, the tier gate and the oscillation detector are recomputed
|
|
@@ -271,20 +291,24 @@ dsh plugin --profile web add dshmarket
|
|
|
271
291
|
|
|
272
292
|
## Quick start
|
|
273
293
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
`/team
|
|
294
|
+
| Command | What it does |
|
|
295
|
+
|---|---|
|
|
296
|
+
| `/team <one-sentence goal>` | one sentence in, a one-shot team delivery out |
|
|
297
|
+
| `/team --persist <task>` | persistent live team: members can be re-tasked, survives sessions |
|
|
298
|
+
| `/team --one-shot <task>` | inverse override: run once even if persistence is the default |
|
|
299
|
+
| `/team --no-code <task>` | produce planning/review/test artifacts only, change no code |
|
|
300
|
+
| `/team --code <task>` | inverse override: touch code even if "artifacts only" is the default |
|
|
301
|
+
| `/team --confirm <task>` | create the run but do not dispatch; click "run" in the overlay |
|
|
302
|
+
| `/team --tier <tier> <task>` | pick the process tier (fast / standard / strict) |
|
|
303
|
+
| `/team status` | phase, members, model plan and live violations for every run |
|
|
304
|
+
| `/team models [<run>]` | cost / model plan per role |
|
|
305
|
+
| `/team canvas [<run>]` | render the team canvas (HTML); `--watch` refreshes live |
|
|
306
|
+
| `/team learn` | aggregate logs → `METRICS.md` + distilled lessons → `LEARNINGS.md` |
|
|
307
|
+
| `/team wait [<run>]` | in-flight task progress (non-blocking) |
|
|
308
|
+
| `/team resume <run-id>` | resume across sessions |
|
|
309
|
+
| `/team uninstall` | reclaim what this plugin laid down under `$DSH_HOME` |
|
|
310
|
+
|
|
311
|
+
Full command list (`/team codeindex` code index, `/team limit` quotas, `/team settle` cold-start settlement, …) — see `/team help`.
|
|
288
312
|
|
|
289
313
|
**Where artifacts land**
|
|
290
314
|
|
|
@@ -292,6 +316,54 @@ Full command list (`/team canvas` visual canvas, `/team codeindex` code index, `
|
|
|
292
316
|
- `$DSH_HOME/expert-team/` — machine-local preferences and cross-project experience: `settings.json`,
|
|
293
317
|
`session-runs.json`, `LEARNINGS.md`
|
|
294
318
|
|
|
319
|
+
## FAQ
|
|
320
|
+
|
|
321
|
+
**What exactly is it?** A plugin you install into your local `dsh`: `/team <one-sentence goal>` spins up a 12-role subagent team (product / architect / researcher / UI / backend / frontend / data / security / reviewer / QA / DevOps / docs), delivers through a 9-phase gated pipeline inside your workspace, and writes the whole process down as reviewable artifacts.
|
|
322
|
+
|
|
323
|
+
**How is it different from "one agent doing it all"?** It targets the three classic failure modes: **context drift** (hand-offs carry structured returns *and* artifact files, not chat history), **grading your own homework** (review and test are separate roles; a `qa`/`reviewer` verdict is required), and **rework that never converges** (over-budget rounds and unclosed items are stopped by hard gates and reported, not hidden).
|
|
324
|
+
|
|
325
|
+
**Do I have to install other plugins?** **No.** This plugin has zero runtime dependencies; Hindsight (cross-project memory) is **recommended** and `dsh-cost-meter` (cost view) is **optional** — the full flow runs without either. See [Dependencies and recommended plugins](#dependencies-and-recommended-plugins).
|
|
326
|
+
|
|
327
|
+
**Which dsh versions are supported?** `engines.dsh: >=0.1.5-rc.1` (developed and verified on 0.1.5-rc.1); earlier versions are untested. Node.js ≥ 20.
|
|
328
|
+
|
|
329
|
+
**Where does the data live?** Artifacts in your workspace: `<workspace>/team/<run-id>/`. Machine-local preferences and cross-project lessons: `$DSH_HOME/expert-team/` (`settings.json` / `LEARNINGS.md` / `session-runs.json`).
|
|
330
|
+
|
|
331
|
+
**How do I uninstall?** `/team uninstall` reclaims the copies this plugin laid down under `$DSH_HOME` (the skill is registered at runtime and never copied), then remove the plugin via the CLI or the marketplace. **Note:** `LEARNINGS.md` and friends under `$DSH_HOME/expert-team/` are **your data** and are kept.
|
|
332
|
+
|
|
333
|
+
**Does it go online by itself?** No. It only calls the tools the host gives the session (files, shell, subagents); network access depends on the tool face you grant.
|
|
334
|
+
|
|
335
|
+
**Which models does it use?** Whatever the host provides; this plugin supports **per-role model configuration** (the overlay shows which model each member runs), and heterogeneous models can be used for cross-checking.
|
|
336
|
+
|
|
337
|
+
**Does it work without the "Expert team mode" preset?** Yes. `/team` is a host-plane command and runs under any preset; it then falls back to the generic `subagent` (role personas go into the prompt), losing only the configuration-level boundaries (`toolFilter` / `maxDepth: 1`).
|
|
338
|
+
|
|
339
|
+
**Why is the overlay/canvas slow to open?** See [Troubleshooting](#troubleshooting) — since 1.3.5 `/state` no longer reads every sub-session log in full; upgrade to ≥ 1.3.5 and restart `dsh web`.
|
|
340
|
+
|
|
341
|
+
## Glossary
|
|
342
|
+
|
|
343
|
+
**Roles (12)**
|
|
344
|
+
|
|
345
|
+
| Term | Code id |
|
|
346
|
+
|---|---|
|
|
347
|
+
| Product | `pm` |
|
|
348
|
+
| Architect | `architect` |
|
|
349
|
+
| Researcher | `researcher` |
|
|
350
|
+
| UI/UX | `ui` |
|
|
351
|
+
| Backend | `backend` |
|
|
352
|
+
| Frontend | `frontend` |
|
|
353
|
+
| DBA | `dba` |
|
|
354
|
+
| Security | `sec` |
|
|
355
|
+
| Reviewer | `reviewer` |
|
|
356
|
+
| QA | `qa` |
|
|
357
|
+
| DevOps | `devops` |
|
|
358
|
+
| Docs | `docs` |
|
|
359
|
+
|
|
360
|
+
**Phases (9)**: `clarify` → `research` → `design` → `spec-review` (**hard gate**) → `方案确认` plan approval (**approval gate**, on by default, can be turned off) → `implement` → `review` → `test` → `deliver`. (Ids and Chinese labels come from the single source `lib/vocab.js`.)
|
|
361
|
+
|
|
362
|
+
**Key artifacts**: `SPEC.md` (spec and boundaries) · `RESEARCH.md` · `PLAN.md` · `TASKS.json` (task ledger) · `ROSTER.json` (staffing) · `STATE.json` · `AUTHORITY.md` (single source for write authority) · `REVIEW.md` · `TEST.md` · `SUMMARY.md` · `METRICS.md` (cost and timing — an **aggregate snapshot at the `team/` root**, produced by `/team learn`) · `RUN.log.md`
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
**Who writes**: each artifact is written **by the role that produces it** into `<run-dir>/`; the lead **only reads artifacts to gate and decide** (it has no `write` tool). `STATE.json` and `RUN.log.md` are maintained **by the runtime**.
|
|
366
|
+
|
|
295
367
|
## Layout
|
|
296
368
|
|
|
297
369
|
```
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
[](https://github.com/yangdcm/dsh-expert-team/blob/main/LICENSE)
|
|
7
7
|
[](https://github.com/yangdcm/dsh-expert-team/actions/workflows/ci.yml)
|
|
8
8
|
|
|
9
|
-

|
|
10
10
|
|
|
11
11
|
> **一句话组队交付**:`/team 做一个带登录的支付模块` —— 自动组建 12 角色专家团,走
|
|
12
12
|
> 澄清 → 调研 → 设计 → 规格评审 → 方案确认 → 实现 → 审查 → 测试 → 交付 的门控流水线,
|
|
@@ -18,7 +18,26 @@
|
|
|
18
18
|
|---|---|---|---|---|
|
|
19
19
|
| 各带人设 / `toolFilter` / `maxDepth: 1` | 含 1 道硬门 + 1 道确认门 | 含 136 条变异目录与多组棘轮 | `dependencies: {}` | 无 bundler、无 `prepare` 钩子 |
|
|
20
20
|
|
|
21
|
-
> 零运行时依赖。推荐同时装 **Hindsight**(跨项目记忆)—— 见[依赖与推荐插件](
|
|
21
|
+
> 零运行时依赖。推荐同时装 **Hindsight**(跨项目记忆)—— 见[依赖与推荐插件](#依赖与推荐插件--dependencies-and-recommended-plugins)。
|
|
22
|
+
|
|
23
|
+
## 速览 / At a glance
|
|
24
|
+
|
|
25
|
+
| 项目 | 值 |
|
|
26
|
+
|---|---|
|
|
27
|
+
| 包名 | `@yangdcm/dsh-expert-team`(npm 公开包) |
|
|
28
|
+
| 仓库 | <https://github.com/yangdcm/dsh-expert-team> |
|
|
29
|
+
| 宿主 | [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) ≥ **0.1.5-rc.1**(`web` profile) |
|
|
30
|
+
| 运行时依赖 | **无**(`dependencies: {}`;`lib/` 只 import 同目录文件与 Node 内建) |
|
|
31
|
+
| Node.js | ≥ 20 |
|
|
32
|
+
| License | MIT |
|
|
33
|
+
| 安装(一行) | `dsh plugin --profile web add @yangdcm/dsh-expert-team` |
|
|
34
|
+
| 上手(一行) | 会话切到「专家团模式」→ `/team 做一个带登录的支付模块` |
|
|
35
|
+
| 过程产物 | `<你的工作区>/team/<run-id>/`(`SPEC.md` · `PLAN.md` · `TASKS.json` · `REVIEW.md` · `TEST.md` · `SUMMARY.md` …) |
|
|
36
|
+
| 本机数据 | `$DSH_HOME/expert-team/`(`settings.json` · `LEARNINGS.md` · `session-runs.json`) |
|
|
37
|
+
|
|
38
|
+
> dsh 插件 · DeepSeek Harness 多智能体(multi-agent)编排器:角色化 subagent 团队 · 依赖 DAG 并行 · 阶段门控 · 质量门禁 · 工件留痕。
|
|
39
|
+
>
|
|
40
|
+
> 给 LLM / 检索用的索引:[`llms.txt`](https://github.com/yangdcm/dsh-expert-team/blob/main/llms.txt)
|
|
22
41
|
|
|
23
42
|
## 为谁而做
|
|
24
43
|
|
|
@@ -46,7 +65,7 @@ UI/UX、前后端、数据、安全、评审、测试、运维、文档这 12
|
|
|
46
65
|
|
|
47
66
|
<sub>一句诚实边界:它**不是**人类团队 —— 产品级与范围级决策仍由你拍板。</sub>
|
|
48
67
|
|
|
49
|
-

|
|
50
69
|
|
|
51
70
|
<sub>图 1:9 阶段门控流水线。「规格评审」是**硬门** —— SPEC.md 的「边界与禁止项」没填就不放行(`lib/interception.js`);「方案确认」是默认开启的**确认门**(`identity.keepPlanGate`,可在设置里关掉);「实现」阶段按依赖 DAG **并行扇出**,多个实现者同时开工、各自只改自己那份文件。</sub>
|
|
52
71
|
|
|
@@ -65,9 +84,10 @@ $ /team 做一个带登录的支付模块
|
|
|
65
84
|
├─ 实现 backend … 按依赖 DAG 并行扇出 → 直接改你工作区的代码
|
|
66
85
|
├─ 审查 sec·reviewer 独立评审(换模型交叉验证) → REVIEW.md
|
|
67
86
|
├─ 测试 qa 复现、覆盖、回归 → TEST.md
|
|
68
|
-
└─ 交付 docs
|
|
87
|
+
└─ 交付 docs 收尾结论 → SUMMARY.md
|
|
69
88
|
|
|
70
89
|
全程留痕:TASKS.json · ROSTER.json · STATE.json · AUTHORITY.md · RUN.log.md
|
|
90
|
+
聚合快照:`METRICS.md` 在 **`team/` 根**(`/team learn` 产出;不是 run 目录模板)
|
|
71
91
|
落盘位置:<你的工作区>/team/<run-id>/
|
|
72
92
|
```
|
|
73
93
|
|
|
@@ -87,27 +107,27 @@ $ /team 做一个带登录的支付模块
|
|
|
87
107
|
|
|
88
108
|
## 看一眼它在干什么
|
|
89
109
|
|
|
90
|
-

|
|
91
111
|
|
|
92
112
|
<sub>图 2:**全屏画布**。看阶段条与进度、团队编制(谁在跑、用哪个模型)、以及 `人 / 事 / 料 / 盘` 四个视角 —— 比浮层更完整的一层视图。</sub>
|
|
93
113
|
|
|
94
|
-

|
|
95
115
|
|
|
96
116
|
<sub>图 3:**任务依赖图** —— 11 个任务按依赖 DAG 并行推进;7 个完成、4 个失败。失败会触发 `repair` 与**独立复验**(`repair-1 → review-2 → repair-2 → review-3`),直到通过或被如实判为需修订 —— 这就是「返工不收敛」的硬门禁在真实运行里的样子。</sub>
|
|
97
117
|
|
|
98
|
-

|
|
99
119
|
|
|
100
120
|
<sub>图 4:**门禁违规**。看顶部那条横幅 —— 违规项与拒绝理由(例如"SPEC.md 的边界章节已进入 `implement` 但仍无任何一行填写")由 `lib/interception.js` 挂在宿主 `tools/post-execute` 上当场判出后推出,不是提示词提醒。</sub>
|
|
101
121
|
|
|
102
|
-

|
|
103
123
|
|
|
104
124
|
<sub>图 5:**角色编制**。看成员列表 —— 谁在跑、用哪个模型、当前在做什么;展开任一成员可看它的任务与产物。模型可按角色分别配置,异构模型用于交叉验证。</sub>
|
|
105
125
|
|
|
106
|
-

|
|
107
127
|
|
|
108
128
|
<sub>图 6:**阶段与工件**。看阶段条与预览区 —— 当前阶段、已过阶段、以及该阶段真正写下的工件正文(工件是唯一真源,浮层只是它的视图)。</sub>
|
|
109
129
|
|
|
110
|
-

|
|
111
131
|
|
|
112
132
|
<sub>图 7:**设置**。看官方 `设置 →「专家团」` 这一页 —— 18 个设置项、中文标签、**改动即保存并即时生效**(上限/轮次/档位门/振荡检测在进程内重算);值存在宿主命名空间 `expert-team`,随插件市场的备份/恢复一起走。</sub>
|
|
113
133
|
|
|
@@ -131,7 +151,7 @@ $ /team 做一个带登录的支付模块
|
|
|
131
151
|
还会堵住整个 `dsh web` 的事件循环。1.3.5 改成只查表(实测 0.0026 ms/次、零次 `readSession`),
|
|
132
152
|
**修复后的端到端数字待实机复测**;`state-perf-guard.test.mjs` 守着它不许回退。
|
|
133
153
|
|
|
134
|
-
## 安装
|
|
154
|
+
## 安装 / Installation
|
|
135
155
|
|
|
136
156
|
**要求**
|
|
137
157
|
|
|
@@ -190,7 +210,7 @@ pnpm install && dsh web
|
|
|
190
210
|
**排障**:预设丢了、或被同名预设占住 —— **重启一次 `dsh web` 即自愈**(插件加载会重铺),也可跑一次 `/team <任务>`。
|
|
191
211
|
细节见下面「排障」一节,其中包含那条最容易踩的坑:**不要**用 `expert-team` 这个 id 去「创建 preset」。
|
|
192
212
|
|
|
193
|
-
## 依赖与推荐插件
|
|
213
|
+
## 依赖与推荐插件 / Dependencies and recommended plugins
|
|
194
214
|
|
|
195
215
|
**必需**:无。本插件**零运行时依赖**(`package.json` 无 `dependencies` 字段;`lib/` 只 import 同目录文件与 Node 内建,
|
|
196
216
|
`client.js` 只 `require('react')`,由宿主提供),只要求宿主 `DeepSeek Harness ≥ 0.1.5-rc.1`(web profile)。
|
|
@@ -229,28 +249,80 @@ dsh plugin --profile web add dshmarket
|
|
|
229
249
|
> 装了 `dsh-browser` / `dsh-mcp-connector` / `@xmanrui/dsh-im` 之后,活动流里出现的就是它们真实的工具名与参数:
|
|
230
250
|
> **纯显示,装了更清楚,不装不影响团队功能。**
|
|
231
251
|
|
|
232
|
-
## 快速上手
|
|
233
|
-
|
|
234
|
-
```
|
|
235
|
-
/team 做一个带登录的支付模块 # 一句话组队(一次性,自动组队并交付)
|
|
236
|
-
/team --persist 重构订单模块 # 持久化活团队:成员可反复指挥、跨会话恢复
|
|
237
|
-
/team --one-shot 跑一个小活 # 反向覆盖:即使默认设了持久化,这次也只跑一次
|
|
238
|
-
/team --no-code 评审现有 API 设计 # 只产出计划/评审/测试工件,不改代码
|
|
239
|
-
/team --code 直接改 # 反向覆盖:即使默认设了"只出工件",这次也动代码
|
|
240
|
-
/team --confirm 大改版需求 # 先建 run、不自动派工,浮层点「执行」才开工
|
|
241
|
-
/team uninstall # 回收本插件铺到 $DSH_HOME 的副本(skill 默认走运行时注册,本就不落地)
|
|
242
|
-
/team status # 所有 run 的阶段、成员、模型计划、实时违规
|
|
243
|
-
/team resume <run-id> # 跨会话恢复
|
|
244
|
-
```
|
|
252
|
+
## 快速上手 / Quick start
|
|
245
253
|
|
|
246
|
-
|
|
247
|
-
|
|
254
|
+
| 命令 | 作用 |
|
|
255
|
+
|---|---|
|
|
256
|
+
| `/team <一句话目标>` | 一句话组队(一次性,自动组队并交付) |
|
|
257
|
+
| `/team --persist <任务>` | 持久化活团队:成员可反复指挥、跨会话恢复 |
|
|
258
|
+
| `/team --one-shot <任务>` | 反向覆盖:即使默认设了持久化,这次也只跑一次 |
|
|
259
|
+
| `/team --no-code <任务>` | 只产出计划/评审/测试工件,不改代码 |
|
|
260
|
+
| `/team --code <任务>` | 反向覆盖:即使默认"只出工件",这次也动代码 |
|
|
261
|
+
| `/team --confirm <任务>` | 先建 run、不自动派工,浮层点「执行」才开工 |
|
|
262
|
+
| `/team --tier <档位> <任务>` | 指定流程档位(快速档 / 标准档 / 严格档) |
|
|
263
|
+
| `/team status` | 所有 run 的阶段、成员、模型计划、实时违规 |
|
|
264
|
+
| `/team models [<run>]` | 每个角色的成本 / 模型计划 |
|
|
265
|
+
| `/team canvas [<run>]` | 生成可视化团队画布(HTML);`--watch` 实时刷新 |
|
|
266
|
+
| `/team learn` | 聚合日志 → `METRICS.md` + 蒸馏经验到 `LEARNINGS.md` |
|
|
267
|
+
| `/team wait [<run>]` | 查看在飞任务进展(不阻塞) |
|
|
268
|
+
| `/team resume <run-id>` | 跨会话恢复 |
|
|
269
|
+
| `/team uninstall` | 回收本插件铺到 `$DSH_HOME` 的副本(skill 走运行时注册,本就不落地) |
|
|
270
|
+
|
|
271
|
+
完整命令(`/team codeindex` 代码索引、`/team limit` 配额、`/team settle` 冷启动清算……)见 `/team help`。
|
|
248
272
|
|
|
249
273
|
**产物落在哪**
|
|
250
274
|
|
|
251
275
|
- `<你的工作区>/team/<run-id>/` —— `SPEC / PLAN / TASKS / ROSTER / STATE / REVIEW / TEST / SUMMARY / RUN.log.md` 等工件
|
|
252
276
|
- `$DSH_HOME/expert-team/` —— 本机偏好与跨项目经验:`settings.json`、`session-runs.json`、`LEARNINGS.md`
|
|
253
277
|
|
|
278
|
+
## 常见问题 / FAQ
|
|
279
|
+
|
|
280
|
+
**它到底是什么?** 一个装在本机 `dsh` 上的插件:`/team <一句话目标>` 会拉起一支 12 角色的 subagent 团队(产品 / 架构 / 调研 / UI / 前后端 / 数据 / 安全 / 评审 / 测试 / 运维 / 文档),按 9 阶段门控流程在你的工作区里交付,并把过程写成可复核的工件。
|
|
281
|
+
|
|
282
|
+
**和"直接让一个 agent 硬做"有什么区别?** 针对三个固定失败模式:**上下文漂移**(阶段与工件双通道交接)、**自己批自己**(评审/测试是独立角色,`qa`/`reviewer` 裁决才算过)、**返工不收敛**(超轮次与未闭环被硬门禁拦下并如实报错)。详见[为什么不是「一个 agent 硬做」](#为什么不是一个-agent-硬做)。
|
|
283
|
+
|
|
284
|
+
**必须再装别的插件吗?** **不必**。本插件零运行时依赖;Hindsight(跨项目记忆)是**推荐**、`dsh-cost-meter`(费用视图)是**可选**,不装也能跑完整个流程 —— 见[依赖与推荐插件](#依赖与推荐插件--dependencies-and-recommended-plugins)。
|
|
285
|
+
|
|
286
|
+
**支持哪些 dsh 版本?** `engines.dsh: >=0.1.5-rc.1`(开发与验证基线 0.1.5-rc.1);更早版本未经测试。Node.js ≥ 20。
|
|
287
|
+
|
|
288
|
+
**数据放在哪?** 工件在你的工作区 `<workspace>/team/<run-id>/`;本机偏好与跨项目经验在 `$DSH_HOME/expert-team/`(`settings.json` / `LEARNINGS.md` / `session-runs.json`)。
|
|
289
|
+
|
|
290
|
+
**怎么卸载?** `/team uninstall` 回收它铺到 `$DSH_HOME` 的副本(skill 走运行时注册、本就不落地),再从命令行或插件市场移除插件。**注意**:`$DSH_HOME/expert-team/` 下的 `LEARNINGS.md` 等是**你的数据**,卸载不会删。
|
|
291
|
+
|
|
292
|
+
**会自己联网吗?** 不会主动联网:它只调用宿主提供的工具(文件、shell、子代理);能不能联网取决于你给会话的工具面。
|
|
293
|
+
|
|
294
|
+
**支持哪些模型?** 由宿主决定;本插件支持**按角色分别配置模型**(浮层里能看到每个成员用哪个模型),异构模型可用于交叉验证。
|
|
295
|
+
|
|
296
|
+
**不切「专家团模式」preset 也能用吗?** 能。`/team` 是 host 平面命令,任何预设下都能跑;此时退回通用 `subagent`(角色人设写进 prompt),少的是配置层的边界保证(`toolFilter` / `maxDepth: 1`)。
|
|
297
|
+
|
|
298
|
+
**浮层/画布打开很慢?** 见[排障](#排障) —— 1.3.5 起 `/state` 不再逐条全量读子会话日志;升级到 ≥ 1.3.5 后重启 `dsh web` 即可。
|
|
299
|
+
|
|
300
|
+
## 术语 / Glossary
|
|
301
|
+
|
|
302
|
+
**角色(12)**
|
|
303
|
+
|
|
304
|
+
| 中文 | English | 代码标识 |
|
|
305
|
+
|---|---|---|
|
|
306
|
+
| 产品 | Product | `pm` |
|
|
307
|
+
| 架构 | Architect | `architect` |
|
|
308
|
+
| 调研 | Researcher | `researcher` |
|
|
309
|
+
| 界面设计 | UI/UX | `ui` |
|
|
310
|
+
| 后端 | Backend | `backend` |
|
|
311
|
+
| 前端 | Frontend | `frontend` |
|
|
312
|
+
| 数据 | DBA | `dba` |
|
|
313
|
+
| 安全审计 | Security | `sec` |
|
|
314
|
+
| 评审 | Reviewer | `reviewer` |
|
|
315
|
+
| 测试 | QA | `qa` |
|
|
316
|
+
| 运维 | DevOps | `devops` |
|
|
317
|
+
| 文档 | Docs | `docs` |
|
|
318
|
+
|
|
319
|
+
**阶段(9)**:`clarify` 澄清 → `research` 调研 → `design` 设计 → `spec-review` 规格评审(**硬门**)→ `方案确认` 方案确认(**确认门**,默认开、可关)→ `implement` 实现 → `review` 审查 → `test` 测试 → `deliver` 交付。(id 与中文名的唯一真源是 `lib/vocab.js`。)
|
|
320
|
+
|
|
321
|
+
**主要工件**:`SPEC.md`(规格与边界)· `RESEARCH.md`(调研)· `PLAN.md`(方案)· `TASKS.json`(任务台账)· `ROSTER.json`(编制)· `STATE.json`(状态)· `AUTHORITY.md`(写入权限单源)· `REVIEW.md`(评审)· `TEST.md`(测试)· `SUMMARY.md`(交付总结)· `METRICS.md`(成本与耗时:**`team/` 根的聚合快照**,`/team learn` 产出)· `RUN.log.md`(运行日志)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
**谁写**:每个工件由**产出它的角色**自己 `write` 进 `<run-dir>/`;lead **只读工件做门控与裁决**(它没有 `write` 工具)。`STATE.json` 与 `RUN.log.md` 由**运行时**维护。
|
|
325
|
+
|
|
254
326
|
## 插件结构
|
|
255
327
|
|
|
256
328
|
```
|
|
@@ -299,7 +371,7 @@ dsh 安装里插件自带的 Config schema(dsh 路径自动探测,可用 `DS
|
|
|
299
371
|
**浮层打开很慢 / 整个 `dsh web` 发卡?** 1.3.5 之前 `/state` 会逐条全量读子会话日志(实测热态 7–10 s、
|
|
300
372
|
冷态 283 s),并堵住事件循环。升级到 ≥ 1.3.5 后重启 `dsh web` 即可。
|
|
301
373
|
|
|
302
|
-
##
|
|
374
|
+
## 自定义预设 / Custom presets(想改专家团默认行为时)
|
|
303
375
|
|
|
304
376
|
- **创建**:`设置 → Agent 预设 → 用「创造模式」创作自定义预设`(其机制是"复制一份既有预设",产出落在 `$DSH_HOME/.agent-presets/<id>/`)。
|
|
305
377
|
- **要定制专家团,请以「专家团模式」为源、换一个你自己的 id**(例如 `my-team`):复制出来的目录天然带上 12 个角色工具与它的 skill 目录,
|
package/client.js
CHANGED
|
@@ -1343,10 +1343,13 @@ window.__ModuleLoader__.load({
|
|
|
1343
1343
|
var planMsgS = useState(''); var planMsg = planMsgS[0], setPlanMsg = planMsgS[1]
|
|
1344
1344
|
var err = useState(''); var errV = err[0], setErr = err[1]
|
|
1345
1345
|
|
|
1346
|
-
function stateUrl() {
|
|
1346
|
+
function stateUrl(section) {
|
|
1347
1347
|
var url = '/plugins/dsh-expert-team/state', q = []
|
|
1348
1348
|
if (sessionId) q.push('sessionId=' + encodeURIComponent(sessionId))
|
|
1349
1349
|
if (selRunV && selRunV.workspace && selRunV.runId) { q.push('workspace=' + encodeURIComponent(selRunV.workspace)); q.push('run=' + encodeURIComponent(selRunV.runId)) }
|
|
1350
|
+
// `section`:渐进式状态(2026-09-15 性能修复 #3)。首屏只发 `summary`(便宜),
|
|
1351
|
+
// 人/料/事件流晚一拍、低频拉(见下面的双订阅)。不传 = 完整负载(服务端向后兼容)。
|
|
1352
|
+
if (section) q.push('section=' + encodeURIComponent(section))
|
|
1350
1353
|
return url + (q.length ? ('?' + q.join('&')) : '')
|
|
1351
1354
|
}
|
|
1352
1355
|
// ── 数据到达后的副作用(**本组件不再自带轮询器**;由 stateHub 统一驱动)─────────
|
|
@@ -1354,9 +1357,22 @@ window.__ModuleLoader__.load({
|
|
|
1354
1357
|
// 画布另走 liveStore 的 setInterval ⇒ 画布一开就有 2–3 个轮询并发压同一个重端点
|
|
1355
1358
|
// (实测 10 秒 7 发、6 次重叠、单发被拖到 5.9/8.3 s)。现在全部经 stateHub:
|
|
1356
1359
|
// 同 URL 同刻只跑一次、全局单一时钟、统一退避(见 stateHub 的注释)。
|
|
1360
|
+
// ── 分节负载的合并(2026-09-15 性能修复 #3)────────────────────────────────
|
|
1361
|
+
// 为什么必须合并而不是替换:首屏只发 `?section=summary`(便宜:阶段/进度/计数),
|
|
1362
|
+
// "人/料/事件流"晚一拍才到。若直接 `setData(d)`,摘要那一拍会把上一拍已知的成员/事件流
|
|
1363
|
+
// **抹掉**(UI 闪空、通知误判"成员消失")。规则:**新负载里有的键覆盖旧值,没有的键保留**
|
|
1364
|
+
// —— 缺块 ≠ 空数据;`sections` 字段如实告诉 UI"这份负载包含哪些块"(两种零可区分)。
|
|
1365
|
+
function mergeStatePayload(prev, d) {
|
|
1366
|
+
if (!d || typeof d !== 'object') return d
|
|
1367
|
+
if (!prev || typeof prev !== 'object' || prev.ok !== true) return d
|
|
1368
|
+
var out = {}, k
|
|
1369
|
+
for (k in prev) { if (Object.prototype.hasOwnProperty.call(prev, k)) out[k] = prev[k] }
|
|
1370
|
+
for (k in d) { if (Object.prototype.hasOwnProperty.call(d, k)) out[k] = d[k] }
|
|
1371
|
+
return out
|
|
1372
|
+
}
|
|
1357
1373
|
function onState(d) {
|
|
1358
1374
|
if (d && d.ok) {
|
|
1359
|
-
setData(d); setErr(''); reschedule(d)
|
|
1375
|
+
setData(function (prev) { return mergeStatePayload(prev, d) }); setErr(''); reschedule(d)
|
|
1360
1376
|
// N2: keep the composer takeover in sync with pendingDecision
|
|
1361
1377
|
// 批 0-2:把 /state 已经返回的 runId/workspace 一并传出,供横幅拍板时回传给 /decide
|
|
1362
1378
|
try { if (exports._syncPending) exports._syncPending(d.pendingDecision, sessionId, d.runId, d.workspace) } catch (e) {}
|
|
@@ -1391,13 +1407,18 @@ window.__ModuleLoader__.load({
|
|
|
1391
1407
|
}
|
|
1392
1408
|
useEffect(function () {
|
|
1393
1409
|
if (!isOpen && !viewMode) return undefined // panel closed & not canvas → no polling
|
|
1394
|
-
|
|
1395
|
-
//
|
|
1396
|
-
|
|
1397
|
-
|
|
1410
|
+
// **双订阅**(性能修复 #3):`summary` 便宜(阶段/进度/计数)⇒ 按 `pollMs` 快拉,
|
|
1411
|
+
// 首屏立刻有内容;`people,feed,artifacts` 贵(成员解析/事件流/日志尾+git)⇒ 至少 6 秒一次,
|
|
1412
|
+
// 到达后由 `mergeStatePayload` 合并进同一份 data(**不会**把摘要拍掉的字段抹掉)。
|
|
1413
|
+
// stateHub 按 URL 各自计时(per-URL due),所以"一快一慢"不会互相拖。
|
|
1414
|
+
var off = stateHubSubscribe(stateUrl('summary'), dispCfg.pollMs, onState)
|
|
1415
|
+
var offDetail = stateHubSubscribe(stateUrl('people,feed,artifacts'), Math.max(dispCfg.pollMs, 6000), onState)
|
|
1416
|
+
// catch up instantly when the user returns to the tab / window(只补便宜那一份)
|
|
1417
|
+
var onVis = function () { if (document.visibilityState === 'visible') stateHubFetch(stateUrl('summary'), onState) }
|
|
1418
|
+
var onFocus = function () { stateHubFetch(stateUrl('summary'), onState) }
|
|
1398
1419
|
document.addEventListener('visibilitychange', onVis)
|
|
1399
1420
|
window.addEventListener('focus', onFocus)
|
|
1400
|
-
return function () { off(); document.removeEventListener('visibilitychange', onVis); window.removeEventListener('focus', onFocus) }
|
|
1421
|
+
return function () { off(); offDetail(); document.removeEventListener('visibilitychange', onVis); window.removeEventListener('focus', onFocus) }
|
|
1401
1422
|
}, [sessionId, selRunV, isOpen, viewMode, dispCfg.pollMs])
|
|
1402
1423
|
|
|
1403
1424
|
// Make the docked panel reserve space (shift the shell frame) instead of
|
|
@@ -2350,23 +2371,32 @@ window.__ModuleLoader__.load({
|
|
|
2350
2371
|
// ③ 间隔 = clamp(max(基础间隔, 上次耗时×2), 基础间隔, 30000);
|
|
2351
2372
|
// 团队有人在跑时基础间隔按 40% 加速(面板原有意图,现在对徽章/画布同样生效)。
|
|
2352
2373
|
// 纪律不变:**未回绝不发下一个**(晚一点看到状态,好过把 web 拖死)。
|
|
2353
|
-
var stateHub = { inflight: {}, lastMs: {}, subs: [], timer: 0, busy: false }
|
|
2374
|
+
var stateHub = { inflight: {}, lastMs: {}, lastAt: {}, subs: [], timer: 0, busy: false }
|
|
2354
2375
|
function stateHubNow() { return (window.performance && performance.now) ? performance.now() : Date.now() }
|
|
2355
2376
|
function stateHubUrls() { var out = []; stateHub.subs.forEach(function (x) { if (out.indexOf(x.url) < 0) out.push(x.url) }) ; return out }
|
|
2356
|
-
|
|
2377
|
+
// 每个 URL 的**自己的**基础间隔(同一 URL 有多个订阅者时取最小 ⇒ 谁最急听谁的)。
|
|
2378
|
+
// 性能修复 #3 的"一快一慢"就靠它:`summary` 3 s 一发、`people,feed,artifacts` ≥ 6 s 一发,
|
|
2379
|
+
// 两者互不拖拽(旧实现是"全局取最小 base、每个 tick 把所有 URL 都拉一遍" ⇒ 慢的那份被快钟拖着跑)。
|
|
2380
|
+
function stateHubBaseOf(u) {
|
|
2357
2381
|
var base = 0
|
|
2358
|
-
stateHub.subs.forEach(function (x) { if (!base || x.base < base) base = x.base })
|
|
2382
|
+
stateHub.subs.forEach(function (x) { if (x.url === u && (!base || x.base < base)) base = x.base })
|
|
2359
2383
|
if (!base) base = 3000
|
|
2360
2384
|
return stateHub.busy ? Math.max(300, Math.round(base * 0.4)) : base
|
|
2361
2385
|
}
|
|
2386
|
+
function stateHubDueAt(u) { return (stateHub.lastAt[u] || 0) + stateHubBaseOf(u) }
|
|
2362
2387
|
function stateHubSchedule() {
|
|
2363
2388
|
clearInterval(stateHub.timer)
|
|
2364
2389
|
stateHub.timer = 0
|
|
2365
2390
|
if (!stateHub.subs.length) return
|
|
2366
2391
|
var slowest = 0
|
|
2367
2392
|
stateHubUrls().forEach(function (u) { if ((stateHub.lastMs[u] || 0) > slowest) slowest = stateHub.lastMs[u] })
|
|
2368
|
-
var
|
|
2369
|
-
var
|
|
2393
|
+
var now = stateHubNow(), earliest = Infinity
|
|
2394
|
+
stateHubUrls().forEach(function (u) { var at = stateHubDueAt(u); if (at < earliest) earliest = at })
|
|
2395
|
+
if (!isFinite(earliest)) earliest = now + 3000
|
|
2396
|
+
// 基础等待 = 最早到期的那一刻;再按"最慢一次请求 ×2"放大(未回绝不发下一个的纪律仍由
|
|
2397
|
+
// in-flight 守卫保证,这里的放大只是为了别把慢端点压成队列)。
|
|
2398
|
+
var wait = Math.max(200, Math.round(earliest - now))
|
|
2399
|
+
var next = Math.max(wait, Math.min(30000, Math.round(slowest * 2)))
|
|
2370
2400
|
stateHub.timer = setInterval(stateHubTick, next)
|
|
2371
2401
|
}
|
|
2372
2402
|
function stateHubDeliver(url) {
|
|
@@ -2380,6 +2410,8 @@ window.__ModuleLoader__.load({
|
|
|
2380
2410
|
var p = stateHub.inflight[url]
|
|
2381
2411
|
if (p) { p.then(function (d) { try { onData(d) } catch (e) {} }); return } // 同刻同 URL:复用在飞结果
|
|
2382
2412
|
var t0 = stateHubNow()
|
|
2413
|
+
// 记"上次发出时刻":即使**失败**也记(否则失败会变成热循环重试)。
|
|
2414
|
+
stateHub.lastAt[url] = t0
|
|
2383
2415
|
p = fetch(url).then(function (r) { return r.ok ? r.json() : null }).then(function (d) {
|
|
2384
2416
|
delete stateHub.inflight[url]
|
|
2385
2417
|
stateHub.lastMs[url] = Math.max(0, stateHubNow() - t0)
|
|
@@ -2390,7 +2422,9 @@ window.__ModuleLoader__.load({
|
|
|
2390
2422
|
}
|
|
2391
2423
|
function stateHubTick() {
|
|
2392
2424
|
if (!stateHub.subs.length) { clearInterval(stateHub.timer); stateHub.timer = 0; return }
|
|
2393
|
-
|
|
2425
|
+
var now = stateHubNow()
|
|
2426
|
+
// **只发改到期的那些 URL**(不是每个 tick 把所有 URL 拉一遍)
|
|
2427
|
+
stateHubUrls().forEach(function (u) { if (stateHubDueAt(u) <= now && !stateHub.inflight[u]) stateHubFetch(u, stateHubDeliver(u)) })
|
|
2394
2428
|
stateHubSchedule()
|
|
2395
2429
|
}
|
|
2396
2430
|
function stateHubSubscribe(url, base, onData) {
|
|
@@ -2409,7 +2443,7 @@ window.__ModuleLoader__.load({
|
|
|
2409
2443
|
// ── 共享 state 轮询(HeaderButton / LiveCapsule 复用,单例;无订阅者即停)──
|
|
2410
2444
|
var liveStore = { data: null, sid: '', off: null, subs: new Set() }
|
|
2411
2445
|
var LIVE_BASE_MS = 2500 // 徽章/画布的基础节奏(与面板的 dispCfg.pollMs 取更小者由 hub 统一决定)
|
|
2412
|
-
function liveUrl(sid) { return '/plugins/dsh-expert-team/state?sessionId=' + encodeURIComponent(sid) }
|
|
2446
|
+
function liveUrl(sid) { return '/plugins/dsh-expert-team/state?sessionId=' + encodeURIComponent(sid) + '§ion=people,feed' }
|
|
2413
2447
|
// 手动催一次(子代理刚出现时立刻刷新一次徽章,不必等下一个 tick)
|
|
2414
2448
|
function liveTick() {
|
|
2415
2449
|
var sid = liveStore.sid
|