dsh-subagent-profile 0.3.2 → 0.3.3
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/README.md +38 -29
- package/README.zh.md +38 -29
- package/index.mjs +188 -62
- package/lib/client.js +1240 -47
- package/lib/core/catalog-cache.mjs +45 -7
- package/lib/core/catalog.mjs +6 -6
- package/lib/core/cost-guard.mjs +71 -44
- package/lib/core/decision-trace.mjs +433 -0
- package/lib/core/delegation.mjs +106 -48
- package/lib/core/dispatch-gates.mjs +146 -0
- package/lib/core/dispatch-guard.mjs +150 -0
- package/lib/core/dispatch-schema.mjs +98 -14
- package/lib/core/dispatch-tool.mjs +208 -199
- package/lib/core/escape.mjs +130 -0
- package/lib/core/evolution-ledger.mjs +289 -0
- package/lib/core/evolution-summary.mjs +432 -0
- package/lib/core/http-routes.mjs +155 -40
- package/lib/core/intersection.mjs +6 -9
- package/lib/core/presets-sync.mjs +256 -136
- package/lib/core/profile-provider.mjs +41 -39
- package/lib/core/profiles-store.mjs +103 -76
- package/lib/core/pure.mjs +110 -66
- package/lib/core/shims.mjs +56 -75
- package/lib/core/whitelist.mjs +23 -17
- package/package.json +2 -3
- package/presets/orchestrator/agent.cordis.yml +243 -271
- package/presets/orchestrator/NOTICE +0 -3
package/README.md
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
<div align="center">
|
|
5
5
|
<b style="font-size: 1.15em;">Subagent dispatch, profiled — the right agent for the right task (preset / model / reasoning effort)</b><br /><br />
|
|
6
6
|
<img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg" />
|
|
7
|
-
<img alt="Version: v0.3.2" src="https://img.shields.io/badge/Version-v0.3.2-blue.svg" />
|
|
8
7
|
<img alt="npm" src="https://img.shields.io/npm/v/dsh-subagent-profile.svg" />
|
|
9
|
-
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.1.0--rc.6%20~%200.
|
|
8
|
+
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.1.0--rc.6%20~%200.2.0-blue.svg" />
|
|
10
9
|
</div>
|
|
11
10
|
|
|
12
11
|
<div align="center">English · <a href="README.zh.md">中文</a></div>
|
|
@@ -15,7 +14,7 @@ For [DeepSeek Harness](https://github.com/deepseek-ai/dsh) (DSH).
|
|
|
15
14
|
|
|
16
15
|
> *Thinking, Fast and Slow*: System 1 is fast and cheap, System 2 is slow and careful. The built-in `subagent` gives every subtask the same brain as its parent — no way to tell them apart. `dsh-subagent-profile` lets you pick per subtask: research with a fast brain, deep work with a careful one, saved as named **profiles**.
|
|
17
16
|
|
|
18
|
-
## Why
|
|
17
|
+
## Why this plugin
|
|
19
18
|
|
|
20
19
|
| | Built-in `subagent` | `dsh-subagent-profile` |
|
|
21
20
|
|---|---|---|
|
|
@@ -25,13 +24,11 @@ For [DeepSeek Harness](https://github.com/deepseek-ai/dsh) (DSH).
|
|
|
25
24
|
| Cost guardrails | ❌ | ✅ model / effort / tokens / depth capped |
|
|
26
25
|
| GUI management | ❌ | ✅ settings page |
|
|
27
26
|
|
|
28
|
-
## What it solves
|
|
29
|
-
|
|
30
27
|
- **Per-subtask control over the child's brain.** `dispatch` sets, per subtask: which preset (composition), which model, which reasoning effort, which tools, and the token cap. A research subtask and a coding subtask can run with completely different setups — something the plain `subagent` tool can't do (it only inherits the parent).
|
|
31
28
|
- **Named, reusable profiles.** A profile is one bundle of preset + model + reasoning effort + tool scope + persona. Save "research" as `researcher` (reasoning off, search-only tools) and dispatch with `dispatch(profile="researcher")`; two built-ins ship (`swap-standard` = full standard coding toolkit, `researcher`), and you can add/edit/remove your own in the settings page.
|
|
32
29
|
- **Fully observable.** Every result reports the effective profile / preset / model / reasoning effort; logs are tagged `[dsh-subagent-profile]`.
|
|
33
30
|
|
|
34
|
-
##
|
|
31
|
+
## Installation
|
|
35
32
|
|
|
36
33
|
```bash
|
|
37
34
|
dsh plugin --profile web add dsh-subagent-profile # published package
|
|
@@ -44,7 +41,14 @@ Restart `dsh web`. This is a standard **bundle plugin**: it provides the `dispat
|
|
|
44
41
|
|
|
45
42
|
### 1. Configure sub-agent profiles
|
|
46
43
|
|
|
47
|
-
Profiles are managed in the settings page — each one bundles preset + model + reasoning effort + tool scope (and optionally a persona), and can be enabled, disabled, edited, or reset individually.
|
|
44
|
+
Profiles are managed in the settings page — each one bundles preset + model + reasoning effort + tool scope (and optionally a persona), and can be enabled, disabled, edited, or reset individually. Two built-ins ship:
|
|
45
|
+
|
|
46
|
+
| Profile | Purpose |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `swap-standard` | switch the child to the full standard coding toolkit |
|
|
49
|
+
| `researcher` | deep reasoning off, search-only tools |
|
|
50
|
+
|
|
51
|
+
Profiles live in `~/.dsh/subagent-profiles.json` and take effect immediately (edits are made from the settings page).
|
|
48
52
|
|
|
49
53
|

|
|
50
54
|
|
|
@@ -62,15 +66,6 @@ dispatch(
|
|
|
62
66
|
|
|
63
67
|

|
|
64
68
|
|
|
65
|
-
## Profiles
|
|
66
|
-
|
|
67
|
-
Profiles live in `~/.dsh/subagent-profiles.json` and take effect immediately (edits are made from the settings page).
|
|
68
|
-
|
|
69
|
-
| Profile | Purpose |
|
|
70
|
-
|---|---|
|
|
71
|
-
| `swap-standard` | switch the child to the full standard coding toolkit |
|
|
72
|
-
| `researcher` | deep reasoning off, search-only tools |
|
|
73
|
-
|
|
74
69
|
## Safety model
|
|
75
70
|
|
|
76
71
|
Delegation never lets a subagent gain more power than you already have — this is the default, with no configuration:
|
|
@@ -83,13 +78,14 @@ Delegation never lets a subagent gain more power than you already have — this
|
|
|
83
78
|
|
|
84
79
|
- `~/.dsh/subagent-profiles.json` — the profile registry (edited from the settings page).
|
|
85
80
|
- `~/.dsh/subagent-profiles.state.json` — the plugin's enable/disable switch (default enabled).
|
|
81
|
+
- `~/.dsh/subagent-profiles.failed-traces.json` — the failure ledger (dispatch failure traces).
|
|
86
82
|
- `~/.dsh/.agent-presets/orchestrator/` — the self-installed `orchestrator` agent preset (synced from the bundled `presets/orchestrator/` on every startup).
|
|
87
83
|
|
|
88
|
-
`DSH_HOME` is respected and defaults to `~/.dsh`.
|
|
84
|
+
`DSH_HOME` is respected and defaults to `~/.dsh`. Uninstalling the plugin removes the three data files above and the self-installed `orchestrator` preset directory (other plugins' presets are left untouched); re-installing or re-launching re-syncs the preset and regenerates the data files.
|
|
89
85
|
|
|
90
86
|
## Known limitations
|
|
91
87
|
|
|
92
|
-
- **Background** one-shot dispatch requires `@deepseek-ai/dsh-jobs` and `@deepseek-ai/dsh-tool-jobs` to be loaded; otherwise it fails with "
|
|
88
|
+
- **Background** one-shot dispatch requires `@deepseek-ai/dsh-jobs` and `@deepseek-ai/dsh-tool-jobs` to be loaded; otherwise it fails with "dispatch: 后台派发不可用:缺少 jobs 服务".
|
|
93
89
|
- **Continuable** mode goes through the DSH standard composition path, so the `preset` swap and `reasoningEffort` are ignored (the child inherits the parent preset at the default reasoning effort).
|
|
94
90
|
- **Continuable** tool gate is a plugin-side mitigation: the child's `allow` is pre-computed as a closed set — parent tool set − `run_code` − `deny`, then intersected with `allow`. **Assumption:** continuable inherits the parent preset, so the child's tool set ≈ the parent's. **Failure condition:** any host behavior change that makes the child's tool set differ from the parent's (not only preset swap — e.g. a future preset swap, composing a different tool set) means the parent set can contain tools the child does not have, so `tools.restrict` throws "unknown tool" and this mitigation automatically degrades to fail-loud (conservatively safe); it must then be replaced with a true parent ∩ child intersection once the upstream provides a provider guard seam.
|
|
95
91
|
|
|
@@ -103,22 +99,40 @@ dsh-subagent-profile/
|
|
|
103
99
|
│ └── core/ # host-side modules (lightweight layering)
|
|
104
100
|
│ ├── pure.mjs # dependency-free pure functions (sanitize / prune / guard math — unit-tested)
|
|
105
101
|
│ ├── shims.mjs # the single @deepseek-ai import facade (guards fail loud, helpers degrade softly)
|
|
106
|
-
│
|
|
107
|
-
│
|
|
102
|
+
│ ├── catalog.mjs # tool-name → zh / category tables (zero-dep)
|
|
103
|
+
│ ├── catalog-cache.mjs # process-shared catalog snapshot (models / presets / tools, TTL cache)
|
|
104
|
+
│ ├── cost-guard.mjs # runtime capability checks (provider / model / reasoning effort)
|
|
105
|
+
│ ├── decision-trace.mjs # decision trace (gates / effective / settled) + failure ledger
|
|
106
|
+
│ ├── delegation.mjs # background one-shot settling + child usage collection
|
|
107
|
+
│ ├── dispatch-gates.mjs # pre-dispatch gates (whitelist / cost / intersection / budget)
|
|
108
|
+
│ ├── dispatch-guard.mjs # concurrency + per-parent token budget guard
|
|
109
|
+
│ ├── dispatch-schema.mjs # dispatch tool input/output schema declarations
|
|
110
|
+
│ ├── dispatch-tool.mjs # dispatch tool factory (defineTool + execute + syncTool)
|
|
111
|
+
│ ├── escape.mjs # escape-hatch allow store
|
|
112
|
+
│ ├── evolution-ledger.mjs # dispatch ledger (jsonl) + governance audit
|
|
113
|
+
│ ├── evolution-summary.mjs # T1 aggregate summaries + advice text
|
|
114
|
+
│ ├── http-routes.mjs # settings loopback HTTP routes
|
|
115
|
+
│ ├── intersection.mjs # tool-intersection pure core
|
|
116
|
+
│ ├── presets-sync.mjs # bundled preset self-install (hash-gated sync)
|
|
117
|
+
│ ├── profile-provider.mjs # the `profile` subagent provider
|
|
118
|
+
│ ├── profiles-store.mjs # profile registry store + switch persistence
|
|
119
|
+
│ └── whitelist.mjs # system-trust preset whitelist
|
|
108
120
|
├── presets/orchestrator/ # bundled "orchestrator" agent preset (self-installed, synced on every startup)
|
|
109
121
|
├── cordis.patch.yml # bundle patch: inserts the plugin row into the host composition
|
|
110
122
|
├── .gitea/workflows/ci.yml # bare-CI (Gitea Actions; needs an Act runner on the server)
|
|
111
123
|
├── package.json # metadata, files whitelist, exports (test / test:bare / preflight scripts)
|
|
112
124
|
├── scripts/
|
|
113
|
-
│ ├──
|
|
114
|
-
│ └──
|
|
125
|
+
│ ├── preflight.mjs # preflight: preset-tree reconciliation + no hardcoded version badge (zero-dep)
|
|
126
|
+
│ └── leak-scan.mjs # public-release gate: scans all history + worktree for sensitive patterns
|
|
115
127
|
├── docs/
|
|
116
128
|
│ └── screenshots/ # README screenshots
|
|
117
|
-
├── test/ # host-side tests (node:test, zero extra deps;
|
|
129
|
+
├── test/ # host-side tests (node:test, zero extra deps; 352 cases — bare subset in CI, junction local)
|
|
118
130
|
│ ├── README.md / README.zh.md # test directory guide (EN/ZH) — two-tier split explained
|
|
119
131
|
│ ├── harness/ctx.mjs # fake Cordis ctx + ~/.dsh isolation
|
|
120
132
|
│ ├── pure / input-schema / catalog-integrity.test.mjs # bare tier (import-free, runs in bare CI)
|
|
121
|
-
│ └── characterization / facade / gating / persist / recycle /
|
|
133
|
+
│ └── *.test.mjs # junction tier (local only): characterization / facade / gating / persist / recycle /
|
|
134
|
+
│ # cost-guard / continuable-guard / decision-trace / dispatch-guard / escape-hatch /
|
|
135
|
+
│ # evolution-* / csrf / label-preset-sync / percall-spec / trust-label / audit-meta / …
|
|
122
136
|
├── README.md / README.zh.md # this document (EN/ZH)
|
|
123
137
|
└── LICENSE
|
|
124
138
|
```
|
|
@@ -127,11 +141,6 @@ dsh-subagent-profile/
|
|
|
127
141
|
|
|
128
142
|
Found a bug or have an idea? [Open an issue](https://github.com/muzyLink/dsh-subagent-profile/issues) or submit a pull request — all contributions are welcome.
|
|
129
143
|
|
|
130
|
-
### CI (bare) & preflight
|
|
131
|
-
|
|
132
|
-
- **Gitea Actions** (`.gitea/workflows/ci.yml`): on `push` / `pull_request` to `v2-midterm` / `main`, runs the **bare-CI** subset — the tests that never load `@deepseek-ai` (`npm run test:bare`: pure / input-schema / catalog-integrity), `node --check` syntax checks, eslint (installed in isolation, since the bare environment cannot install the `@deepseek-ai` devDependencies), and `npm run preflight`. **Enabling Gitea Actions requires installing an Act runner on the server** (register it with the `ubuntu-latest` label); the workflow takes effect automatically once the runner is ready. Junction-dependent tests (characterization / facade / gating / persist / recycle / cost-guard / continuable-guard) run locally only — see `test/README.md` for the two-tier split and its rationale.
|
|
133
|
-
- **Preflight** (`npm run preflight`): zero-dependency self-check — ① reconciles the bundled `presets/` tree against a fresh `$DSH_HOME/.agent-presets` derived tree (byte-identical set, both directions; never touches the real `~/.dsh`), ② asserts the README version badge (`v<version>` in the hero) matches `package.json` `version`. Non-zero exit means drift — fix and re-run before release.
|
|
134
|
-
|
|
135
144
|
If this plugin has been useful to you, please give it a ⭐ on GitHub — it helps others find it.
|
|
136
145
|
|
|
137
146
|
## Credits
|
package/README.zh.md
CHANGED
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
<div align="center">
|
|
5
5
|
<b style="font-size: 1.15em;">子 Agent 派发方案化插件 —— 用对的人(预设 / 模型 / 推理强度)干对的事</b><br /><br />
|
|
6
6
|
<img alt="License: MIT" src="https://img.shields.io/badge/License-MIT-yellow.svg" />
|
|
7
|
-
<img alt="Version: v0.3.2" src="https://img.shields.io/badge/Version-v0.3.2-blue.svg" />
|
|
8
7
|
<img alt="npm" src="https://img.shields.io/npm/v/dsh-subagent-profile.svg" />
|
|
9
|
-
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.1.0--rc.6%20~%200.
|
|
8
|
+
<img alt="DSH" src="https://img.shields.io/badge/DSH-0.1.0--rc.6%20~%200.2.0-blue.svg" />
|
|
10
9
|
</div>
|
|
11
10
|
|
|
12
11
|
<div align="center"><a href="README.md">English</a> · 中文</div>
|
|
@@ -15,7 +14,7 @@
|
|
|
15
14
|
|
|
16
15
|
> 《思考,快与慢》:系统 1 快而省,系统 2 慢而稳。内置的 `subagent` 给所有子任务同一个「大脑」,分不出快慢;`dsh-subagent-profile` 让你按任务指定——调研用快思考,攻坚用慢思考,常用搭配存成命名**方案**。
|
|
17
16
|
|
|
18
|
-
##
|
|
17
|
+
## 为什么选这个插件
|
|
19
18
|
|
|
20
19
|
| | 内置 `subagent` | `dsh-subagent-profile` |
|
|
21
20
|
|---|---|---|
|
|
@@ -25,13 +24,11 @@
|
|
|
25
24
|
| 成本护栏 | ❌ | ✅ 模型/推理强度/token/深度上限 |
|
|
26
25
|
| GUI 管理 | ❌ | ✅ 设置页 |
|
|
27
26
|
|
|
28
|
-
## 解决什么问题
|
|
29
|
-
|
|
30
27
|
- **按子任务指定子 Agent 的「大脑」。** `dispatch` 给每个子任务单独指定:用哪个预设(composition)、哪个模型、哪种推理强度、只开哪些工具、给多少 token 上限。查资料和写代码两个子任务可以用完全不同的配置——这是内置 `subagent` 做不到的(它只能让子任务继承父 Agent 的同一套配置)。
|
|
31
28
|
- **把常用搭配存成命名方案,按名调用。** 「方案」= 预设 + 模型 + 推理强度 + 工具范围 + 人设的一揽子配置。把「调研」存成 `researcher`(关深度推理、只留检索工具),以后 `dispatch(profile="researcher")` 即可;内置 `swap-standard`(切到 standard 全套编码工具)和 `researcher` 两个现成方案,也能在设置页自己增删改。
|
|
32
29
|
- **每次派发都看得见实际用了什么。** 结果里标注实际生效的方案/预设/模型/推理强度,日志带 `[dsh-subagent-profile]` 标记,方便排查。
|
|
33
30
|
|
|
34
|
-
##
|
|
31
|
+
## 安装
|
|
35
32
|
|
|
36
33
|
```bash
|
|
37
34
|
dsh plugin --profile web add dsh-subagent-profile # 发布包
|
|
@@ -44,7 +41,14 @@ dsh plugin --profile web add ./dsh-subagent-profile # 本地检出
|
|
|
44
41
|
|
|
45
42
|
### 1. 配置子 Agent 方案
|
|
46
43
|
|
|
47
|
-
在设置页管理命名方案——每个方案打包预设 + 模型 + 推理强度 +
|
|
44
|
+
在设置页管理命名方案——每个方案打包预设 + 模型 + 推理强度 + 工具范围(可选人设),可单独启用、禁用、编辑或批量重置。内置两个现成方案:
|
|
45
|
+
|
|
46
|
+
| 方案 | 用途 |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `swap-standard` | 子 Agent 切换为 standard 全套编码工具 |
|
|
49
|
+
| `researcher` | 关深度推理、只留检索工具 |
|
|
50
|
+
|
|
51
|
+
方案保存在 `~/.dsh/subagent-profiles.json`,改完立即生效(在设置页编辑)。
|
|
48
52
|
|
|
49
53
|

|
|
50
54
|
|
|
@@ -62,15 +66,6 @@ dispatch(
|
|
|
62
66
|
|
|
63
67
|

|
|
64
68
|
|
|
65
|
-
## 方案(Profiles)
|
|
66
|
-
|
|
67
|
-
方案保存在 `~/.dsh/subagent-profiles.json`,改完立即生效(在设置页编辑)。
|
|
68
|
-
|
|
69
|
-
| 方案 | 用途 |
|
|
70
|
-
|---|---|
|
|
71
|
-
| `swap-standard` | 子 Agent 切换为 standard 全套编码工具 |
|
|
72
|
-
| `researcher` | 关深度推理、只留检索工具 |
|
|
73
|
-
|
|
74
69
|
## 安全模型
|
|
75
70
|
|
|
76
71
|
委派绝不会让子 Agent 拿到比你更多的权限,默认生效、无需配置:
|
|
@@ -83,13 +78,14 @@ dispatch(
|
|
|
83
78
|
|
|
84
79
|
- `~/.dsh/subagent-profiles.json` —— 方案注册表(由设置页编辑)。
|
|
85
80
|
- `~/.dsh/subagent-profiles.state.json` —— 插件的启用/禁用开关(默认启用)。
|
|
81
|
+
- `~/.dsh/subagent-profiles.failed-traces.json` —— 失败台账(派发失败轨迹)。
|
|
86
82
|
- `~/.dsh/.agent-presets/orchestrator/` —— 自动安装的 `orchestrator` 编排者预设(每次启动由打包的 `presets/orchestrator/` 同步)。
|
|
87
83
|
|
|
88
|
-
尊重 `DSH_HOME`,默认 `~/.dsh
|
|
84
|
+
尊重 `DSH_HOME`,默认 `~/.dsh`。卸载插件会删除上述三个数据文件与自动安装的 `orchestrator` 预设目录(其它插件的预设不受影响);重新安装或重新启动会重新同步预设并重新生成数据文件。
|
|
89
85
|
|
|
90
86
|
## 已知限制
|
|
91
87
|
|
|
92
|
-
- **后台**一次性派发需要加载 `@deepseek-ai/dsh-jobs` 与 `@deepseek-ai/dsh-tool-jobs
|
|
88
|
+
- **后台**一次性派发需要加载 `@deepseek-ai/dsh-jobs` 与 `@deepseek-ai/dsh-tool-jobs`,否则报「后台派发不可用:缺少 jobs 服务」。
|
|
93
89
|
- **可续跑**模式走 DSH 标准组合路径,因此 `preset` 换用与 `reasoningEffort` 会被忽略(继承父预设、使用默认推理强度)。
|
|
94
90
|
- **可续跑**工具门为插件侧缓解:子 Agent 的 `allow` 预加工为闭集 —— 父工具集 − `run_code` − `deny`,再与 `allow` 取交集。**假设:** 可续跑继承父预设 ⇒ 子工具集 ≈ 父工具集。**失效条件:** 任何导致子工具集与父工具集不一致的宿主行为变化(非仅换用预设——例如未来允许换用预设、组合不同工具集等),父集都可能含子集没有的工具,`tools.restrict` 会抛「未知工具」→ 本缓解自动降级为 fail-loud(保守安全);待上游提供 provider 守卫接缝后替换为真交集。
|
|
95
91
|
|
|
@@ -103,22 +99,40 @@ dsh-subagent-profile/
|
|
|
103
99
|
│ └── core/ # 宿主侧模块(轻量分层)
|
|
104
100
|
│ ├── pure.mjs # 无依赖纯函数(净化 / 剪枝 / 护栏计算——可单测)
|
|
105
101
|
│ ├── shims.mjs # @deepseek-ai 依赖唯一入口(facade:守卫型 fail-loud、功能映射型软降级)
|
|
106
|
-
│
|
|
107
|
-
│
|
|
102
|
+
│ ├── catalog.mjs # 工具名 → 中文/分类数据表(零依赖)
|
|
103
|
+
│ ├── catalog-cache.mjs # 进程级共享 catalog 快照(模型/预设/工具,TTL 缓存)
|
|
104
|
+
│ ├── cost-guard.mjs # 运行时能力校验(provider / model / reasoningEffort)
|
|
105
|
+
│ ├── decision-trace.mjs # 决策轨迹(gates/effective/settled)+ 失败台账
|
|
106
|
+
│ ├── delegation.mjs # 后台 one-shot 结算 + 子会话 usage 收集
|
|
107
|
+
│ ├── dispatch-gates.mjs # 派发预检闸(白名单/成本/交集/预算)
|
|
108
|
+
│ ├── dispatch-guard.mjs # 并发 + 每父累计 token 预算守卫
|
|
109
|
+
│ ├── dispatch-schema.mjs # dispatch 工具输入/输出 schema 声明
|
|
110
|
+
│ ├── dispatch-tool.mjs # dispatch 工具工厂(defineTool + execute + syncTool)
|
|
111
|
+
│ ├── escape.mjs # 逃生舱放行集 store
|
|
112
|
+
│ ├── evolution-ledger.mjs # 派发台账(jsonl)+ 治理审计
|
|
113
|
+
│ ├── evolution-summary.mjs # T1 聚合 summaries + 建议文案
|
|
114
|
+
│ ├── http-routes.mjs # 设置页 loopback HTTP 路由
|
|
115
|
+
│ ├── intersection.mjs # 工具交集纯函数核心
|
|
116
|
+
│ ├── presets-sync.mjs # bundled 预设自安装(哈希门控同步)
|
|
117
|
+
│ ├── profile-provider.mjs # `profile` 子 Agent provider
|
|
118
|
+
│ ├── profiles-store.mjs # profile 注册表 store + 开关持久化
|
|
119
|
+
│ └── whitelist.mjs # system-trust 预设白名单
|
|
108
120
|
├── presets/orchestrator/ # 内置「编排者模式」agent 预设(自安装,每次启动同步)
|
|
109
121
|
├── cordis.patch.yml # bundle 补丁:把插件行插入宿主组成
|
|
110
122
|
├── .gitea/workflows/ci.yml # bare-CI(Gitea Actions;需服务器装 Act runner)
|
|
111
123
|
├── package.json # 元数据、files 发布白名单、exports(test / test:bare / preflight scripts)
|
|
112
124
|
├── scripts/
|
|
113
|
-
│ ├──
|
|
114
|
-
│ └──
|
|
125
|
+
│ ├── preflight.mjs # preflight:preset 树对账 + 无硬编码版本徽章(零依赖)
|
|
126
|
+
│ └── leak-scan.mjs # 公开发布门禁:全历史 + 工作区敏感模式扫描
|
|
115
127
|
├── docs/
|
|
116
128
|
│ └── screenshots/ # README 截图
|
|
117
|
-
├── test/ # 宿主侧自动化测试(node:test,零新增依赖;
|
|
129
|
+
├── test/ # 宿主侧自动化测试(node:test,零新增依赖;352 用例——bare 子集 CI 跑、junction 档本机跑)
|
|
118
130
|
│ ├── README.md / README.zh.md # 测试目录说明(中英双语)——两档测试划分
|
|
119
131
|
│ ├── harness/ctx.mjs # 假宿主环境(fake ctx + ~/.dsh 隔离)
|
|
120
132
|
│ ├── pure / input-schema / catalog-integrity.test.mjs # bare 档(import-free,bare CI 可跑)
|
|
121
|
-
│ └── characterization / facade / gating / persist / recycle /
|
|
133
|
+
│ └── *.test.mjs # junction 档(仅本机):characterization / facade / gating / persist / recycle /
|
|
134
|
+
│ # cost-guard / continuable-guard / decision-trace / dispatch-guard / escape-hatch /
|
|
135
|
+
│ # evolution-* / csrf / label-preset-sync / percall-spec / trust-label / audit-meta / …
|
|
122
136
|
├── README.md / README.zh.md # 本文档(中英双语)
|
|
123
137
|
└── LICENSE
|
|
124
138
|
```
|
|
@@ -127,11 +141,6 @@ dsh-subagent-profile/
|
|
|
127
141
|
|
|
128
142
|
发现 Bug 或有新想法?欢迎[提 Issue](https://github.com/muzyLink/dsh-subagent-profile/issues)或提交 Pull Request,任何形式的贡献都欢迎。
|
|
129
143
|
|
|
130
|
-
### CI(bare)与 preflight
|
|
131
|
-
|
|
132
|
-
- **Gitea Actions**(`.gitea/workflows/ci.yml`):`push` / `pull_request` 到 `v2-midterm` / `main` 时运行 **bare-CI** 子集——从不加载 `@deepseek-ai` 的测试(`npm run test:bare`:pure / input-schema / catalog-integrity)、`node --check` 全仓库语法检查、eslint 行门(eslint 在仓库外隔离安装,因裸环境装不到 `@deepseek-ai` devDependencies)、以及 `npm run preflight`。**启用 Gitea Actions 需服务器装 Act runner**(注册时带上 `ubuntu-latest` 标签),runner 就绪后本工作流自动生效。依赖 junction 的测试(characterization / facade / gating / persist / recycle / cost-guard / continuable-guard)仅本机跑——两档测试的划分与理由见 `test/README.zh.md`。
|
|
133
|
-
- **Preflight**(`npm run preflight`):零依赖自检——① `presets/` 与全新 `$DSH_HOME/.agent-presets` 派生树做 byte 一致集合对账(双向;绝不写真实 `~/.dsh`);② 断言 README 版本徽章(Hero 区 `v<version>`)与 `package.json` 的 `version` 一致。非零退出即漂移,发布前修复后重跑。
|
|
134
|
-
|
|
135
144
|
如果这个插件帮到了你,欢迎在 GitHub 上点个 ⭐,让更多人看到它。
|
|
136
145
|
|
|
137
146
|
## 致谢
|
package/index.mjs
CHANGED
|
@@ -2,51 +2,114 @@
|
|
|
2
2
|
// 源自原型动态插件 `code.host` 主体;宿主导入面(registerTool/defineTool/
|
|
3
3
|
// handle)收敛在 lib/core/shims.mjs,装配块按模块拆分驻留 lib/core/(catalog /
|
|
4
4
|
// presets-sync / profiles-store / cost-guard / whitelist / intersection /
|
|
5
|
-
// delegation / pure / shims / http-routes / profile-provider / dispatch-tool
|
|
5
|
+
// delegation / pure / shims / http-routes / profile-provider / dispatch-tool /
|
|
6
|
+
// dispatch-guard)。
|
|
6
7
|
// apply 的装配辅助函数(syncBundledPresetsToHome / provideProfileService /
|
|
7
8
|
// registerSystemPromptSections / registerSettingsRoutes)保持 section 文本与
|
|
8
9
|
// 门控逐字不变;`enabled` 始终经 getter 注入,门控读取当前值。
|
|
9
10
|
|
|
11
|
+
import { readdirSync, renameSync, rmSync } from 'node:fs';
|
|
12
|
+
import { createRequire } from 'node:module';
|
|
10
13
|
import { join } from 'node:path';
|
|
11
14
|
import { syncBundledPresets } from './lib/core/presets-sync.mjs';
|
|
12
15
|
import { dshHome, createProfileStore } from './lib/core/profiles-store.mjs';
|
|
13
16
|
import { createHttpRoutes } from './lib/core/http-routes.mjs';
|
|
14
17
|
import { createProfileProvider } from './lib/core/profile-provider.mjs';
|
|
15
18
|
import { createDispatchTool } from './lib/core/dispatch-tool.mjs';
|
|
19
|
+
import { createDispatchGuard } from './lib/core/dispatch-guard.mjs';
|
|
16
20
|
import { createCatalogCache } from './lib/core/catalog-cache.mjs';
|
|
21
|
+
import { createFailureLedger } from './lib/core/decision-trace.mjs';
|
|
22
|
+
import { createEvolutionLedger } from './lib/core/evolution-ledger.mjs';
|
|
23
|
+
import { createEscapeStore, recordEscapeAllowProvider } from './lib/core/escape.mjs';
|
|
17
24
|
import { tierSortKey } from './lib/core/pure.mjs';
|
|
25
|
+
import { resolveWhitelist, FALLBACK_WHITELIST } from './lib/core/whitelist.mjs';
|
|
26
|
+
import { buildAdviceText, refreshSummaries } from './lib/core/evolution-summary.mjs';
|
|
18
27
|
|
|
19
28
|
export const name = 'dsh-subagent-profile';
|
|
20
29
|
export const inject = ['subagents', 'tools', 'agents'];
|
|
21
30
|
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// intended upgrade path. Fail-soft: the dispatch tool and settings page keep
|
|
27
|
-
// working even if the write is denied.
|
|
31
|
+
// 自装 bundled 的 "orchestrator" agent 预设到 DSH agent-presets 根,让该模式
|
|
32
|
+
// 无需手动复制就出现在新建会话选择器里(与官方 dsh-liangshen 自安装相同)。
|
|
33
|
+
// 幂等:字节一致的树跳过;bundle 变更重写预设——即预期的升级路径。fail-soft:
|
|
34
|
+
// 写入被拒时 dispatch 工具与设置页照常工作。
|
|
28
35
|
function syncBundledPresetsToHome(ctx) {
|
|
29
36
|
try {
|
|
30
37
|
const presetRoot = join(dshHome(), '.agent-presets');
|
|
31
38
|
const sync = syncBundledPresets(presetRoot);
|
|
32
39
|
for (const { id, error } of sync.failed) ctx.logger.warn(`[dsh-subagent-profile] preset ${id} sync failed: ${error}`);
|
|
33
40
|
if (sync.synced.length > 0) ctx.logger.info(`[dsh-subagent-profile] presets synced into ${presetRoot}: ${sync.synced.join(', ')}`);
|
|
41
|
+
if (sync.userModified.length > 0) ctx.logger.warn(`[dsh-subagent-profile] presets left untouched (user-modified): ${sync.userModified.join(', ')}`);
|
|
34
42
|
} catch (error) {
|
|
35
43
|
ctx.logger.warn('[dsh-subagent-profile] preset sync failed:', error instanceof Error ? error.message : String(error));
|
|
36
44
|
}
|
|
37
45
|
}
|
|
38
46
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
47
|
+
// 卸载清理:把本插件持久化的 3 个数据文件 + 自装的 orchestrator 预设目录改名备份
|
|
48
|
+
// (只动本插件拥有的 orchestrator 目录,不碰 .agent-presets 下其它插件/用户的目录)。
|
|
49
|
+
// 改为「改名到 <name>.removed-<ts>」而非 rmSync 删除:ctx.effect 的清理器在禁用/热
|
|
50
|
+
// 重载/进程退出时都可能触发,硬删除会静默丢失用户 profiles/state/失败台账(S1)。
|
|
51
|
+
// 备份保留可恢复副本;规范路径被清空,重装/重启后经 syncBundledPresetsToHome 重新
|
|
52
|
+
// 同步预设、数据文件按需重新生成。fail-soft:改名失败不抛(卸载不应因清理失败而阻断)。
|
|
53
|
+
function removeOwnedData(home) {
|
|
54
|
+
const stamp = Date.now();
|
|
55
|
+
for (const file of ['subagent-profiles.json', 'subagent-profiles.state.json', 'subagent-profiles.failed-traces.json']) {
|
|
56
|
+
try { renameSync(join(home, file), join(home, `${file}.removed-${stamp}`)); } catch { /* 源不存在(正常首启)等:best effort */ }
|
|
57
|
+
}
|
|
58
|
+
try { renameSync(join(home, '.agent-presets', 'orchestrator'), join(home, '.agent-presets', `orchestrator.removed-${stamp}`)); } catch { /* best effort */ }
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 启动清理 S1 改名备份残留:.removed-<ts> 备份在下次正常启动后已无保留价值(数据
|
|
62
|
+
// 文件按需重新生成),启动时清掉防积累(热重载/禁用反复触发 effect 会持续产生新
|
|
63
|
+
// 备份)。只清本插件前缀(subagent-profiles.* 与 orchestrator.removed-*),不碰其它
|
|
64
|
+
// 文件;卸载后用户手动改回原名的场景不受影响(改回后无 .removed 残留)。fail-soft。
|
|
65
|
+
function cleanRemovedBackups(home) {
|
|
66
|
+
for (const dir of [home, join(home, '.agent-presets')]) {
|
|
67
|
+
let names;
|
|
68
|
+
try { names = readdirSync(dir); } catch { continue; }
|
|
69
|
+
for (const name of names) {
|
|
70
|
+
try {
|
|
71
|
+
if (name.startsWith('subagent-profiles.') && name.includes('.removed-')) {
|
|
72
|
+
rmSync(join(dir, name), { force: true });
|
|
73
|
+
} else if (name.startsWith('orchestrator.removed-')) {
|
|
74
|
+
rmSync(join(dir, name), { recursive: true, force: true });
|
|
75
|
+
}
|
|
76
|
+
} catch { /* best effort */ }
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 卸载接线:注销 dispatch 工具(若仍注册)+ 清空失败台账 + 级联取消在途派发 +
|
|
82
|
+
// 清空守卫记账 + 删本插件数据文件与自装预设目录。另挂钩宿主 agent/disposed
|
|
83
|
+
// (Agent 注册 fiber 卸载时发射):父会话逻辑结束时按父释放并发/token 记账,
|
|
84
|
+
// 防配额随会话累积驻留。
|
|
85
|
+
function registerTeardown(ctx, dispatch, ledger, guard, home) {
|
|
86
|
+
ctx.effect(() => dispatch.dispose);
|
|
87
|
+
ctx.effect(() => ledger.clear);
|
|
88
|
+
ctx.effect(() => () => {
|
|
89
|
+
guard.cancelAll();
|
|
90
|
+
guard.reset();
|
|
91
|
+
removeOwnedData(home);
|
|
92
|
+
});
|
|
93
|
+
const onAgentDisposed = ({ agent }) => {
|
|
94
|
+
const parentSessionId = agent?.session?.header?.id;
|
|
95
|
+
if (parentSessionId !== undefined && parentSessionId !== null && parentSessionId !== '') {
|
|
96
|
+
guard.resetParent(parentSessionId);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
ctx.on('agent/disposed', onAgentDisposed);
|
|
100
|
+
ctx.effect(() => () => ctx.off('agent/disposed', onAgentDisposed));
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// subagent-profiles service:包在 store 的 per-apply profiles Map 之上,对外只暴露
|
|
104
|
+
// 枚举/扩展注册表的窄接口,不触碰内部结构。
|
|
42
105
|
function provideProfileService(ctx, store) {
|
|
43
106
|
ctx.provide('subagent-profiles', {
|
|
44
107
|
register(profile) {
|
|
45
108
|
if (!profile || typeof profile.id !== 'string' || profile.id.length === 0) {
|
|
46
|
-
throw new Error('subagent-profiles: profile id
|
|
109
|
+
throw new Error('subagent-profiles: profile id 必须为非空字符串');
|
|
47
110
|
}
|
|
48
111
|
if (store.profiles.has(profile.id)) {
|
|
49
|
-
throw new Error(`subagent-profiles: profile "${profile.id}"
|
|
112
|
+
throw new Error(`subagent-profiles: profile "${profile.id}" 已注册(id 需唯一)`);
|
|
50
113
|
}
|
|
51
114
|
const registered = { ...profile };
|
|
52
115
|
store.profiles.set(profile.id, registered);
|
|
@@ -113,11 +176,21 @@ function profileSectionText(store, gate, context) {
|
|
|
113
176
|
// orchestrator:mode section 文本(逐字保留;与 profiles 同门控)。
|
|
114
177
|
const ORCHESTRATOR_MODE_TEXT = '本机已安装 dsh-subagent-profile 插件的「编排者模式」agent preset:新建会话的预设选择器中可选「编排者模式」。该模式把 Agent 定位为主协调者——拆解任务后按场景用 dispatch(内置 swap-standard=标准编码、researcher=调研检索,可在「子 Agent 方案」设置页自定义)与 subagent/subagent_fork/workflow 委派给子 Agent,再整合结果。preset 文件由插件维护于 ~/.dsh/.agent-presets,安装/升级时自动同步;用户提到「编排者模式 / orchestrator / 主协调模式」时即指本预设,请据此协作。';
|
|
115
178
|
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
//
|
|
119
|
-
|
|
120
|
-
|
|
179
|
+
// 建议注入候选池:system-trust 白名单(agentPresets 可选;解析失败回退内置
|
|
180
|
+
// 回退名单——fail-loud 检查在建议生成路径进行,不在此处)。刻意不叠加逃生舱放行
|
|
181
|
+
// 集:只读建议不享受逃生舱(逃生舱仅放行「其余三道闸全过」的显式派发,不扩建议池)。
|
|
182
|
+
async function resolveAdviceWhitelist(ctx) {
|
|
183
|
+
try {
|
|
184
|
+
return new Set(await resolveWhitelist(ctx.get('agentPresets')));
|
|
185
|
+
} catch {
|
|
186
|
+
return new Set(FALLBACK_WHITELIST);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// 目录 section:渲染可用方案(systemPrompt 的 section text 接受函数,官方
|
|
191
|
+
// tool-subagent 即此用法)。门控注意:`enabled` 必须经 getter 实时读取,
|
|
192
|
+
// 使 /set-enabled 切换立即生效、无需重启。
|
|
193
|
+
function registerSystemPromptSections(ctx, store, getEnabled, getEvolutionAdvice, adviceEnv) {
|
|
121
194
|
const pluginSystemPrompt = ctx.get('systemPrompt');
|
|
122
195
|
if (pluginSystemPrompt === undefined) return;
|
|
123
196
|
const gate = (context) => sectionGatePasses(ctx, getEnabled, context);
|
|
@@ -126,25 +199,38 @@ function registerSystemPromptSections(ctx, store, getEnabled) {
|
|
|
126
199
|
order: 116.5,
|
|
127
200
|
text: (context) => profileSectionText(store, gate, context),
|
|
128
201
|
});
|
|
129
|
-
//
|
|
130
|
-
//
|
|
131
|
-
// way — only a dispatch-capable agent sees it.
|
|
202
|
+
// 宣告自装的 orchestrator 预设,让当前 agent 知道该模式存在、可引导用户使用。
|
|
203
|
+
// 门控与 profiles 段相同——只有可派发的 agent 才看得到。
|
|
132
204
|
pluginSystemPrompt.section({
|
|
133
205
|
name: 'orchestrator:mode',
|
|
134
206
|
order: 117,
|
|
135
207
|
text: (context) => (gate(context) ? ORCHESTRATOR_MODE_TEXT : ''),
|
|
136
208
|
});
|
|
209
|
+
// 只读建议段:门控 = orchestrator(sectionGatePasses)+ evolutionAdvice 开关。
|
|
210
|
+
// 只进父 Agent(复用同一门控)、默认关、只含确定性聚合数字与建议文案,不含派生原文。
|
|
211
|
+
// 生成异常 try/catch 兜底:fail-loud 检查(非 system 候选)落 warn 日志但绝不
|
|
212
|
+
// 阻断每次提示装配——损坏/手改的 summaries 不得让 orchestrator 会话无法启动。
|
|
213
|
+
pluginSystemPrompt.section({
|
|
214
|
+
name: 'evolution:advice',
|
|
215
|
+
order: 116.8,
|
|
216
|
+
text: (context) => {
|
|
217
|
+
if (!getEvolutionAdvice() || !gate(context)) return '';
|
|
218
|
+
try {
|
|
219
|
+
return buildAdviceText(adviceEnv);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
adviceEnv.logger.warn(`[dsh-subagent-profile] evolution:advice 生成失败,本次不注入:${error instanceof Error ? error.message : String(error)}`);
|
|
222
|
+
return '';
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
});
|
|
137
226
|
}
|
|
138
227
|
|
|
139
|
-
//
|
|
140
|
-
//
|
|
141
|
-
//
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
|
|
145
|
-
// resolve, so register inside an inject sub-scope that waits for it
|
|
146
|
-
// (ctx.get would read undefined at apply time).
|
|
147
|
-
function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, catalog) {
|
|
228
|
+
// Client 设置 UI 的 HTTP loopback 路由(webServer.register ↔ client fetch,纯 JSON)。
|
|
229
|
+
// 路由本体在 lib/core/http-routes.mjs(上文已 import);这里只注入 per-apply 依赖。
|
|
230
|
+
// webServer 可选——无头部署保留 dispatch 工具、只丢设置页。webServer 的激活
|
|
231
|
+
// (listen)是异步的,可能晚于本插件 inject 依赖解析完成,故在等它的 inject
|
|
232
|
+
// 子 scope 内注册(apply 时 ctx.get 会读到 undefined)。
|
|
233
|
+
function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, catalog, ledger, getAudit, getEvolutionAdvice, setEvolutionAdvice, getEscapeEnabled, setEscapeEnabled, escape, refreshAdvice) {
|
|
148
234
|
ctx.inject(['webServer'], (scope) => {
|
|
149
235
|
scope.effect(createHttpRoutes({
|
|
150
236
|
webServer: scope.webServer,
|
|
@@ -153,6 +239,14 @@ function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, ca
|
|
|
153
239
|
setEnabled,
|
|
154
240
|
syncTool,
|
|
155
241
|
catalog,
|
|
242
|
+
ledger,
|
|
243
|
+
getAudit,
|
|
244
|
+
getEvolutionAdvice,
|
|
245
|
+
setEvolutionAdvice,
|
|
246
|
+
getEscapeEnabled,
|
|
247
|
+
setEscapeEnabled,
|
|
248
|
+
escape,
|
|
249
|
+
refreshAdvice,
|
|
156
250
|
logger: ctx.logger,
|
|
157
251
|
}), 'dsh-subagent-profile: settings routes');
|
|
158
252
|
});
|
|
@@ -170,48 +264,80 @@ function createSharedCatalog(ctx) {
|
|
|
170
264
|
});
|
|
171
265
|
}
|
|
172
266
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
267
|
+
// 读本插件 package.json version 供派发台账 provenance 使用。fail-soft:读取/解析
|
|
268
|
+
// 失败回退 'unknown',绝不阻断插件启动。
|
|
269
|
+
function readPluginVersion() {
|
|
270
|
+
try {
|
|
271
|
+
const pkg = createRequire(import.meta.url)('./package.json');
|
|
272
|
+
return typeof pkg?.version === 'string' && pkg.version !== '' ? pkg.version : 'unknown';
|
|
273
|
+
} catch {
|
|
274
|
+
return 'unknown';
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// 逃生舱装配:放行集 store + 开关态(state.json 持久化)+ 访问器。开关态经对象字段
|
|
279
|
+
// 可变(/set-escape 改写),getEscapeSet 开关关时恒空数组(零叠加、零放行审计)。
|
|
280
|
+
function setupEscape(ctx, store, evoLedger, home) {
|
|
281
|
+
const escape = createEscapeStore({ dshHome: home, logger: ctx.logger, onGovernanceFailure: () => evoLedger.markGovernanceFailure() });
|
|
282
|
+
const state = { escapeEnabled: store.loadEscapeEnabled() };
|
|
283
|
+
return {
|
|
284
|
+
escape,
|
|
285
|
+
getEscapeSet: () => (state.escapeEnabled ? escape.list() : []),
|
|
286
|
+
getEscapeEnabled: () => state.escapeEnabled,
|
|
287
|
+
setEscapeEnabled: (next) => { state.escapeEnabled = next; },
|
|
288
|
+
recordEscapeAllowProvider: (parent, preset) => recordEscapeAllowProvider(evoLedger, parent, preset),
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// `dispatch` 工具装配(defineTool + execute + syncTool)。须在 HTTP inject 之前
|
|
293
|
+
// 构造,使 createHttpRoutes 能拿 dispatch.syncTool 供 /set-enabled 注册/注销。
|
|
294
|
+
function createDispatch(ctx, store, catalog, ledger, guard, evoLedger, getEscapeSet, getEnabled) {
|
|
295
|
+
return createDispatchTool({
|
|
191
296
|
register: (tool) => ctx.tools.register(tool),
|
|
192
297
|
store,
|
|
193
|
-
getEnabled
|
|
298
|
+
getEnabled,
|
|
194
299
|
getService: (name) => ctx.get(name),
|
|
195
300
|
logger: ctx.logger,
|
|
196
301
|
subagents: ctx.subagents,
|
|
197
302
|
catalog,
|
|
303
|
+
ledger,
|
|
304
|
+
guard,
|
|
305
|
+
evoLedger,
|
|
306
|
+
getEscapeSet,
|
|
198
307
|
});
|
|
199
|
-
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
export async function apply(ctx) {
|
|
311
|
+
const home = dshHome();
|
|
312
|
+
// 启动清理:上个生命周期留下的 .removed-* 备份残留(S1 改名的副产物)。
|
|
313
|
+
cleanRemovedBackups(home);
|
|
314
|
+
// 派发台账 + 审计分级须在 store 之前构造:store 的治理审计钩子指向其 markGovernanceFailure。
|
|
315
|
+
const evoLedger = createEvolutionLedger({ dshHome: home, pluginVersion: readPluginVersion(), warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`) });
|
|
316
|
+
const store = createProfileStore({ dshHome: home, logger: ctx.logger, onGovernanceFailure: () => evoLedger.markGovernanceFailure() });
|
|
317
|
+
const escapeCtl = setupEscape(ctx, store, evoLedger, home);
|
|
318
|
+
let enabled = store.loadEnabled();
|
|
319
|
+
let evolutionAdvice = store.loadEvolutionAdvice();
|
|
320
|
+
store.loadProfiles();
|
|
321
|
+
// 自装 bundled 的 orchestrator 预设(幂等、fail-soft)。
|
|
322
|
+
syncBundledPresetsToHome(ctx);
|
|
323
|
+
const catalog = createSharedCatalog(ctx);
|
|
324
|
+
const ledger = createFailureLedger({ warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`), stateFile: join(home, 'subagent-profiles.failed-traces.json') });
|
|
325
|
+
const guard = createDispatchGuard({ warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`) });
|
|
326
|
+
const dispatch = createDispatch(ctx, store, catalog, ledger, guard, evoLedger, escapeCtl.getEscapeSet, () => enabled);
|
|
200
327
|
provideProfileService(ctx, store);
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
//
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
getEnabled: () => enabled,
|
|
328
|
+
const adviceWhitelist = await resolveAdviceWhitelist(ctx);
|
|
329
|
+
const adviceEnv = { summariesFile: join(home, 'subagent-evolution', 'summaries.json'), dispatchFile: join(home, 'subagent-evolution', 'dispatch.jsonl'), whitelist: adviceWhitelist, logger: ctx.logger };
|
|
330
|
+
// 生产聚合触发点(T1 修复):/options/refresh 手动刷新时重算 summaries.json,让
|
|
331
|
+
// evolution:advice 有初始生成路径(原先 computeSummaries/writeSummaries 只被测试调用)。
|
|
332
|
+
const refreshAdvice = () => refreshSummaries({
|
|
333
|
+
dispatchFile: join(home, 'subagent-evolution', 'dispatch.jsonl'),
|
|
334
|
+
summariesFile: join(home, 'subagent-evolution', 'summaries.json'),
|
|
209
335
|
logger: ctx.logger,
|
|
210
|
-
catalog,
|
|
211
336
|
});
|
|
337
|
+
registerSystemPromptSections(ctx, store, () => enabled, () => evolutionAdvice, adviceEnv);
|
|
338
|
+
const disposeProvider = createProfileProvider({ subagents: ctx.subagents, store, getEnabled: () => enabled, logger: ctx.logger, catalog, getEscapeSet: escapeCtl.getEscapeSet, recordEscapeAllowProvider: escapeCtl.recordEscapeAllowProvider });
|
|
212
339
|
if (typeof disposeProvider === 'function') ctx.effect(() => disposeProvider);
|
|
213
|
-
//
|
|
214
|
-
registerSettingsRoutes(ctx, store, () => enabled, (next) => { enabled = next; }, dispatch.syncTool, catalog);
|
|
215
|
-
|
|
216
|
-
ctx.effect(() => dispatch.dispose);
|
|
340
|
+
// Client 设置 UI 的 HTTP loopback 路由 —— lib/core/http-routes.mjs。
|
|
341
|
+
registerSettingsRoutes(ctx, store, () => enabled, (next) => { enabled = next; }, dispatch.syncTool, catalog, ledger, () => evoLedger.auditState(), () => evolutionAdvice, (next) => { evolutionAdvice = next; }, escapeCtl.getEscapeEnabled, escapeCtl.setEscapeEnabled, escapeCtl.escape, refreshAdvice);
|
|
342
|
+
registerTeardown(ctx, dispatch, ledger, guard, home);
|
|
217
343
|
}
|