@chaoset/adaptive-perf 0.5.0 → 0.7.0
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 +186 -0
- package/README.md +52 -34
- package/client/client.cjs +18 -48
- package/cordis.patch.yml +21 -12
- package/lib/index.mjs +144 -231
- package/package.json +12 -13
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.7.0](https://github.com/winliyou/dsh-plugins) (2026-08-18)
|
|
4
|
+
|
|
5
|
+
### Features
|
|
6
|
+
|
|
7
|
+
* **first-round anchoring, resident catalog after promotion (anchored-standard semantics).** Only request #1 is composed under Minimal conditions (real Minimal tool pair + minimal persona + suppressed global guide sections + stripped `skill-catalog`/`agent-instructions` injections + suppressed runtime snapshot); after the first persistent promotion signal (first `tool/call` or `assistant/message`) the session enters the **resident phase**: the bootstrap tool pair stays resident together with the discovery tools (`dev_tool_search`/`skill_search`/`skill_load`), the full catalog is unlocked on demand via `dev_tool_search`, and normal context injection becomes visible again. Performance is raised without reducing functionality — dumping the full catalog at promotion pulls the trajectory back to standard-like (the reference implementation's post-promotion regression), so the full catalog is fetched on demand instead. The tool/skill/web-search/plan/goal/subagent/workflow abilities of each mode remain fully available
|
|
8
|
+
|
|
9
|
+
* **promotion is now persistent.** `scanPhase`/`observePhase` no longer reset on `compaction/end` (epoch-aware boundary removed); promotion is remembered per session like the reference implementation, so resume/reload and post-compaction rounds keep the resident catalog
|
|
10
|
+
|
|
11
|
+
* **catalog is zero-trimmed by default.** `leanByDefault` defaults to `false`: orchestration tool families (subagent/workflow/ralph/goal) are never hidden unless explicitly opted in. `suppressInjectedContext` defaults to `true` (injections stay stripped for the whole session; visibility is carried by the resident discovery tools — measured: restoring injections after promotion pulls the trajectory back to standard-like, "Let me…" narration returns). Setting it to `false` restores post-promotion injections as an opt-in
|
|
12
|
+
|
|
13
|
+
* **creator mode (cordis) is a target preset.** `presets` defaults to `["standard", "code", "cordis"]` in the bundle patch, the settings UI, and DEFAULT_CONFIG — the creator preset gets the same minimal-anchored first round
|
|
14
|
+
|
|
15
|
+
### ⚠ Breaking (config)
|
|
16
|
+
|
|
17
|
+
* **removed `skillDiscovery`, `instructionHint`, `bootstrap.compactionTools`; restored `bootstrap.discoveryTools`.** Under resident-catalog semantics the on-demand discovery is done by the resident `dev_tool_search`, so the one-shot instruction hint and the post-compaction work set are no longer needed; `bootstrap.discoveryTools` (default `dev_tool_search`/`skill_search`/`skill_load`) is restored as the resident post-promotion discovery set. The discovery tool factories (`createDevToolSearch`/`createSkillSearch`/`createSkillLoad`/`extractSkillBody`) and `loadUnlockedFromEvents` are back; saved configs ignore the removed fields; the settings UI renders the discovery-tools control again; `leanByDefault` keeps its new default (false) while `suppressInjectedContext` defaults to `true`
|
|
18
|
+
|
|
19
|
+
## [0.6.0](https://github.com/winliyou/dsh-plugins) (2026-08-17)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### ⚠ Breaking (behavior)
|
|
23
|
+
|
|
24
|
+
* **function-first defaults — every optimization is now opt-in.** The previous defaults broke standard/PTC functionality: tool families removed from the catalog (PTC program calls hit `UNKNOWN_TOOL` outright), skill-catalog/AGENTS.md injections stripped, official prompt sections shadowed, request #1 restricted to two tools. These mechanisms have no lossless form (their savings come from removing context/tools), so `leanByDefault`, `suppressRuntimeContext`, `suppressInjectedContext`, `minimalPrompt.enabled`, and `bootstrap.enabled` all default to **false**: a fresh install is a zero-intervention no-op and official presets keep full functionality. Enable any mechanism on demand from the settings page; compensation paths (keyword/failure-signal escalation, `skill_search` discovery, instruction-hint) activate together with them. Tests now cover both the zero-intervention defaults and each mechanism under explicit opt-in config
|
|
25
|
+
|
|
26
|
+
## [0.5.3](https://github.com/winliyou/dsh-plugins) (2026-08-17)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
### Bug Fixes
|
|
30
|
+
|
|
31
|
+
* pass the live config snapshot as `base` when registering the settings namespace: without `base`, a schema without defaults (adaptive-perf's `any()`) made `settings.describe()` return `value: undefined`, and the settings page's wire validation (`invalid_type: nonoptional` at `namespaces[n].value`) failed hard, breaking the whole settings UI. With `base` the resolved value is always the full config object
|
|
32
|
+
|
|
33
|
+
## [0.5.2](https://github.com/winliyou/dsh-plugins) (2026-08-17)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
### Bug Fixes
|
|
37
|
+
|
|
38
|
+
* register the config namespace into the host settings service (`ctx.settings.register`) so the settings page's configurable-plugins tab lists it: the tab dispatches cards from `settings.describe()`, and a namespace that was never registered renders nothing even with a correctly keyed card. Registration is visibility-only — the card still reads/writes through the plugin's own config gateway (config.json stays authoritative, hot-reload preserved). Fail-safe when schemastery or the settings service is unavailable; duplicate registrations (HMR) are ignored
|
|
39
|
+
|
|
40
|
+
## [0.5.1](https://github.com/winliyou/dsh-plugins) (2026-08-17)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
### Bug Fixes
|
|
44
|
+
|
|
45
|
+
* settings card registration: pass `key` (the settings namespace, matching the host-side service key) when registering into `settings.plugin.item` — the host `dsh-client-ui-slots` 0.1.0-rc.7 declares it as a keyed slot, and a registration without `key` fails the whole client bundle apply ("Failed to load plugins: keyed slot requires options.key")
|
|
46
|
+
|
|
47
|
+
## [0.5.0](https://github.com/winliyou/dsh-plugins) (2026-08-16)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
### Features
|
|
51
|
+
|
|
52
|
+
* real Minimal tool pair on request #1 (`bootstrap.realPair`)
|
|
53
|
+
|
|
54
|
+
The bootstrap phase previously only *narrowed* the standard/PTC catalog, so
|
|
55
|
+
request #1 still exposed the sandboxed standard `bash` schema (and no
|
|
56
|
+
`str_replace_editor` at all — the standard preset does not mount it).
|
|
57
|
+
Per dsh-anchored-standard issue #11 the first-request trajectory is decided
|
|
58
|
+
by the *byte-identical* tool schema: the real Minimal pair anchors 5/5 while
|
|
59
|
+
every standard-family schema (including sandboxed bash) falls standard-like
|
|
60
|
+
11/11. The plugin now mounts the official minimal-preset plugins — the
|
|
61
|
+
persistent PTY bash (`@deepseek-ai/dsh-tool-bash-persistent` over
|
|
62
|
+
`dsh-terminal` + `dsh-terminal-bash`, with the exact minimal description)
|
|
63
|
+
and `str_replace_editor` over the bare local fs (`dsh-fs-local`) — into each
|
|
64
|
+
target agent's scoped tool layer, where scoped registrations shadow the
|
|
65
|
+
inherited sandboxed `bash` by name and own-layer registrations are exempt
|
|
66
|
+
from restrictions. The `tool:bash` guidance section is shadowed to empty at
|
|
67
|
+
the same time. The packages are optionalDependencies; when they cannot be
|
|
68
|
+
resolved (or on Windows, where the PTY backend is unavailable) the plugin
|
|
69
|
+
degrades to the previous catalog-only behavior with a warning.
|
|
70
|
+
|
|
71
|
+
* permanent injected-context suppression (`suppressInjectedContext`)
|
|
72
|
+
|
|
73
|
+
The skill-catalog reminder and the AGENTS.md digest were previously stripped
|
|
74
|
+
only on request #1 and re-injected after promotion. The reference implementation
|
|
75
|
+
removes both injections entirely: even post-promotion they perturb the
|
|
76
|
+
trajectory and cost thousands of tokens per request. With
|
|
77
|
+
`suppressInjectedContext: true` (default) the strip now applies to every
|
|
78
|
+
request of the session; `skill_search` / `skill_load` (resident after
|
|
79
|
+
promotion, gated by `skillDiscovery`) replace the ~9KB catalog dump, and the
|
|
80
|
+
new one-shot `instruction-hint` (`instructionHint`, default on) tells the
|
|
81
|
+
model once per session that instruction files exist and should be read when
|
|
82
|
+
relevant, instead of embedding their content. User-initiated skill gestures
|
|
83
|
+
(`skill-invocation`) are never filtered.
|
|
84
|
+
|
|
85
|
+
* resident discovery set now includes skill_search / skill_load
|
|
86
|
+
|
|
87
|
+
`bootstrap.discoveryTools` defaults to `[dev_tool_search, skill_search,
|
|
88
|
+
skill_load]` so the promoted catalog keeps the on-demand discovery surface of
|
|
89
|
+
the reference's resident set; skills stay reachable while the catalog
|
|
90
|
+
injection stays off.
|
|
91
|
+
|
|
92
|
+
## [0.4.0](https://github.com/winliyou/dsh-plugins) (2026-08-16)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
### Features
|
|
96
|
+
|
|
97
|
+
* minimal-prompt layer (register anchoring, per dsh-anchored-standard)
|
|
98
|
+
|
|
99
|
+
The reference's full anchoring condition is the *complete minimal system
|
|
100
|
+
prompt*, not just the tool catalog: tool narrowing alone leaves the global
|
|
101
|
+
orientation sections (harness:identity / harness:source / app:web-surface)
|
|
102
|
+
and the standard persona in the prompt, and the model keeps narrating in the
|
|
103
|
+
standard-like register ("Let me…" thinking chains). The new `minimalPrompt`
|
|
104
|
+
layer shadows those sections to empty per target agent (same effect as
|
|
105
|
+
minimal's `complete` persona; plan-mode and the PTC SDK sections are
|
|
106
|
+
untouched) and shadows `deployment:persona` with the exact minimal-mode text
|
|
107
|
+
("You are a helpful software engineer assistant."), so the assembled system
|
|
108
|
+
prompt for standard / PTC sessions drops to the minimal register from request
|
|
109
|
+
#1 onward — including for resumed/already-promoted sessions, which the
|
|
110
|
+
bootstrap phase never touches. All knobs are configurable and hot-applied
|
|
111
|
+
(`minimalPrompt.enabled` / `.persona` / `.suppressSections`).
|
|
112
|
+
|
|
113
|
+
## [0.3.2](https://github.com/winliyou/dsh-plugins) (2026-08-16)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
### Bug Fixes
|
|
117
|
+
|
|
118
|
+
* bootstrap tool narrowing now uses tools.restrict instead of filtering assembly.tools
|
|
119
|
+
|
|
120
|
+
In PTC/code mode the assembled tool catalog contains only run_code, so
|
|
121
|
+
filtering it down to the Minimal pair always hit the fail-safe and the
|
|
122
|
+
first request kept the full SDK (verified live: the model still saw 15
|
|
123
|
+
tools). tools.restrict drives both the API catalog and the PTC SDK
|
|
124
|
+
reference section (the same mechanism the family trimming uses), so the
|
|
125
|
+
bootstrap phase now temporarily denies everything except
|
|
126
|
+
run_code + the Minimal pair (plus the post-compaction work set) and
|
|
127
|
+
lifts the restriction on the first durable promotion signal. Verified
|
|
128
|
+
live: on request #1 the model reports run_code as the only direct tool
|
|
129
|
+
and the SDK declarations contain only the Minimal pair.
|
|
130
|
+
|
|
131
|
+
## [0.3.1](https://github.com/winliyou/dsh-plugins) (2026-08-16)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
### Bug Fixes
|
|
135
|
+
|
|
136
|
+
* raise engines.node to >=22.19.0 (node 20 is EOL; aligns with the reference preset baseline and the current LTS floor)
|
|
137
|
+
|
|
138
|
+
## [0.3.0](https://github.com/winliyou/dsh-plugins/compare/adaptive-perf-v0.2.2...adaptive-perf-v0.3.0) (2026-08-16)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
### Features
|
|
142
|
+
|
|
143
|
+
* first-request bootstrap anchoring (inspired by dsh-anchored-standard)
|
|
144
|
+
|
|
145
|
+
Request #1 for target presets now exposes only the real Minimal tool pair
|
|
146
|
+
(bash + str_replace_editor) and strips auto-injected context
|
|
147
|
+
(skill-catalog reminder, AGENTS.md digest), which anchors the first-request
|
|
148
|
+
trajectory on minimal conditions (the community measurements: the Minimal
|
|
149
|
+
pair anchors 5/5 at the adapter-default budget while every standard-family
|
|
150
|
+
schema falls into standard-like behavior 11/11, and a present skill catalog
|
|
151
|
+
breaks the anchor 0/9). The session promotes after its first durable
|
|
152
|
+
tool/call or assistant/message (promoteOn: either, or tool-call /
|
|
153
|
+
assistant-message) — phase derived from durable session events, resume-safe.
|
|
154
|
+
A compaction resets the phase (epoch-aware): after compaction/end the
|
|
155
|
+
session falls back to the bootstrap pair plus a configurable compactionTools
|
|
156
|
+
work set until a new promotion signal. bootstrap.maxTokens optionally caps
|
|
157
|
+
request #1's output budget and is stripped after promotion.
|
|
158
|
+
|
|
159
|
+
Wiring uses the harness's public waterfalls: system-prompt/assemble (tool
|
|
160
|
+
catalog narrowing), agent/pre-step (context stripping, prepend-registered so
|
|
161
|
+
the strip is the final transform), agent/request (budget cap), and
|
|
162
|
+
session/event (phase feed). Degrades to the full catalog on any filter
|
|
163
|
+
failure so a plugin bug can never brick a session.
|
|
164
|
+
|
|
165
|
+
## [0.2.2](https://github.com/winliyou/dsh-plugins/compare/adaptive-perf-v0.2.1...adaptive-perf-v0.2.2) (2026-08-16)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
### Bug Fixes
|
|
169
|
+
|
|
170
|
+
* register config-gateway endpoints via a typert-loader host artifact (lib/typert.host.js) so the api-gateway claims them regardless of module-instance identity ([dsh-plugins#registry-install](https://github.com/winliyou/dsh-plugins))
|
|
171
|
+
|
|
172
|
+
When the package is installed from the npm registry, its typert-protocol copy differs from the harness's, so the Remote-decorator SRC markers are invisible to the gateway and settings-page calls fail with "transport failure ... HTTP 404". The typert artifact registers the same get/set endpoints into ctx.typert.local through the official loader mechanism.
|
|
173
|
+
|
|
174
|
+
## [0.2.1](https://github.com/winliyou/dsh-plugins/compare/adaptive-perf-v0.2.0...adaptive-perf-v0.2.1) (2026-08-16)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
### Bug Fixes
|
|
178
|
+
|
|
179
|
+
* mount remote config namespaces in client bundles ([a647484](https://github.com/winliyou/dsh-plugins/commit/a64748489ddf003cb29c0527016ea089748e0c7b))
|
|
180
|
+
|
|
181
|
+
## [0.2.0](https://github.com/winliyou/dsh-plugins/compare/adaptive-perf-v0.1.2...adaptive-perf-v0.2.0) (2026-08-15)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
### Features
|
|
185
|
+
|
|
186
|
+
* convert plugins to DSH bundle installation ([caae797](https://github.com/winliyou/dsh-plugins/commit/caae797acd12c70af52b0868ec352d91a0a7ef12))
|
package/README.md
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
# @chaoset/adaptive-perf — DSH 极简性能自适应插件
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
> **默认策略(0.7.0,首轮锚定、晋升后 resident 目录)**:只提高性能,不减少功能。
|
|
4
|
+
> 非极简模式会话的**请求 #1** 按极简模式条件组装——真实 Minimal 工具对
|
|
5
|
+
> 锚定首轮轨迹、极简 persona + 屏蔽全局引导段对齐语域、剥离技能目录/指令
|
|
6
|
+
> 摘要注入、抑制运行时快照;首个**持久**晋升信号(首个 `tool/call` 或
|
|
7
|
+
> `assistant/message`)后进入 **resident 阶段**——保留 bootstrap 工具对 +
|
|
8
|
+
> 常驻发现工具(`dev_tool_search` / `skill_search` / `skill_load`),完整
|
|
9
|
+
> 目录经 `dev_tool_search` 按需解锁,常规上下文注入恢复可见(与
|
|
10
|
+
> [dsh-anchored-standard](https://github.com/xiaobright/dsh-anchored-standard/)
|
|
11
|
+
> 语义一致:晋升按会话记忆化,compaction 不重置;晋升时一次性倒出完整目录
|
|
12
|
+
> 会把轨迹拉回 standard-like,故完整目录按需取用)。工具目录默认**零裁剪**
|
|
13
|
+
> (编排类工具族始终可用);`leanByDefault` 是省 token 的 opt-in 开关,
|
|
14
|
+
> `suppressInjectedContext` 默认开启(注入剥离由发现工具承担可见性),
|
|
15
|
+
> 设置页按需调整。
|
|
16
|
+
|
|
17
|
+
让 **标准模式(standard)**、**PTC 模式(code)** 与 **创造模式(cordis)**
|
|
18
|
+
达到 **极简模式(minimal)** 级别的高性能,同时保留完整能力——不是静态裁剪,
|
|
19
|
+
而是**动态自适应**:
|
|
5
20
|
|
|
6
21
|
- **真实 Minimal 工具对(realPair)**:首轮"轨迹"由请求 #1 可见的**工具 schema**
|
|
7
22
|
逐字决定——只有与 Minimal 完全相同的 schema 能锚定(参照
|
|
@@ -16,23 +31,20 @@
|
|
|
16
31
|
- 请求 #1 剥离自动注入上下文(技能目录提醒 `skill-catalog`、AGENTS.md 摘要
|
|
17
32
|
`agent-instructions`,用户主动的技能手势不过滤);
|
|
18
33
|
- 会话出现首个**持久**晋升信号(首个 `tool/call` 或 `assistant/message`,
|
|
19
|
-
`promoteOn` 可选 `either`/`tool-call`/`assistant-message`)后进入
|
|
20
|
-
|
|
21
|
-
`
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(bootstrap 工具对 + `compactionTools` 核心工作集),直到出现新的晋升信号;
|
|
34
|
+
`promoteOn` 可选 `either`/`tool-call`/`assistant-message`)后进入
|
|
35
|
+
**resident 阶段**:保留 bootstrap 工具对 + 常驻发现工具(0.7.0 语义:
|
|
36
|
+
只锚定首轮,不减少后续功能;完整目录经 `dev_tool_search` 按需解锁——
|
|
37
|
+
晋升时一次性倒出完整目录会把轨迹拉回 standard-like,anchored-standard
|
|
38
|
+
实测的"晋升后回退"问题),常规注入(技能目录提醒 + AGENTS.md 摘要)
|
|
39
|
+
恢复可见;阶段从持久会话事件推导,**resume / reload 不丢状态**,晋升
|
|
40
|
+
信号**持久**(`compaction/end` 不重置,与 dsh-anchored-standard 一致);
|
|
27
41
|
- `maxTokens > 0` 时可给请求 #1 封顶输出预算(晋升后自动剥离)。
|
|
28
|
-
- **常驻上下文抑制(suppressInjectedContext
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
自己读",而不是每请求 dump 全文(探测项目链 AGENTS.md/CLAUDE.md 与
|
|
35
|
-
`$DSH_HOME/AGENTS.md`,探测失败不注入,绝不抛错)。
|
|
42
|
+
- **常驻上下文抑制(suppressInjectedContext,默认开启)**:整个会话持续
|
|
43
|
+
剥离技能目录提醒 + AGENTS.md 摘要注入——实测晋升后恢复注入会把轨迹拉回
|
|
44
|
+
standard-like("Let me…" 叙述回归),且每请求多耗数千 token。功能可见性
|
|
45
|
+
由常驻发现工具承担:`dev_tool_search` 搜索完整目录并按名解锁、
|
|
46
|
+
`skill_search` / `skill_load` 按需搜索/加载技能说明。设为 `false` 时只剥
|
|
47
|
+
离请求 #1,晋升后恢复常规注入(功能不减少但轨迹回退,opt-in)。
|
|
36
48
|
- **运行时上下文抑制**:对目标 preset 的会话调用 `suppressRuntimeContext()`
|
|
37
49
|
(与极简模式的 `includeRuntimeContext: false` 同一机制),每次模型请求省掉
|
|
38
50
|
"Current runtime context" 快照文本,**零功能损失**。
|
|
@@ -48,9 +60,10 @@
|
|
|
48
60
|
- 挂载真实工具对时按名阴影 `tool:bash` 引导段(与可见工具一致)。
|
|
49
61
|
各处均为 agent 作用域的 prompt 段阴影,仅影响目标会话;**对已恢复/已晋升的
|
|
50
62
|
旧会话同样生效**(不依赖 bootstrap 阶段)。
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
- **工具目录自适应精简(leanByDefault,0.7.0 起为 opt-in)**:默认 `false` =
|
|
64
|
+
工具目录零裁剪,编排类工具族(子代理 / 工作流 / ralph / goal)始终可用。
|
|
65
|
+
设为 `true` 时会话启动按"工具族"隐藏高开销低频工具,只保留核心编码工具
|
|
66
|
+
(bash / 文件读写编辑 / 检索 / jobs / todo / 提问 / 网页搜索 / 技能 / 计划),
|
|
54
67
|
标准模式的工具目录因此逼近极简;PTC 模式系统提示里的 **SDK 参考段同步缩小**。
|
|
55
68
|
- **需求信号自动放行**(会话内单调升级,只升不降,限制请求缓存失效次数):
|
|
56
69
|
- 关键词信号:用户消息命中工具族触发词(如"子代理"、"工作流")→ 放行该族;
|
|
@@ -62,12 +75,12 @@
|
|
|
62
75
|
|
|
63
76
|
| 维度 | 极简模式 | 标准 / PTC 模式 | 本插件做法 |
|
|
64
77
|
|---|---|---|---|
|
|
65
|
-
| 首轮轨迹(决定后续行为风格) | Minimal 真实工具对锚定(5/5) | 任何 standard 系 schema(含 sandboxed bash)落入 standard-like(11/11) | realPair 挂载官方同款插件:请求 #1 的 schema 与 Minimal
|
|
78
|
+
| 首轮轨迹(决定后续行为风格) | Minimal 真实工具对锚定(5/5) | 任何 standard 系 schema(含 sandboxed bash)落入 standard-like(11/11) | realPair 挂载官方同款插件:请求 #1 的 schema 与 Minimal 逐字相同 |
|
|
66
79
|
| 首轮注入提醒 | 无 skill 目录/AGENTS.md 注入 | 自动注入(在场时锚定 0/9) | 请求 #1 剥离 `skill-catalog` + `agent-instructions` |
|
|
67
|
-
| 后置阶段注入 | 永不注入 | 晋升后每请求恢复 ~9KB 目录 + AGENTS.md 全文 |
|
|
80
|
+
| 后置阶段注入 | 永不注入 | 晋升后每请求恢复 ~9KB 目录 + AGENTS.md 全文 | 默认全程剥离(发现工具承担可见性);`suppressInjectedContext=false` opt-in 恢复 |
|
|
68
81
|
| 系统提示语域(persona + 全局引导段) | 一句话 persona,无任何引导段 | identity/source/web-surface 引导段 + 标准 persona("Let me…" 叙述来源) | minimalPrompt 层按名阴影屏蔽 3 个全局段 + 替换极简 persona(对旧会话同样生效) |
|
|
69
82
|
| 运行时上下文快照 | 关闭(`includeRuntimeContext: false`) | 每次请求注入文件策略/审批策略等快照 | 同一机制:`suppressRuntimeContext()` |
|
|
70
|
-
| 模型可见工具目录 | 2 个(bash、str_replace_editor) | ~22 个工具 schema 全部进请求 | 首轮 2 个;晋升后 resident
|
|
83
|
+
| 模型可见工具目录 | 2 个(bash、str_replace_editor) | ~22 个工具 schema 全部进请求 | 首轮 2 个;晋升后 resident(2 + 3 个发现工具,完整目录按需解锁);opt-in lean 时隐藏编排族按需放行 |
|
|
71
84
|
| PTC SDK 参考段 | 无 | 全部工具的类型+描述渲染进提示 | 随目录缩小(同一 `visible` 视图驱动) |
|
|
72
85
|
|
|
73
86
|
## 安装(npm 生态方式)
|
|
@@ -112,12 +125,10 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
|
|
|
112
125
|
| 字段 | 默认值 | 说明 |
|
|
113
126
|
|---|---|---|
|
|
114
127
|
| `enabled` | `true` | 总开关 |
|
|
115
|
-
| `presets` | `["standard", "code"]` | 应用自适应的 preset id |
|
|
128
|
+
| `presets` | `["standard", "code", "cordis"]` | 应用自适应的 preset id |
|
|
116
129
|
| `suppressRuntimeContext` | `true` | 抑制运行时上下文快照(零功能损失) |
|
|
117
|
-
| `suppressInjectedContext` | `true` |
|
|
118
|
-
| `
|
|
119
|
-
| `instructionHint` | `true` | 晋升后一次性注入指令文件提示(0.5.0) |
|
|
120
|
-
| `leanByDefault` | `true` | 会话启动即隐藏编排类工具族 |
|
|
130
|
+
| `suppressInjectedContext` | `true` | 常驻上下文抑制(默认开启):整个会话剥离技能目录/AGENTS.md 注入(发现工具承担可见性);`false` = 只在请求 #1 剥离,晋升后恢复 |
|
|
131
|
+
| `leanByDefault` | `false` | 工具目录精简(opt-in):`true` = 会话启动即隐藏编排类工具族;`false`(默认)= 零裁剪 |
|
|
121
132
|
| `escalateOnKeyword` | `true` | 用户消息命中触发词 → 放行该族 |
|
|
122
133
|
| `escalateOnUnknownTool` | `true` | 工具调用失败(UNKNOWN_TOOL)→ 放行该族 |
|
|
123
134
|
| `coreTools` | 核心编码工具 | 展示用:不进入任何限制族 |
|
|
@@ -134,7 +145,6 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
|
|
|
134
145
|
"tools": ["bash", "str_replace_editor"],
|
|
135
146
|
"promoteOn": "either",
|
|
136
147
|
"suppressedContextSources": ["skill-catalog", "agent-instructions"],
|
|
137
|
-
"compactionTools": ["read", "write", "edit", "glob", "grep", "todo_write", "ask_user_question"],
|
|
138
148
|
"discoveryTools": ["dev_tool_search", "skill_search", "skill_load"],
|
|
139
149
|
"maxTokens": 0
|
|
140
150
|
}
|
|
@@ -147,12 +157,20 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
|
|
|
147
157
|
Windows(无 PTY 后端)时自动降级为"仅收窄目录"并告警。
|
|
148
158
|
- `tools`:请求 #1 可见的工具(真实 Minimal 对)。
|
|
149
159
|
- `promoteOn`:`either`(默认)/ `tool-call` / `assistant-message`。
|
|
150
|
-
- `suppressedContextSources
|
|
151
|
-
- `
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
- `suppressedContextSources`:首轮剥离的自动注入 source.kind;`[]` 关闭剥离。
|
|
161
|
+
- `discoveryTools`:晋升后 resident 目录的常驻发现工具——`dev_tool_search`
|
|
162
|
+
(搜索完整目录并按名解锁,解锁结果下一请求生效且会话内保持)、
|
|
163
|
+
`skill_search` / `skill_load`(无门控,按需搜索/加载技能说明,替代常驻
|
|
164
|
+
~9KB 技能目录)。
|
|
154
165
|
- `maxTokens`:请求 #1 输出预算封顶(0 = 不封顶,opt-in)。
|
|
155
166
|
|
|
167
|
+
> 0.7.0 破坏性变更:移除 `skillDiscovery` / `instructionHint` /
|
|
168
|
+
> `bootstrap.compactionTools` 三个字段(resident 语义下不再需要:
|
|
169
|
+
> `dev_tool_search` 常驻提供按需发现,无压缩回退、无指令提示补偿);
|
|
170
|
+
> `bootstrap.discoveryTools` **恢复**为 resident 目录的常驻发现工具
|
|
171
|
+
> (默认 `dev_tool_search` / `skill_search` / `skill_load`)。已保存的旧配置
|
|
172
|
+
> 会自动忽略被移除字段。
|
|
173
|
+
|
|
156
174
|
`minimalPrompt`(极简提示词层,0.4.0 新增):
|
|
157
175
|
|
|
158
176
|
```json
|
package/client/client.cjs
CHANGED
|
@@ -42,10 +42,6 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
fieldEscalateOnUnknownTool: "PTC 程序调用被隐藏工具报 UNKNOWN_TOOL 时自动放行对应族",
|
|
43
43
|
suppressInjectedContext: "常驻抑制注入上下文",
|
|
44
44
|
fieldSuppressInjectedContext: "整个会话剥离技能目录提醒(skill-catalog)与 AGENTS.md 摘要(agent-instructions),不再在晋升后恢复——替代品是 skill_search/skill_load 按需发现与一次性 instruction-hint(参照 dsh-anchored-standard:注入在场即使后置也扰动轨迹)",
|
|
45
|
-
skillDiscovery: "技能按需发现",
|
|
46
|
-
fieldSkillDiscovery: "晋升后常驻 skill_search / skill_load 两个小工具,替代 ~9KB 的 <available_skills> 目录注入",
|
|
47
|
-
instructionHint: "指令文件一次性提示",
|
|
48
|
-
fieldInstructionHint: "晋升后注入一次“指令文件存在,需要时自读”的短提示(探测项目链 AGENTS.md/CLAUDE.md 与 $DSH_HOME/AGENTS.md),替代全文摘要",
|
|
49
45
|
families: "工具族(高级)",
|
|
50
46
|
fieldFamilies: "JSON:{ 族名: { enabled, tools: [工具名], keywords: [触发词] } }。保存后热生效。",
|
|
51
47
|
coreTools: "核心工具(永不隐藏)",
|
|
@@ -59,11 +55,9 @@ window.__ModuleLoader__.load({
|
|
|
59
55
|
bootstrapPromoteOn: "晋升触发",
|
|
60
56
|
fieldBootstrapPromoteOn: "either(默认):首个 tool/call 或 assistant/message 先到者晋升;tool-call / assistant-message 可单独指定",
|
|
61
57
|
bootstrapSuppressed: "剥离的注入上下文",
|
|
58
|
+
bootstrapDiscovery: "常驻发现工具(晋升后)",
|
|
62
59
|
fieldBootstrapSuppressed: "请求 #1 剥离的自动注入 source.kind(技能目录提醒 skill-catalog、AGENTS.md 摘要 agent-instructions);逗号分隔,留空=不剥离",
|
|
63
|
-
|
|
64
|
-
fieldBootstrapCompactionTools: "compaction/end 之后、再次晋升前额外可用的工具(核心工作集)。逗号分隔。",
|
|
65
|
-
bootstrapDiscoveryTools: "晋升后常驻发现工具",
|
|
66
|
-
fieldBootstrapDiscoveryTools: "晋升后保留在 resident 目录里的按需发现工具(如 dev_tool_search);逗号分隔。",
|
|
60
|
+
fieldBootstrapDiscovery: "晋升后常驻目录的发现工具(dev_tool_search 搜索完整目录并按名解锁;skill_search / skill_load 按需加载技能);逗号分隔",
|
|
67
61
|
bootstrapMaxTokens: "首轮输出预算封顶(token)",
|
|
68
62
|
fieldBootstrapMaxTokens: "请求 #1 的 maxTokens 封顶,晋升后自动剥离;0 = 不封顶(opt-in)",
|
|
69
63
|
minimalPromptEnabled: "极简提示词层(语域锚定)",
|
|
@@ -97,10 +91,6 @@ window.__ModuleLoader__.load({
|
|
|
97
91
|
fieldEscalateOnUnknownTool: "A PTC program calling a hidden tool (UNKNOWN_TOOL) re-enables that family",
|
|
98
92
|
suppressInjectedContext: "Always suppress injected context",
|
|
99
93
|
fieldSuppressInjectedContext: "Strip the skill-catalog reminder and the AGENTS.md digest from every request, not just request #1 — replaced by on-demand skill_search/skill_load and a one-shot instruction hint (per dsh-anchored-standard: the injections perturb the trajectory even after promotion)",
|
|
100
|
-
skillDiscovery: "On-demand skill discovery",
|
|
101
|
-
fieldSkillDiscovery: "Keep skill_search / skill_load resident after promotion as the replacement for the ~9KB <available_skills> catalog injection",
|
|
102
|
-
instructionHint: "One-shot instruction hint",
|
|
103
|
-
fieldInstructionHint: "After promotion, inject ONCE a short hint that instruction files exist and should be read when relevant (probes the project chain AGENTS.md/CLAUDE.md and $DSH_HOME/AGENTS.md), instead of dumping their content",
|
|
104
94
|
families: "Tool families (advanced)",
|
|
105
95
|
fieldFamilies: "JSON: { familyId: { enabled, tools: [names], keywords: [triggers] } }. Hot-applied on save.",
|
|
106
96
|
coreTools: "Core tools (never hidden)",
|
|
@@ -114,11 +104,9 @@ window.__ModuleLoader__.load({
|
|
|
114
104
|
bootstrapPromoteOn: "Promotion trigger",
|
|
115
105
|
fieldBootstrapPromoteOn: "either (default): first tool/call or assistant/message promotes; tool-call / assistant-message select one",
|
|
116
106
|
bootstrapSuppressed: "Suppressed injected context",
|
|
107
|
+
bootstrapDiscovery: "Resident discovery tools (post-promotion)",
|
|
117
108
|
fieldBootstrapSuppressed: "Auto-injected source.kinds stripped on request #1 (skill-catalog reminder, agent-instructions digest); comma-separated, empty = strip nothing",
|
|
118
|
-
|
|
119
|
-
fieldBootstrapCompactionTools: "Extra tools available after compaction/end until a new promotion signal (core work set). Comma-separated.",
|
|
120
|
-
bootstrapDiscoveryTools: "Resident discovery tools",
|
|
121
|
-
fieldBootstrapDiscoveryTools: "On-demand discovery tools kept in the resident catalog after promotion (e.g. dev_tool_search). Comma-separated.",
|
|
109
|
+
fieldBootstrapDiscovery: "Resident discovery tools after promotion (dev_tool_search searches the full catalog and unlocks by name; skill_search / skill_load load skills on demand); comma-separated",
|
|
122
110
|
bootstrapMaxTokens: "First-request output cap (tokens)",
|
|
123
111
|
fieldBootstrapMaxTokens: "maxTokens cap for request #1, stripped after promotion; 0 = no cap (opt-in)",
|
|
124
112
|
minimalPromptEnabled: "Minimal prompt layer (register anchor)",
|
|
@@ -265,21 +253,21 @@ window.__ModuleLoader__.load({
|
|
|
265
253
|
react.createElement("p", { className: "ap_hint" }, t("hint")),
|
|
266
254
|
react.createElement(BoolField, {
|
|
267
255
|
label: t("enabled"),
|
|
268
|
-
value: draft === null ?
|
|
256
|
+
value: draft === null ? true : draft.enabled,
|
|
269
257
|
disabled: draft === null,
|
|
270
258
|
onChange: (v) => setDraft({ ...draft, enabled: v })
|
|
271
259
|
}),
|
|
272
260
|
react.createElement(TextField, {
|
|
273
261
|
label: t("presets"),
|
|
274
262
|
hint: t("fieldPresets"),
|
|
275
|
-
value: draft === null ? "" : (draft.presets || []).join(", "),
|
|
263
|
+
value: draft === null ? "standard, code, cordis" : (draft.presets || []).join(", "),
|
|
276
264
|
disabled: draft === null,
|
|
277
265
|
onChange: (v) => setDraft({ ...draft, presets: v.split(/[,,\s]+/).filter((x) => x.length > 0) })
|
|
278
266
|
}),
|
|
279
267
|
react.createElement(BoolField, {
|
|
280
268
|
label: t("suppressRuntimeContext"),
|
|
281
269
|
hint: t("fieldSuppressRuntimeContext"),
|
|
282
|
-
value: draft === null ?
|
|
270
|
+
value: draft === null ? true : draft.suppressRuntimeContext,
|
|
283
271
|
disabled: draft === null,
|
|
284
272
|
onChange: (v) => setDraft({ ...draft, suppressRuntimeContext: v })
|
|
285
273
|
}),
|
|
@@ -293,14 +281,14 @@ window.__ModuleLoader__.load({
|
|
|
293
281
|
react.createElement(BoolField, {
|
|
294
282
|
label: t("escalateOnKeyword"),
|
|
295
283
|
hint: t("fieldEscalateOnKeyword"),
|
|
296
|
-
value: draft === null ?
|
|
284
|
+
value: draft === null ? true : draft.escalateOnKeyword,
|
|
297
285
|
disabled: draft === null,
|
|
298
286
|
onChange: (v) => setDraft({ ...draft, escalateOnKeyword: v })
|
|
299
287
|
}),
|
|
300
288
|
react.createElement(BoolField, {
|
|
301
289
|
label: t("escalateOnUnknownTool"),
|
|
302
290
|
hint: t("fieldEscalateOnUnknownTool"),
|
|
303
|
-
value: draft === null ?
|
|
291
|
+
value: draft === null ? true : draft.escalateOnUnknownTool,
|
|
304
292
|
disabled: draft === null,
|
|
305
293
|
onChange: (v) => setDraft({ ...draft, escalateOnUnknownTool: v })
|
|
306
294
|
}),
|
|
@@ -311,31 +299,17 @@ window.__ModuleLoader__.load({
|
|
|
311
299
|
disabled: draft === null,
|
|
312
300
|
onChange: (v) => setDraft({ ...draft, suppressInjectedContext: v })
|
|
313
301
|
}),
|
|
314
|
-
react.createElement(BoolField, {
|
|
315
|
-
label: t("skillDiscovery"),
|
|
316
|
-
hint: t("fieldSkillDiscovery"),
|
|
317
|
-
value: draft === null ? false : draft.skillDiscovery,
|
|
318
|
-
disabled: draft === null,
|
|
319
|
-
onChange: (v) => setDraft({ ...draft, skillDiscovery: v })
|
|
320
|
-
}),
|
|
321
|
-
react.createElement(BoolField, {
|
|
322
|
-
label: t("instructionHint"),
|
|
323
|
-
hint: t("fieldInstructionHint"),
|
|
324
|
-
value: draft === null ? false : draft.instructionHint,
|
|
325
|
-
disabled: draft === null,
|
|
326
|
-
onChange: (v) => setDraft({ ...draft, instructionHint: v })
|
|
327
|
-
}),
|
|
328
302
|
react.createElement(BoolField, {
|
|
329
303
|
label: t("bootstrapEnabled"),
|
|
330
304
|
hint: t("fieldBootstrapEnabled"),
|
|
331
|
-
value: draft === null ?
|
|
305
|
+
value: draft === null ? true : !!draft.bootstrap && draft.bootstrap.enabled,
|
|
332
306
|
disabled: draft === null,
|
|
333
307
|
onChange: (v) => setDraft({ ...draft, bootstrap: { ...(draft.bootstrap || {}), enabled: v } })
|
|
334
308
|
}),
|
|
335
309
|
react.createElement(BoolField, {
|
|
336
310
|
label: t("bootstrapRealPair"),
|
|
337
311
|
hint: t("fieldBootstrapRealPair"),
|
|
338
|
-
value: draft === null ?
|
|
312
|
+
value: draft === null ? true : !!draft.bootstrap && draft.bootstrap.realPair,
|
|
339
313
|
disabled: draft === null || (draft.bootstrap && draft.bootstrap.enabled === false),
|
|
340
314
|
onChange: (v) => setDraft({ ...draft, bootstrap: { ...(draft.bootstrap || {}), realPair: v } })
|
|
341
315
|
}),
|
|
@@ -361,15 +335,8 @@ window.__ModuleLoader__.load({
|
|
|
361
335
|
onChange: (v) => setDraft({ ...draft, bootstrap: { ...(draft.bootstrap || {}), suppressedContextSources: v.split(/[,,\s]+/).filter((x) => x.length > 0) } })
|
|
362
336
|
}),
|
|
363
337
|
react.createElement(TextField, {
|
|
364
|
-
label: t("
|
|
365
|
-
hint: t("
|
|
366
|
-
value: draft === null ? "" : ((draft.bootstrap && draft.bootstrap.compactionTools) || []).join(", "),
|
|
367
|
-
disabled: draft === null || (draft.bootstrap && draft.bootstrap.enabled === false),
|
|
368
|
-
onChange: (v) => setDraft({ ...draft, bootstrap: { ...(draft.bootstrap || {}), compactionTools: v.split(/[,,\s]+/).filter((x) => x.length > 0) } })
|
|
369
|
-
}),
|
|
370
|
-
react.createElement(TextField, {
|
|
371
|
-
label: t("bootstrapDiscoveryTools"),
|
|
372
|
-
hint: t("fieldBootstrapDiscoveryTools"),
|
|
338
|
+
label: t("bootstrapDiscovery"),
|
|
339
|
+
hint: t("fieldBootstrapDiscovery"),
|
|
373
340
|
value: draft === null ? "" : ((draft.bootstrap && draft.bootstrap.discoveryTools) || []).join(", "),
|
|
374
341
|
disabled: draft === null || (draft.bootstrap && draft.bootstrap.enabled === false),
|
|
375
342
|
onChange: (v) => setDraft({ ...draft, bootstrap: { ...(draft.bootstrap || {}), discoveryTools: v.split(/[,,\s]+/).filter((x) => x.length > 0) } })
|
|
@@ -387,14 +354,14 @@ window.__ModuleLoader__.load({
|
|
|
387
354
|
react.createElement(BoolField, {
|
|
388
355
|
label: t("minimalPromptEnabled"),
|
|
389
356
|
hint: t("fieldMinimalPromptEnabled"),
|
|
390
|
-
value: draft === null ?
|
|
357
|
+
value: draft === null ? true : (draft.minimalPrompt && draft.minimalPrompt.enabled === true),
|
|
391
358
|
disabled: draft === null,
|
|
392
359
|
onChange: (v) => setDraft({ ...draft, minimalPrompt: { ...(draft.minimalPrompt || {}), enabled: v } })
|
|
393
360
|
}),
|
|
394
361
|
react.createElement(BoolField, {
|
|
395
362
|
label: t("minimalPromptSuppressSections"),
|
|
396
363
|
hint: t("fieldMinimalPromptSuppressSections"),
|
|
397
|
-
value: draft === null ?
|
|
364
|
+
value: draft === null ? true : (draft.minimalPrompt && draft.minimalPrompt.suppressSections === true),
|
|
398
365
|
disabled: draft === null || (draft.minimalPrompt && draft.minimalPrompt.enabled === false),
|
|
399
366
|
onChange: (v) => setDraft({ ...draft, minimalPrompt: { ...(draft.minimalPrompt || {}), suppressSections: v } })
|
|
400
367
|
}),
|
|
@@ -501,6 +468,9 @@ window.__ModuleLoader__.load({
|
|
|
501
468
|
});
|
|
502
469
|
ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
|
|
503
470
|
name: "settings.plugin.item",
|
|
471
|
+
// keyed slot:key 为卡片编辑的设置 namespace(与 host 侧 serviceKey 一致),
|
|
472
|
+
// 设置页按 key 与宿主服务的 namespace 配对 dispatch。
|
|
473
|
+
key: "adaptivePerfConfig",
|
|
504
474
|
id: "adaptive-perf",
|
|
505
475
|
order: 40,
|
|
506
476
|
locale: NS,
|
package/cordis.patch.yml
CHANGED
|
@@ -10,27 +10,36 @@
|
|
|
10
10
|
name: '@chaoset/adaptive-perf'
|
|
11
11
|
config:
|
|
12
12
|
enabled: true
|
|
13
|
-
presets: [standard, code]
|
|
13
|
+
presets: [standard, code, cordis]
|
|
14
14
|
suppressRuntimeContext: true
|
|
15
|
-
leanByDefault:
|
|
15
|
+
leanByDefault: false
|
|
16
16
|
escalateOnKeyword: true
|
|
17
17
|
escalateOnUnknownTool: true
|
|
18
|
-
#
|
|
19
|
-
# 提醒与 AGENTS.md
|
|
20
|
-
#
|
|
18
|
+
# 常驻上下文抑制(0.7.0 默认关闭):true = 整个会话剥离技能目录
|
|
19
|
+
# 提醒与 AGENTS.md 摘要注入;false(默认)= 只在首轮剥离,晋升后
|
|
20
|
+
# 常驻剥离注入(0.7.0 默认开启):技能目录/AGENTS.md 摘要的可见性
|
|
21
|
+
# 由 resident 发现工具 dev_tool_search / skill_search / skill_load
|
|
22
|
+
# 承担——实测晋升后恢复注入会把轨迹拉回 standard-like。
|
|
21
23
|
suppressInjectedContext: true
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
24
|
+
# 首轮锚定、晋升后 resident 目录(参照 dsh-anchored-standard issue
|
|
25
|
+
# #11):请求 #1 只暴露真实 Minimal 工具对(realPair 挂载官方 minimal
|
|
26
|
+
# preset 同款插件,schema 与 Minimal 逐字相同)并剥离自动注入上下文;
|
|
27
|
+
# 首个持久信号后保留 bootstrap 工具对 + 常驻发现工具(完整目录经
|
|
28
|
+
# dev_tool_search 按需解锁,避免晋升时一次性倒出完整目录把轨迹拉回
|
|
29
|
+
# standard-like),常规注入恢复可见。
|
|
28
30
|
bootstrap:
|
|
29
31
|
enabled: true
|
|
30
32
|
realPair: true
|
|
31
33
|
tools: [bash, str_replace_editor]
|
|
32
34
|
promoteOn: either
|
|
33
35
|
suppressedContextSources: [skill-catalog, agent-instructions]
|
|
34
|
-
compactionTools: [read, write, edit, glob, grep, todo_write, ask_user_question]
|
|
35
36
|
discoveryTools: [dev_tool_search, skill_search, skill_load]
|
|
36
37
|
maxTokens: 0
|
|
38
|
+
# 极简提示词层(参照 dsh-anchored-standard 的完整锚定条件):按名
|
|
39
|
+
# 阴影屏蔽 harness:identity / harness:source / app:web-surface 三个
|
|
40
|
+
# 全局引导段,并替换 deployment:persona 为极简语域 persona——只收窄
|
|
41
|
+
# 工具 schema 不够,首轮轨迹由 Minimal 的完整 system prompt 决定。
|
|
42
|
+
minimalPrompt:
|
|
43
|
+
enabled: true
|
|
44
|
+
persona: You are a helpful software engineer assistant.
|
|
45
|
+
suppressSections: true
|
package/lib/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* adaptive-perf — 极简性能自适应插件(@chaoset/adaptive-perf)
|
|
3
3
|
*
|
|
4
|
-
* 目标:让「标准模式(standard
|
|
5
|
-
* (minimal
|
|
4
|
+
* 目标:让「标准模式(standard)」「PTC 模式(code)」与「创造模式
|
|
5
|
+
* (cordis)」达到与「极简模式(minimal)」相同级别的高性能。极简模式快
|
|
6
|
+
* 的原因(对源码的对照结论):
|
|
6
7
|
* 1. persona 配置了 includeRuntimeContext: false —— 每次模型请求不注入
|
|
7
8
|
* "Current runtime context" 快照(文件策略 / 审批策略等动态段);
|
|
8
9
|
* 2. 工具目录极小(只有 bash + str_replace_editor)—— 每个请求的
|
|
@@ -30,23 +31,20 @@
|
|
|
30
31
|
* (技能目录提醒 skill-catalog、AGENTS.md 摘要 agent-instructions;
|
|
31
32
|
* 用户主动的技能手势不过滤);首个**持久**晋升信号(首个 tool/call 或
|
|
32
33
|
* assistant/message,promoteOn 可选 either/tool-call/assistant-message)
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* E. 工具目录自适应精简:会话启动时按"工具族"默认隐藏高开销低频工具
|
|
48
|
-
* (子代理 / 工作流 / ralph / goal 等编排类),只保留核心编码工具,
|
|
49
|
-
* 随后根据两类信号在会话内"单调升级"放行对应工具族(只升不降):
|
|
34
|
+
* 后**完整恢复**该模式的全部工具目录与常规上下文注入(0.7.0,
|
|
35
|
+
* dsh-anchored-standard 语义:只锚定首轮,不减少后续功能);阶段从
|
|
36
|
+
* 持久会话事件推导,resume/reload 不丢状态,晋升信号持久(compaction
|
|
37
|
+
* 不重置);bootstrap.maxTokens(可选)给请求 #1 封顶输出预算,晋升后
|
|
38
|
+
* 剥离。
|
|
39
|
+
* D. 常驻上下文抑制(suppressInjectedContext,0.5.0 新增,0.7.0 默认
|
|
40
|
+
* 开启):true = 整个会话剥离技能目录提醒与 AGENTS.md 摘要注入(功能
|
|
41
|
+
* 可见性由常驻发现工具 dev_tool_search / skill_search / skill_load
|
|
42
|
+
* 承担——实测晋升后恢复注入会把轨迹拉回 standard-like);false =
|
|
43
|
+
* 只剥离首轮,晋升后恢复常规注入(opt-in)。
|
|
44
|
+
* E. 工具目录自适应精简(leanByDefault,0.7.0 默认关闭的 opt-in):开启时
|
|
45
|
+
* 按"工具族"隐藏高开销低频工具(子代理 / 工作流 / ralph / goal 等编排
|
|
46
|
+
* 类),只保留核心编码工具,随后根据两类信号在会话内"单调升级"放行
|
|
47
|
+
* 对应工具族(只升不降):
|
|
50
48
|
* - 关键词信号:用户消息命中某工具族的触发词(如"子代理")→ 放行该族;
|
|
51
49
|
* - 失败信号:PTC 的 run_code 程序调用被隐藏工具报 UNKNOWN_TOOL
|
|
52
50
|
* (tools/result 失败文本含工具名)→ 放行该族,下次程序即可调用。
|
|
@@ -57,17 +55,15 @@
|
|
|
57
55
|
* - agent.ctx.systemPrompt.section() 按名阴影 prompt 段(persona/引导段)
|
|
58
56
|
* - agent.ctx.tools.schemas(agent) 读取该 agent 当前可见工具(限制交集)
|
|
59
57
|
* - agent.ctx.tools.restrict({ deny }) 按 agent 作用域裁剪继承工具
|
|
60
|
-
* - agent.ctx.tools.register() 在 agent 作用域注册工具(按名阴影继承工具,
|
|
61
|
-
* 自身层注册不受 restrict 影响——view() 对 own layer 豁免)
|
|
62
58
|
* - agent.ctx.plugin() 挂载官方 minimal preset 同款插件(持久 bash /
|
|
63
59
|
* str_replace_editor / 其依赖的 terminals 与本地 fs)
|
|
64
|
-
* - agent/pre-step(waterfall)
|
|
60
|
+
* - agent/pre-step(waterfall) 首轮剥离自动注入上下文
|
|
65
61
|
* - agent/request(waterfall) 首轮输出预算封顶
|
|
66
|
-
* - session/event
|
|
62
|
+
* - session/event 增量喂入持久事件(晋升信号)
|
|
67
63
|
* - agent/created、agent/disposed、agent/inbox/inserted、tools/result 事件
|
|
68
64
|
* 监听器都注册在 host 根作用域(与 dsh-agent-presets 自身同款用法),事件按
|
|
69
|
-
* scope
|
|
70
|
-
*
|
|
65
|
+
* scope 过滤分发,子作用域派发的事件根监听器可收到;waterfall 均以 prepend
|
|
66
|
+
* 注册保证"最外层"(剥离是最后一道变换)。
|
|
71
67
|
*
|
|
72
68
|
* 配置:cordis.patch.yml 的 config(安装默认)与
|
|
73
69
|
* ~/.dsh/plugins/adaptive-perf/config.json(设置页 UI,权威)合并,
|
|
@@ -89,6 +85,14 @@ try {
|
|
|
89
85
|
console.warn('adaptive-perf: settings gateway unavailable: ' + (error && error.message || error));
|
|
90
86
|
}
|
|
91
87
|
|
|
88
|
+
// 宿主 settings 体系的 schema 库(dsh-settings 0.1.0-rc.7+ 用 schemastery)。
|
|
89
|
+
// DSH profile 的 hoisted node_modules 直接解析;仓库测试环境无此包时为
|
|
90
|
+
// null,settings namespace 注册段静默跳过(fail-safe)。
|
|
91
|
+
let Schema = null;
|
|
92
|
+
try {
|
|
93
|
+
({ default: Schema } = await import('@deepseek-ai/schemastery'));
|
|
94
|
+
} catch {}
|
|
95
|
+
|
|
92
96
|
export const name = 'adaptive-perf';
|
|
93
97
|
|
|
94
98
|
/**
|
|
@@ -100,34 +104,41 @@ export const name = 'adaptive-perf';
|
|
|
100
104
|
*
|
|
101
105
|
* bootstrap:首轮锚定(参照 dsh-anchored-standard 的实测结论)。请求 #1 只暴露
|
|
102
106
|
* bootstrap.tools(真实 Minimal 工具对),剥离 suppressedContextSources 列出的
|
|
103
|
-
* 自动注入上下文;首个持久晋升信号(promoteOn
|
|
104
|
-
*
|
|
105
|
-
*
|
|
107
|
+
* 自动注入上下文;首个持久晋升信号(promoteOn)后完整恢复全部工具与常规注入
|
|
108
|
+
* (晋升是持久信号,compaction/end 不重置);maxTokens>0 时给请求 #1 封顶输出
|
|
109
|
+
* 预算(晋升后剥离)。
|
|
106
110
|
*/
|
|
107
111
|
export const DEFAULT_CONFIG = {
|
|
108
112
|
/** 总开关:关闭后插件对会话不产生任何影响。 */
|
|
109
113
|
enabled: true,
|
|
110
114
|
/** 应用自适应的 agent preset 列表(composedPreset 返回值)。 */
|
|
111
|
-
presets: ['standard', 'code'],
|
|
115
|
+
presets: ['standard', 'code', 'cordis'],
|
|
116
|
+
/**
|
|
117
|
+
* 默认策略(0.7.0):首轮锚定、晋升后 resident 目录(dsh-anchored-standard
|
|
118
|
+
* 语义)。非极简 preset 的会话:请求 #1 按极简条件组装(真实 Minimal 工具
|
|
119
|
+
* 对 + 极简 persona + 屏蔽全局引导段 + 剥离自动注入 + 抑制运行时快照)以
|
|
120
|
+
* 锚定极简轨迹;首个持久晋升信号后进入 resident 阶段——保留 bootstrap 工具
|
|
121
|
+
* 对 + 常驻发现工具(dev_tool_search / skill_search / skill_load),完整
|
|
122
|
+
* 目录经 dev_tool_search 按需解锁(避免晋升时一次性倒出完整目录把轨迹拉回
|
|
123
|
+
* standard-like,anchored-standard 实测的"晋升后回退"问题),常规上下文
|
|
124
|
+
* 注入恢复可见——功能不减少。工具目录默认零裁剪(leanByDefault 关闭,
|
|
125
|
+
* 编排类工具族始终可用);需要时可在设置页按需开启。
|
|
126
|
+
*/
|
|
112
127
|
/** 抑制运行时上下文快照(等同极简模式的 includeRuntimeContext: false)。 */
|
|
113
128
|
suppressRuntimeContext: true,
|
|
114
129
|
/** 会话启动时默认隐藏编排类工具族(核心编码工具始终保留)。 */
|
|
115
|
-
leanByDefault:
|
|
130
|
+
leanByDefault: false,
|
|
116
131
|
/** 用户消息命中工具族触发词时自动放行该族(会话内单调)。 */
|
|
117
132
|
escalateOnKeyword: true,
|
|
118
133
|
/** 工具调用失败(UNKNOWN_TOOL 文本含隐藏工具名)时自动放行该族。 */
|
|
119
134
|
escalateOnUnknownTool: true,
|
|
120
135
|
/**
|
|
121
136
|
* 常驻上下文抑制(0.5.0):true = 整个会话剥离自动注入上下文
|
|
122
|
-
* (skill-catalog 技能目录提醒、agent-instructions AGENTS.md
|
|
123
|
-
*
|
|
124
|
-
* 与
|
|
137
|
+
* (skill-catalog 技能目录提醒、agent-instructions AGENTS.md 摘要);
|
|
138
|
+
* false = 只在 bootstrap 未晋升阶段剥离,晋升后恢复注入(0.7.0 默认,
|
|
139
|
+
* 与 dsh-anchored-standard 一致:只锚定首轮、不减少后续功能)。
|
|
125
140
|
*/
|
|
126
141
|
suppressInjectedContext: true,
|
|
127
|
-
/** 晋升后常驻 skill_search / skill_load 两个按需技能发现工具。 */
|
|
128
|
-
skillDiscovery: true,
|
|
129
|
-
/** 晋升后注入一次"指令文件存在,需要时自读"的短提示(替代全文注入)。 */
|
|
130
|
-
instructionHint: true,
|
|
131
142
|
/** 核心编码工具(不进入任何限制族;此处仅作文档展示)。 */
|
|
132
143
|
coreTools: [
|
|
133
144
|
'bash', 'read', 'write', 'edit', 'glob', 'grep', 'read_image',
|
|
@@ -174,9 +185,7 @@ export const DEFAULT_CONFIG = {
|
|
|
174
185
|
promoteOn: 'either',
|
|
175
186
|
/** 请求 #1 剥离的自动注入上下文 source.kind;[] 关闭剥离。 */
|
|
176
187
|
suppressedContextSources: ['skill-catalog', 'agent-instructions'],
|
|
177
|
-
/**
|
|
178
|
-
compactionTools: ['read', 'write', 'edit', 'glob', 'grep', 'todo_write', 'ask_user_question'],
|
|
179
|
-
/** 晋升后常驻的按需发现工具(dsh-anchored-standard 的 resident discovery set)。 */
|
|
188
|
+
/** 晋升后 resident 目录的常驻发现工具(按需解锁完整目录)。 */
|
|
180
189
|
discoveryTools: ['dev_tool_search', 'skill_search', 'skill_load'],
|
|
181
190
|
/** 请求 #1 的输出预算封顶(token);0 = 不封顶(opt-in)。 */
|
|
182
191
|
maxTokens: 0,
|
|
@@ -260,8 +269,7 @@ export function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
|
|
|
260
269
|
tools: stringList(rawBootstrap.tools, db.tools),
|
|
261
270
|
promoteOn,
|
|
262
271
|
suppressedContextSources: stringListOrEmpty(rawBootstrap.suppressedContextSources, db.suppressedContextSources),
|
|
263
|
-
|
|
264
|
-
discoveryTools: stringListOrEmpty(rawBootstrap.discoveryTools, db.discoveryTools),
|
|
272
|
+
discoveryTools: stringList(rawBootstrap.discoveryTools, db.discoveryTools),
|
|
265
273
|
maxTokens,
|
|
266
274
|
};
|
|
267
275
|
const rawMP = raw.minimalPrompt !== null && typeof raw.minimalPrompt === 'object' && !Array.isArray(raw.minimalPrompt)
|
|
@@ -281,8 +289,6 @@ export function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
|
|
|
281
289
|
escalateOnKeyword: boolValue(merged.escalateOnKeyword, defaults.escalateOnKeyword),
|
|
282
290
|
escalateOnUnknownTool: boolValue(merged.escalateOnUnknownTool, defaults.escalateOnUnknownTool),
|
|
283
291
|
suppressInjectedContext: boolValue(merged.suppressInjectedContext, defaults.suppressInjectedContext),
|
|
284
|
-
skillDiscovery: boolValue(merged.skillDiscovery, defaults.skillDiscovery),
|
|
285
|
-
instructionHint: boolValue(merged.instructionHint, defaults.instructionHint),
|
|
286
292
|
coreTools: stringList(merged.coreTools, defaults.coreTools),
|
|
287
293
|
families,
|
|
288
294
|
bootstrap,
|
|
@@ -296,7 +302,7 @@ export function validateConfig(partial) {
|
|
|
296
302
|
throw new TypeError('adaptive-perf config must be a plain object');
|
|
297
303
|
}
|
|
298
304
|
const boolFields = ['enabled', 'suppressRuntimeContext', 'leanByDefault', 'escalateOnKeyword', 'escalateOnUnknownTool',
|
|
299
|
-
'suppressInjectedContext'
|
|
305
|
+
'suppressInjectedContext'];
|
|
300
306
|
for (const key of boolFields) {
|
|
301
307
|
if (partial[key] !== void 0 && typeof partial[key] !== 'boolean') {
|
|
302
308
|
throw new TypeError(`adaptive-perf config field "${key}" must be a boolean`);
|
|
@@ -342,7 +348,7 @@ export function validateConfig(partial) {
|
|
|
342
348
|
if (b.maxTokens !== void 0 && !(Number.isSafeInteger(b.maxTokens) && b.maxTokens >= 0)) {
|
|
343
349
|
throw new TypeError('adaptive-perf config field "bootstrap.maxTokens" must be a non-negative safe integer');
|
|
344
350
|
}
|
|
345
|
-
for (const key of ['tools', 'suppressedContextSources', '
|
|
351
|
+
for (const key of ['tools', 'suppressedContextSources', 'discoveryTools']) {
|
|
346
352
|
if (b[key] !== void 0 && !(Array.isArray(b[key]) && b[key].every((v) => typeof v === 'string'))) {
|
|
347
353
|
throw new TypeError(`adaptive-perf config field "bootstrap.${key}" must be an array of strings`);
|
|
348
354
|
}
|
|
@@ -519,89 +525,7 @@ export function mountRealPair(agent, mounts, warn) {
|
|
|
519
525
|
return disposers;
|
|
520
526
|
}
|
|
521
527
|
|
|
522
|
-
// ── instruction-hint(0.5.0,参照 dsh-anchored-standard)──────────────────
|
|
523
|
-
|
|
524
|
-
/** 项目链候选指令文件名(探测顺序)与用户全局候选。 */
|
|
525
|
-
export const INSTRUCTION_PROJECT_CANDIDATES = ['AGENTS.md', 'CLAUDE.md', 'AGENTS.local.md', 'CLAUDE.local.md'];
|
|
526
|
-
export const INSTRUCTION_USER_CANDIDATE = 'AGENTS.md';
|
|
527
528
|
|
|
528
|
-
/** 平台无关的路径拼接(与参考实现一致)。 */
|
|
529
|
-
export function joinPath(dir, segment) {
|
|
530
|
-
if (dir.endsWith('/') || dir.endsWith('\\')) return dir + segment;
|
|
531
|
-
const sep = dir.includes('\\') ? '\\' : '/';
|
|
532
|
-
return dir + sep + segment;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
/** 绝对 POSIX/Windows 路径的父目录。 */
|
|
536
|
-
export function parentPath(path) {
|
|
537
|
-
const idx = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
|
538
|
-
if (idx <= 0) return path;
|
|
539
|
-
const parent = path.slice(0, idx);
|
|
540
|
-
return parent.length === 0 ? path : parent;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
/** 从 cwd 向上找项目根(含 .git/.hg/.svn 标记的最近祖先)。 */
|
|
544
|
-
export async function findProjectRoot(fs, cwd, signal) {
|
|
545
|
-
let current = cwd;
|
|
546
|
-
for (;;) {
|
|
547
|
-
for (const marker of ['.git', '.hg', '.svn']) {
|
|
548
|
-
try {
|
|
549
|
-
const target = await fs.resolve(joinPath(current, marker), { cwd, signal });
|
|
550
|
-
const info = await fs.stat(target, signal);
|
|
551
|
-
if (info !== void 0) return current;
|
|
552
|
-
} catch {
|
|
553
|
-
// 探测失败 = 标记不存在,继续。
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
const parent = parentPath(current);
|
|
557
|
-
if (parent === current || parent.length === 0) return cwd;
|
|
558
|
-
current = parent;
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
/** 列出某目录下存在的候选指令文件。 */
|
|
563
|
-
export async function presentInDir(fs, dir, candidates, signal) {
|
|
564
|
-
const found = [];
|
|
565
|
-
for (const candidate of candidates) {
|
|
566
|
-
try {
|
|
567
|
-
const target = await fs.resolve(joinPath(dir, candidate), { cwd: dir, signal });
|
|
568
|
-
const info = await fs.stat(target, signal);
|
|
569
|
-
if (info !== void 0 && info.type === 'file') found.push(candidate);
|
|
570
|
-
} catch {
|
|
571
|
-
// 不存在/不可读——跳过。
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
return found;
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* 探测会话的指令文件(项目链 + $DSH_HOME/AGENTS.md)。任何失败都返回
|
|
579
|
-
* null/空集——提示注入绝不能让会话受损。
|
|
580
|
-
* @returns { projectFiles, root, userGlobalFiles } 或 null(无 fs / 无 cwd)。
|
|
581
|
-
*/
|
|
582
|
-
export async function probeInstructionFiles(fs, cwd, dshHome, signal) {
|
|
583
|
-
if (fs === null || fs === void 0 || typeof cwd !== 'string' || cwd.length === 0) return null;
|
|
584
|
-
const projectFiles = [];
|
|
585
|
-
let root = cwd;
|
|
586
|
-
try {
|
|
587
|
-
root = await findProjectRoot(fs, cwd, signal);
|
|
588
|
-
projectFiles.push(...await presentInDir(fs, root, INSTRUCTION_PROJECT_CANDIDATES, signal));
|
|
589
|
-
} catch {
|
|
590
|
-
// 项目链探测失败——忽略,仍报告用户全局文件。
|
|
591
|
-
}
|
|
592
|
-
const userGlobalFiles = [];
|
|
593
|
-
if (typeof dshHome === 'string' && dshHome.length > 0) {
|
|
594
|
-
try {
|
|
595
|
-
userGlobalFiles.push(...await presentInDir(fs, dshHome, [INSTRUCTION_USER_CANDIDATE], signal));
|
|
596
|
-
} catch {
|
|
597
|
-
// 不可读——忽略。
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
if (projectFiles.length === 0 && userGlobalFiles.length === 0) return null;
|
|
601
|
-
return { projectFiles, root, userGlobalFiles };
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
// ── 按需发现工具(resident discovery set,参照 dsh-anchored-standard)─────
|
|
605
529
|
|
|
606
530
|
/**
|
|
607
531
|
* 从 agent/pre-step 的 decision.messages 剥离指定 source.kind 的消息。
|
|
@@ -616,7 +540,17 @@ export function stripSuppressedMessages(messages, suppressedSources) {
|
|
|
616
540
|
return kept.length === messages.length ? messages : kept;
|
|
617
541
|
}
|
|
618
542
|
|
|
543
|
+
// ── bootstrap 锚定(参照 dsh-anchored-standard 的实测结论)──────────────
|
|
544
|
+
|
|
545
|
+
/** promoteOn 配置 → 晋升的持久事件类型集合。 */
|
|
546
|
+
export const PROMOTE_EVENTS = {
|
|
547
|
+
'tool-call': ['tool/call'],
|
|
548
|
+
'assistant-message': ['assistant/message'],
|
|
549
|
+
either: ['tool/call', 'assistant/message'],
|
|
550
|
+
};
|
|
551
|
+
|
|
619
552
|
/**
|
|
553
|
+
/**
|
|
620
554
|
* dev_tool_search:搜索完整可见目录并按名解锁工具(解锁结果下一请求生效,
|
|
621
555
|
* 经 syncBootstrap 重算 deny 集;解锁记录写入 a.unlocked,resume-safe 由
|
|
622
556
|
* loadUnlockedFromEvents 从持久事件恢复)。
|
|
@@ -828,49 +762,34 @@ export function extractSkillBody(skill) {
|
|
|
828
762
|
return '';
|
|
829
763
|
}
|
|
830
764
|
|
|
831
|
-
// ── bootstrap 锚定(参照 dsh-anchored-standard 的实测结论)──────────────
|
|
832
|
-
|
|
833
|
-
/** promoteOn 配置 → 晋升的持久事件类型集合。 */
|
|
834
|
-
export const PROMOTE_EVENTS = {
|
|
835
|
-
'tool-call': ['tool/call'],
|
|
836
|
-
'assistant-message': ['assistant/message'],
|
|
837
|
-
either: ['tool/call', 'assistant/message'],
|
|
838
|
-
};
|
|
839
765
|
|
|
840
766
|
/**
|
|
841
|
-
* 扫描一段持久会话日志,推导 {
|
|
842
|
-
*
|
|
843
|
-
*
|
|
767
|
+
* 扫描一段持久会话日志,推导 { promoted } 阶段。
|
|
768
|
+
* 晋升是持久信号(与 dsh-anchored-standard 一致):首次 promoteOn 事件后
|
|
769
|
+
* 恒为已晋升,resume/reload 不丢状态,compaction 不重置。
|
|
844
770
|
*/
|
|
845
771
|
export function scanPhase(events, promoteEvents) {
|
|
846
|
-
let boundary = -1;
|
|
847
772
|
let promoted = false;
|
|
848
773
|
if (Array.isArray(events)) {
|
|
849
774
|
for (const event of events) {
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
promoted = false;
|
|
854
|
-
continue;
|
|
775
|
+
if (promoteEvents.includes(event?.type)) {
|
|
776
|
+
promoted = true;
|
|
777
|
+
break;
|
|
855
778
|
}
|
|
856
|
-
if (promoteEvents.includes(event?.type) && seq > boundary) promoted = true;
|
|
857
779
|
}
|
|
858
780
|
}
|
|
859
|
-
return {
|
|
781
|
+
return { promoted };
|
|
860
782
|
}
|
|
861
783
|
|
|
862
784
|
/** 增量喂入一个持久事件(仅更新已存在条目;冷会话由 scanPhase 全量推导)。 */
|
|
863
785
|
export function observePhase(state, sessionId, event) {
|
|
864
786
|
const entry = state.get(sessionId);
|
|
865
787
|
if (entry === void 0) return;
|
|
866
|
-
const seq = typeof event?.seq === 'number' ? event.seq : 0;
|
|
867
|
-
if (event?.type === 'compaction/end') {
|
|
868
|
-
state.set(sessionId, { boundary: seq, promoted: false, promoteEvents: entry.promoteEvents });
|
|
869
|
-
return;
|
|
870
|
-
}
|
|
871
788
|
if (entry.promoted) return;
|
|
872
|
-
const promoteEvents = entry.promoteEvents
|
|
873
|
-
|
|
789
|
+
const promoteEvents = Array.isArray(entry.promoteEvents)
|
|
790
|
+
? entry.promoteEvents
|
|
791
|
+
: PROMOTE_EVENTS[entry.promoteOn] ?? [];
|
|
792
|
+
if (promoteEvents.includes(event?.type)) {
|
|
874
793
|
state.set(sessionId, { ...entry, promoted: true });
|
|
875
794
|
}
|
|
876
795
|
}
|
|
@@ -913,6 +832,34 @@ export function applyBootstrapBudget(config, promoted, maxTokens) {
|
|
|
913
832
|
|
|
914
833
|
// ── host 插件 ────────────────────────────────────────────────────────────
|
|
915
834
|
|
|
835
|
+
/** 把配置 namespace 注册进宿主 settings 体系(dsh-settings 0.1.0-rc.7+)。
|
|
836
|
+
* 设置页 describe() 只枚举 settings.register 注册过的 namespace;本插件
|
|
837
|
+
* 的卡片读写不经过宿主 settings 文档,注册只为让卡片出现在设置页。
|
|
838
|
+
* options.base 必传当前生效配置快照:宿主 resolve = schema(mergeLayers(base,
|
|
839
|
+
* section)),schema 无默认值时 base 缺失会让 describe() 的 value 为
|
|
840
|
+
* undefined,而设置页 wire 校验要求 value 非空(invalid_type: nonoptional),
|
|
841
|
+
* 一项失败会拖垮整个设置页。本插件的 any() schema 完全依赖 base。
|
|
842
|
+
* fail-safe(schema 库/服务缺失静默跳过)+ 幂等(重复注册忽略)。
|
|
843
|
+
* 导出以便测试注入 Schema stub。 */
|
|
844
|
+
export function registerSettingsNamespace(ctx, ns, schemaLib, buildSchema, options) {
|
|
845
|
+
if (schemaLib === null || schemaLib === undefined) return false;
|
|
846
|
+
if (ctx === null || typeof ctx !== 'object' || typeof ctx.inject !== 'function') return false;
|
|
847
|
+
try {
|
|
848
|
+
ctx.inject(['settings'], (settingsCtx) => {
|
|
849
|
+
try {
|
|
850
|
+
settingsCtx.settings.register(ns, buildSchema(schemaLib), options);
|
|
851
|
+
} catch (error) {
|
|
852
|
+
const message = String(error && error.message || error);
|
|
853
|
+
if (!message.includes('already registered')) throw error;
|
|
854
|
+
}
|
|
855
|
+
});
|
|
856
|
+
return true;
|
|
857
|
+
} catch (error) {
|
|
858
|
+
try { ctx.logger?.warn?.(`adaptive-perf: settings namespace registration skipped: ${error && error.message || error}`); } catch {}
|
|
859
|
+
return false;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
916
863
|
export async function apply(ctx, config, options = {}) {
|
|
917
864
|
// fail-safe:初始化失败只记录,绝不让本插件拖垮 harness(host 层挂载时
|
|
918
865
|
// entry 异常会导致进程启动失败)。
|
|
@@ -936,7 +883,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
936
883
|
|
|
937
884
|
/** 真实 Minimal 工具对模块(0.5.0)。加载失败 = null = 降级旧行为。
|
|
938
885
|
* options.realPairModules 仅供测试注入。 */
|
|
939
|
-
|
|
886
|
+
let realPairModules = options.realPairModules === void 0
|
|
940
887
|
? (cfg.bootstrap.enabled && cfg.bootstrap.realPair ? await loadRealPairModules() : null)
|
|
941
888
|
: options.realPairModules;
|
|
942
889
|
/** 是否已尝试加载过真实工具对模块(避免配置热更新时反复重试)。 */
|
|
@@ -947,9 +894,6 @@ export async function apply(ctx, config, options = {}) {
|
|
|
947
894
|
} catch {}
|
|
948
895
|
}
|
|
949
896
|
|
|
950
|
-
/** 已注入过 instruction-hint 的会话(内存去重,与参考实现一致)。 */
|
|
951
|
-
const hintedSessions = new Set();
|
|
952
|
-
|
|
953
897
|
/**
|
|
954
898
|
* 配置热更新把 realPair 从关闭切到开启时补加载官方模块(初始未启用时
|
|
955
899
|
* 不加载)。加载成功后对已接管会话补挂。
|
|
@@ -1138,6 +1082,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1138
1082
|
a.realPairActive = a.realPairDisposers.length > 0;
|
|
1139
1083
|
}
|
|
1140
1084
|
|
|
1085
|
+
|
|
1141
1086
|
/**
|
|
1142
1087
|
* 给目标 agent 注册 on-demand 发现工具(dsh-anchored-standard 的 resident
|
|
1143
1088
|
* discovery pattern):晋升后常驻 bootstrap.discoveryTools 列出的工具——
|
|
@@ -1189,10 +1134,10 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1189
1134
|
catalogHint: 'This session starts with a minimal resident set: bash, str_replace_editor, skill_search, skill_load. Everything else is unlocked on demand through this tool.',
|
|
1190
1135
|
});
|
|
1191
1136
|
}
|
|
1192
|
-
if (name === 'skill_search'
|
|
1137
|
+
if (name === 'skill_search') {
|
|
1193
1138
|
return createSkillSearch({ skillsOf: () => ctx.get('skills') });
|
|
1194
1139
|
}
|
|
1195
|
-
if (name === 'skill_load'
|
|
1140
|
+
if (name === 'skill_load') {
|
|
1196
1141
|
return createSkillLoad({ skillsOf: () => ctx.get('skills') });
|
|
1197
1142
|
}
|
|
1198
1143
|
return null;
|
|
@@ -1250,10 +1195,6 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1250
1195
|
try { disposer(); } catch {}
|
|
1251
1196
|
}
|
|
1252
1197
|
a.promptDisposers = [];
|
|
1253
|
-
for (const disposer of a.toolDisposers.values()) {
|
|
1254
|
-
try { disposer(); } catch {}
|
|
1255
|
-
}
|
|
1256
|
-
a.toolDisposers.clear();
|
|
1257
1198
|
for (const dispose of a.realPairDisposers) {
|
|
1258
1199
|
try { dispose(); } catch {}
|
|
1259
1200
|
}
|
|
@@ -1261,6 +1202,10 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1261
1202
|
if (a.bootstrapDisposer !== null) {
|
|
1262
1203
|
try { a.bootstrapDisposer(); } catch {}
|
|
1263
1204
|
}
|
|
1205
|
+
for (const disposer of a.toolDisposers.values()) {
|
|
1206
|
+
try { disposer(); } catch {}
|
|
1207
|
+
}
|
|
1208
|
+
a.toolDisposers.clear();
|
|
1264
1209
|
for (const disposer of a.familyDisposers.values()) {
|
|
1265
1210
|
try { disposer(); } catch {}
|
|
1266
1211
|
}
|
|
@@ -1312,10 +1257,11 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1312
1257
|
});
|
|
1313
1258
|
|
|
1314
1259
|
// ── 首轮锚定(bootstrap,参照 dsh-anchored-standard 的实测结论)─────
|
|
1315
|
-
// 阶段状态:sessionId -> {
|
|
1260
|
+
// 阶段状态:sessionId -> { promoted, promoteEvents }。冷会话在
|
|
1316
1261
|
// handleAgent 时全量扫描持久日志(resume 安全),此后 session/event 增量
|
|
1317
|
-
//
|
|
1318
|
-
// prepend 注册,保证本插件是"最外层"
|
|
1262
|
+
// 更新。晋升是持久信号(compaction 不重置)。子代理(delegationDepth > 0)
|
|
1263
|
+
// 恒为已晋升。所有 waterfall 用 prepend 注册,保证本插件是"最外层"
|
|
1264
|
+
// 变换(剥离是最后一道)。
|
|
1319
1265
|
const bootstrapState = new Map();
|
|
1320
1266
|
let bootstrapWarned = false;
|
|
1321
1267
|
const warnBootstrapOnce = (message) => {
|
|
@@ -1326,14 +1272,14 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1326
1272
|
|
|
1327
1273
|
function bootstrapPhaseOf(agent) {
|
|
1328
1274
|
const session = agent !== null && typeof agent === 'object' ? agent.session : void 0;
|
|
1329
|
-
if (session === void 0 || typeof session.id !== 'string') return {
|
|
1275
|
+
if (session === void 0 || typeof session.id !== 'string') return { promoted: true };
|
|
1330
1276
|
// 子代理首轮即可用工具(与参考实现一致)。
|
|
1331
|
-
if ((session.header?.delegationDepth ?? 0) > 0) return {
|
|
1277
|
+
if ((session.header?.delegationDepth ?? 0) > 0) return { promoted: true };
|
|
1332
1278
|
let entry = bootstrapState.get(session.id);
|
|
1333
1279
|
if (entry === void 0) {
|
|
1334
1280
|
entry = {
|
|
1335
1281
|
...scanPhase(session.events, PROMOTE_EVENTS[cfg.bootstrap.promoteOn]),
|
|
1336
|
-
|
|
1282
|
+
promoteOn: cfg.bootstrap.promoteOn,
|
|
1337
1283
|
};
|
|
1338
1284
|
bootstrapState.set(session.id, entry);
|
|
1339
1285
|
}
|
|
@@ -1348,12 +1294,13 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1348
1294
|
}
|
|
1349
1295
|
|
|
1350
1296
|
/**
|
|
1351
|
-
* bootstrap
|
|
1352
|
-
* - 未晋升:bootstrap 工具对 + PTC 的直接调用工具(run_code
|
|
1353
|
-
*
|
|
1354
|
-
*
|
|
1355
|
-
*
|
|
1356
|
-
*
|
|
1297
|
+
* bootstrap 阶段的保留工具集(0.7.0:首轮锚定、晋升后 resident 目录):
|
|
1298
|
+
* - 未晋升:bootstrap 工具对 + PTC 的直接调用工具(run_code);
|
|
1299
|
+
* - 已晋升:bootstrap 工具对 + 常驻发现工具(dev_tool_search /
|
|
1300
|
+
* skill_search / skill_load)+ dev_tool_search 已解锁工具
|
|
1301
|
+
* (dsh-anchored-standard 的 resident catalog:晋升时一次性倒出
|
|
1302
|
+
* 完整 Standard 目录会把轨迹拉回 standard-like——晋升后回退问题,
|
|
1303
|
+
* 因此重型工具保持一次 dev_tool_search 即可取用)。
|
|
1357
1304
|
*
|
|
1358
1305
|
* 注意:不能靠过滤 system-prompt/assemble 的 assembly.tools 来实现——
|
|
1359
1306
|
* PTC 模式下 assembly.tools 只有 [run_code],Minimal 工具对不在其中,
|
|
@@ -1371,14 +1318,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1371
1318
|
if (phase.promoted) {
|
|
1372
1319
|
for (const name of cfg.bootstrap.discoveryTools) keep.add(name);
|
|
1373
1320
|
for (const name of a.unlocked) keep.add(name);
|
|
1374
|
-
for (const [id, family] of Object.entries(cfg.families)) {
|
|
1375
|
-
if (a.escalated.has(id)) {
|
|
1376
|
-
for (const name of familyDeny(a, family)) keep.add(name);
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
return keep;
|
|
1380
1321
|
}
|
|
1381
|
-
if (phase.boundary >= 0) for (const name of cfg.bootstrap.compactionTools) keep.add(name);
|
|
1382
1322
|
return keep;
|
|
1383
1323
|
}
|
|
1384
1324
|
|
|
@@ -1386,7 +1326,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1386
1326
|
function syncBootstrap(a) {
|
|
1387
1327
|
const phase = bootstrapPhaseOf(a.agent);
|
|
1388
1328
|
const keep = bootstrapKeepSet(a);
|
|
1389
|
-
// discovery 工具只在晋升后的 resident 阶段注册;bootstrap
|
|
1329
|
+
// discovery 工具只在晋升后的 resident 阶段注册;bootstrap 阶段隐藏。
|
|
1390
1330
|
syncDiscoveryTools(a, keep !== null && phase.promoted);
|
|
1391
1331
|
const key = keep === null ? null : [...keep].sort().join(',');
|
|
1392
1332
|
if (key === a.bootstrapKey) return;
|
|
@@ -1415,65 +1355,29 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1415
1355
|
const a = agents.get(session.id);
|
|
1416
1356
|
if (a !== void 0) {
|
|
1417
1357
|
const after = bootstrapState.get(session.id);
|
|
1418
|
-
if (before === void 0 || after === void 0 || before.promoted !== after.promoted
|
|
1358
|
+
if (before === void 0 || after === void 0 || before.promoted !== after.promoted) {
|
|
1419
1359
|
syncBootstrap(a);
|
|
1420
1360
|
}
|
|
1421
1361
|
}
|
|
1422
1362
|
});
|
|
1423
1363
|
|
|
1424
|
-
//
|
|
1425
|
-
//
|
|
1426
|
-
//
|
|
1427
|
-
//
|
|
1428
|
-
//
|
|
1429
|
-
ctx.on('agent/pre-step', async ({ agent
|
|
1364
|
+
// 上下文剥离(首轮锚定的注入部分)。prepend + 根作用域注册保证是
|
|
1365
|
+
// 最后一道变换(后注册的注入者无法再补回)。
|
|
1366
|
+
// 剥离条件:bootstrap 未晋升(首轮剥离)或常驻抑制开启
|
|
1367
|
+
// (suppressInjectedContext,0.7.0 默认开启:晋升后注入保持剥离,
|
|
1368
|
+
// 由常驻发现工具承担功能可见性)。
|
|
1369
|
+
ctx.on('agent/pre-step', async ({ agent }, next) => {
|
|
1430
1370
|
const decision = await next();
|
|
1431
1371
|
if (decision === null || typeof decision !== 'object' || decision.kind === 'reject') return decision;
|
|
1432
1372
|
try {
|
|
1433
1373
|
if (!cfg.enabled || !isTarget(agent)) return decision;
|
|
1434
1374
|
const phase = bootstrapPhaseOf(agent);
|
|
1435
1375
|
const suppressed = new Set(cfg.bootstrap.suppressedContextSources);
|
|
1436
|
-
|
|
1437
|
-
// 剥离条件:bootstrap 未晋升(旧行为)或常驻抑制开启(0.5.0 默认:
|
|
1438
|
-
// 整个会话不再注入技能目录与 AGENTS.md 摘要,替代品是按需发现工具)。
|
|
1376
|
+
if (suppressed.size === 0) return decision;
|
|
1439
1377
|
const stripActive = (cfg.bootstrap.enabled && !phase.promoted) || cfg.suppressInjectedContext;
|
|
1440
|
-
if (
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
}
|
|
1444
|
-
// instruction-hint:晋升后一次性注入"指令文件存在,需要时自读"。
|
|
1445
|
-
if (cfg.bootstrap.enabled && cfg.instructionHint && phase.promoted && Array.isArray(out.messages)) {
|
|
1446
|
-
const session = agent.session;
|
|
1447
|
-
if (session !== null && typeof session === 'object' && typeof session.id === 'string' && !hintedSessions.has(session.id)) {
|
|
1448
|
-
hintedSessions.add(session.id);
|
|
1449
|
-
const fs = ctx.get('fs');
|
|
1450
|
-
const dshHome = process.env.DSH_HOME?.trim() || `${process.env.HOME || process.env.USERPROFILE || ''}/.dsh`;
|
|
1451
|
-
const probe = await probeInstructionFiles(fs, session.header?.cwd, dshHome, signal);
|
|
1452
|
-
if (probe !== null) {
|
|
1453
|
-
const sections = [];
|
|
1454
|
-
if (probe.projectFiles.length > 0) {
|
|
1455
|
-
sections.push(`Workspace instruction files exist: ${probe.projectFiles.join(', ')} (project root: ${probe.root}).`);
|
|
1456
|
-
}
|
|
1457
|
-
if (probe.userGlobalFiles.length > 0) {
|
|
1458
|
-
sections.push(`A user-global instruction file exists: ${INSTRUCTION_USER_CANDIDATE}.`);
|
|
1459
|
-
}
|
|
1460
|
-
const text = [
|
|
1461
|
-
...sections,
|
|
1462
|
-
'Do NOT assume their content. When a task touches this workspace, read the relevant instruction files first and follow them.',
|
|
1463
|
-
].join(' ');
|
|
1464
|
-
out = {
|
|
1465
|
-
...out,
|
|
1466
|
-
messages: [...out.messages, {
|
|
1467
|
-
id: `instruction-hint-${session.id}`,
|
|
1468
|
-
role: 'user',
|
|
1469
|
-
content: [{ type: 'text', text }],
|
|
1470
|
-
source: { kind: 'instruction-hint', form: 'hint' },
|
|
1471
|
-
}],
|
|
1472
|
-
};
|
|
1473
|
-
}
|
|
1474
|
-
}
|
|
1475
|
-
}
|
|
1476
|
-
return out;
|
|
1378
|
+
if (!stripActive || !Array.isArray(decision.messages)) return decision;
|
|
1379
|
+
const kept = stripSuppressedMessages(decision.messages, suppressed);
|
|
1380
|
+
return kept === decision.messages ? decision : { ...decision, messages: kept };
|
|
1477
1381
|
} catch (error) {
|
|
1478
1382
|
// 过滤失败绝不吞掉用户的上下文:原样放行。
|
|
1479
1383
|
warnBootstrapOnce(`bootstrap context filter failed, keeping injected context: ${String((error && error.message) || error)}`);
|
|
@@ -1547,6 +1451,15 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1547
1451
|
ctx.plugin(PluginConfigGateway, { store, serviceKey: 'adaptivePerfConfig' });
|
|
1548
1452
|
}
|
|
1549
1453
|
|
|
1454
|
+
// 注册进宿主 settings 体系(可见性):设置页用 settings.describe() 枚举
|
|
1455
|
+
// registrations,未注册的 namespace 即使卡片带正确的 key 也不渲染。
|
|
1456
|
+
// 卡片的实际读写仍走上面的 config gateway(config.json 权威、热更新)。
|
|
1457
|
+
// 配置为多层嵌套结构(families/bootstrap/minimalPrompt…),schema 用 any
|
|
1458
|
+
// 透传——describe 元数据只服务于可见性,卡片不读取它。any() 无默认值,
|
|
1459
|
+
// 必须传 base(当前生效配置快照),否则 describe() 的 value 为 undefined,
|
|
1460
|
+
// 设置页 wire 校验(nonoptional)会整体失败。
|
|
1461
|
+
registerSettingsNamespace(ctx, 'adaptivePerfConfig', Schema, (z) => z.any(), { base: cfg });
|
|
1462
|
+
|
|
1550
1463
|
// 卸载清理:释放全部会话副作用与监听。
|
|
1551
1464
|
ctx.effect(() => () => {
|
|
1552
1465
|
for (const agentId of [...agents.keys()]) releaseAgent(agentId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaoset/adaptive-perf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "DSH host plugin: bring standard / PTC presets to minimal-mode-level performance — real Minimal tool pair, permanent injected-context suppression with on-demand discovery, runtime-context suppression, and adaptive tool-catalog trimming with demand-driven escalation",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"lib",
|
|
9
9
|
"client",
|
|
10
10
|
"cordis.patch.yml",
|
|
11
|
-
"README.md"
|
|
11
|
+
"README.md",
|
|
12
|
+
"CHANGELOG.md",
|
|
13
|
+
"LICENSE"
|
|
12
14
|
],
|
|
13
15
|
"exports": {
|
|
14
16
|
".": "./lib/index.mjs",
|
|
@@ -31,19 +33,16 @@
|
|
|
31
33
|
}
|
|
32
34
|
},
|
|
33
35
|
"optionalDependencies": {
|
|
34
|
-
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.
|
|
35
|
-
"@deepseek-ai/dsh-terminal": "^0.1.0-rc.
|
|
36
|
-
"@deepseek-ai/dsh-terminal-bash": "^0.1.0-rc.
|
|
37
|
-
"@deepseek-ai/dsh-tool-bash-persistent": "^0.1.0-rc.
|
|
38
|
-
"@deepseek-ai/dsh-fs-local": "^0.1.0-rc.
|
|
39
|
-
"@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.0-rc.
|
|
40
|
-
},
|
|
41
|
-
"engines": {
|
|
42
|
-
"node": ">=22.19.0"
|
|
36
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.0-rc.7",
|
|
37
|
+
"@deepseek-ai/dsh-terminal": "^0.1.0-rc.7",
|
|
38
|
+
"@deepseek-ai/dsh-terminal-bash": "^0.1.0-rc.7",
|
|
39
|
+
"@deepseek-ai/dsh-tool-bash-persistent": "^0.1.0-rc.7",
|
|
40
|
+
"@deepseek-ai/dsh-fs-local": "^0.1.0-rc.7",
|
|
41
|
+
"@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.0-rc.7"
|
|
43
42
|
},
|
|
44
43
|
"scripts": {
|
|
45
|
-
"test": "
|
|
46
|
-
"prepublishOnly": "
|
|
44
|
+
"test": "bun ../../scripts/test.mjs",
|
|
45
|
+
"prepublishOnly": "bun ../../scripts/test.mjs"
|
|
47
46
|
},
|
|
48
47
|
"repository": {
|
|
49
48
|
"type": "git",
|