@chaoset/adaptive-perf 0.6.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 -42
- package/client/client.cjs +15 -48
- package/cordis.patch.yml +21 -12
- package/lib/index.mjs +100 -241
- 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,15 +1,22 @@
|
|
|
1
1
|
# @chaoset/adaptive-perf — DSH 极简性能自适应插件
|
|
2
2
|
|
|
3
|
-
> **默认策略(0.
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
>
|
|
9
|
-
>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
+
而是**动态自适应**:
|
|
13
20
|
|
|
14
21
|
- **真实 Minimal 工具对(realPair)**:首轮"轨迹"由请求 #1 可见的**工具 schema**
|
|
15
22
|
逐字决定——只有与 Minimal 完全相同的 schema 能锚定(参照
|
|
@@ -24,23 +31,20 @@
|
|
|
24
31
|
- 请求 #1 剥离自动注入上下文(技能目录提醒 `skill-catalog`、AGENTS.md 摘要
|
|
25
32
|
`agent-instructions`,用户主动的技能手势不过滤);
|
|
26
33
|
- 会话出现首个**持久**晋升信号(首个 `tool/call` 或 `assistant/message`,
|
|
27
|
-
`promoteOn` 可选 `either`/`tool-call`/`assistant-message`)后进入
|
|
28
|
-
|
|
29
|
-
`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
(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 一致);
|
|
35
41
|
- `maxTokens > 0` 时可给请求 #1 封顶输出预算(晋升后自动剥离)。
|
|
36
|
-
- **常驻上下文抑制(suppressInjectedContext
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
自己读",而不是每请求 dump 全文(探测项目链 AGENTS.md/CLAUDE.md 与
|
|
43
|
-
`$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)。
|
|
44
48
|
- **运行时上下文抑制**:对目标 preset 的会话调用 `suppressRuntimeContext()`
|
|
45
49
|
(与极简模式的 `includeRuntimeContext: false` 同一机制),每次模型请求省掉
|
|
46
50
|
"Current runtime context" 快照文本,**零功能损失**。
|
|
@@ -56,9 +60,10 @@
|
|
|
56
60
|
- 挂载真实工具对时按名阴影 `tool:bash` 引导段(与可见工具一致)。
|
|
57
61
|
各处均为 agent 作用域的 prompt 段阴影,仅影响目标会话;**对已恢复/已晋升的
|
|
58
62
|
旧会话同样生效**(不依赖 bootstrap 阶段)。
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
- **工具目录自适应精简(leanByDefault,0.7.0 起为 opt-in)**:默认 `false` =
|
|
64
|
+
工具目录零裁剪,编排类工具族(子代理 / 工作流 / ralph / goal)始终可用。
|
|
65
|
+
设为 `true` 时会话启动按"工具族"隐藏高开销低频工具,只保留核心编码工具
|
|
66
|
+
(bash / 文件读写编辑 / 检索 / jobs / todo / 提问 / 网页搜索 / 技能 / 计划),
|
|
62
67
|
标准模式的工具目录因此逼近极简;PTC 模式系统提示里的 **SDK 参考段同步缩小**。
|
|
63
68
|
- **需求信号自动放行**(会话内单调升级,只升不降,限制请求缓存失效次数):
|
|
64
69
|
- 关键词信号:用户消息命中工具族触发词(如"子代理"、"工作流")→ 放行该族;
|
|
@@ -70,12 +75,12 @@
|
|
|
70
75
|
|
|
71
76
|
| 维度 | 极简模式 | 标准 / PTC 模式 | 本插件做法 |
|
|
72
77
|
|---|---|---|---|
|
|
73
|
-
| 首轮轨迹(决定后续行为风格) | 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 逐字相同 |
|
|
74
79
|
| 首轮注入提醒 | 无 skill 目录/AGENTS.md 注入 | 自动注入(在场时锚定 0/9) | 请求 #1 剥离 `skill-catalog` + `agent-instructions` |
|
|
75
|
-
| 后置阶段注入 | 永不注入 | 晋升后每请求恢复 ~9KB 目录 + AGENTS.md 全文 |
|
|
80
|
+
| 后置阶段注入 | 永不注入 | 晋升后每请求恢复 ~9KB 目录 + AGENTS.md 全文 | 默认全程剥离(发现工具承担可见性);`suppressInjectedContext=false` opt-in 恢复 |
|
|
76
81
|
| 系统提示语域(persona + 全局引导段) | 一句话 persona,无任何引导段 | identity/source/web-surface 引导段 + 标准 persona("Let me…" 叙述来源) | minimalPrompt 层按名阴影屏蔽 3 个全局段 + 替换极简 persona(对旧会话同样生效) |
|
|
77
82
|
| 运行时上下文快照 | 关闭(`includeRuntimeContext: false`) | 每次请求注入文件策略/审批策略等快照 | 同一机制:`suppressRuntimeContext()` |
|
|
78
|
-
| 模型可见工具目录 | 2 个(bash、str_replace_editor) | ~22 个工具 schema 全部进请求 | 首轮 2 个;晋升后 resident
|
|
83
|
+
| 模型可见工具目录 | 2 个(bash、str_replace_editor) | ~22 个工具 schema 全部进请求 | 首轮 2 个;晋升后 resident(2 + 3 个发现工具,完整目录按需解锁);opt-in lean 时隐藏编排族按需放行 |
|
|
79
84
|
| PTC SDK 参考段 | 无 | 全部工具的类型+描述渲染进提示 | 随目录缩小(同一 `visible` 视图驱动) |
|
|
80
85
|
|
|
81
86
|
## 安装(npm 生态方式)
|
|
@@ -120,12 +125,10 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
|
|
|
120
125
|
| 字段 | 默认值 | 说明 |
|
|
121
126
|
|---|---|---|
|
|
122
127
|
| `enabled` | `true` | 总开关 |
|
|
123
|
-
| `presets` | `["standard", "code"]` | 应用自适应的 preset id |
|
|
128
|
+
| `presets` | `["standard", "code", "cordis"]` | 应用自适应的 preset id |
|
|
124
129
|
| `suppressRuntimeContext` | `true` | 抑制运行时上下文快照(零功能损失) |
|
|
125
|
-
| `suppressInjectedContext` | `true` |
|
|
126
|
-
| `
|
|
127
|
-
| `instructionHint` | `true` | 晋升后一次性注入指令文件提示(0.5.0) |
|
|
128
|
-
| `leanByDefault` | `true` | 会话启动即隐藏编排类工具族 |
|
|
130
|
+
| `suppressInjectedContext` | `true` | 常驻上下文抑制(默认开启):整个会话剥离技能目录/AGENTS.md 注入(发现工具承担可见性);`false` = 只在请求 #1 剥离,晋升后恢复 |
|
|
131
|
+
| `leanByDefault` | `false` | 工具目录精简(opt-in):`true` = 会话启动即隐藏编排类工具族;`false`(默认)= 零裁剪 |
|
|
129
132
|
| `escalateOnKeyword` | `true` | 用户消息命中触发词 → 放行该族 |
|
|
130
133
|
| `escalateOnUnknownTool` | `true` | 工具调用失败(UNKNOWN_TOOL)→ 放行该族 |
|
|
131
134
|
| `coreTools` | 核心编码工具 | 展示用:不进入任何限制族 |
|
|
@@ -142,7 +145,6 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
|
|
|
142
145
|
"tools": ["bash", "str_replace_editor"],
|
|
143
146
|
"promoteOn": "either",
|
|
144
147
|
"suppressedContextSources": ["skill-catalog", "agent-instructions"],
|
|
145
|
-
"compactionTools": ["read", "write", "edit", "glob", "grep", "todo_write", "ask_user_question"],
|
|
146
148
|
"discoveryTools": ["dev_tool_search", "skill_search", "skill_load"],
|
|
147
149
|
"maxTokens": 0
|
|
148
150
|
}
|
|
@@ -155,12 +157,20 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
|
|
|
155
157
|
Windows(无 PTY 后端)时自动降级为"仅收窄目录"并告警。
|
|
156
158
|
- `tools`:请求 #1 可见的工具(真实 Minimal 对)。
|
|
157
159
|
- `promoteOn`:`either`(默认)/ `tool-call` / `assistant-message`。
|
|
158
|
-
- `suppressedContextSources
|
|
159
|
-
- `
|
|
160
|
-
|
|
161
|
-
|
|
160
|
+
- `suppressedContextSources`:首轮剥离的自动注入 source.kind;`[]` 关闭剥离。
|
|
161
|
+
- `discoveryTools`:晋升后 resident 目录的常驻发现工具——`dev_tool_search`
|
|
162
|
+
(搜索完整目录并按名解锁,解锁结果下一请求生效且会话内保持)、
|
|
163
|
+
`skill_search` / `skill_load`(无门控,按需搜索/加载技能说明,替代常驻
|
|
164
|
+
~9KB 技能目录)。
|
|
162
165
|
- `maxTokens`:请求 #1 输出预算封顶(0 = 不封顶,opt-in)。
|
|
163
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
|
+
|
|
164
174
|
`minimalPrompt`(极简提示词层,0.4.0 新增):
|
|
165
175
|
|
|
166
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
|
}),
|
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,权威)合并,
|
|
@@ -108,26 +104,28 @@ export const name = 'adaptive-perf';
|
|
|
108
104
|
*
|
|
109
105
|
* bootstrap:首轮锚定(参照 dsh-anchored-standard 的实测结论)。请求 #1 只暴露
|
|
110
106
|
* bootstrap.tools(真实 Minimal 工具对),剥离 suppressedContextSources 列出的
|
|
111
|
-
* 自动注入上下文;首个持久晋升信号(promoteOn
|
|
112
|
-
*
|
|
113
|
-
*
|
|
107
|
+
* 自动注入上下文;首个持久晋升信号(promoteOn)后完整恢复全部工具与常规注入
|
|
108
|
+
* (晋升是持久信号,compaction/end 不重置);maxTokens>0 时给请求 #1 封顶输出
|
|
109
|
+
* 预算(晋升后剥离)。
|
|
114
110
|
*/
|
|
115
111
|
export const DEFAULT_CONFIG = {
|
|
116
112
|
/** 总开关:关闭后插件对会话不产生任何影响。 */
|
|
117
113
|
enabled: true,
|
|
118
114
|
/** 应用自适应的 agent preset 列表(composedPreset 返回值)。 */
|
|
119
|
-
presets: ['standard', 'code'],
|
|
115
|
+
presets: ['standard', 'code', 'cordis'],
|
|
120
116
|
/**
|
|
121
|
-
* 默认策略(0.
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
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
|
+
* 编排类工具族始终可用);需要时可在设置页按需开启。
|
|
128
126
|
*/
|
|
129
127
|
/** 抑制运行时上下文快照(等同极简模式的 includeRuntimeContext: false)。 */
|
|
130
|
-
suppressRuntimeContext:
|
|
128
|
+
suppressRuntimeContext: true,
|
|
131
129
|
/** 会话启动时默认隐藏编排类工具族(核心编码工具始终保留)。 */
|
|
132
130
|
leanByDefault: false,
|
|
133
131
|
/** 用户消息命中工具族触发词时自动放行该族(会话内单调)。 */
|
|
@@ -136,15 +134,11 @@ export const DEFAULT_CONFIG = {
|
|
|
136
134
|
escalateOnUnknownTool: true,
|
|
137
135
|
/**
|
|
138
136
|
* 常驻上下文抑制(0.5.0):true = 整个会话剥离自动注入上下文
|
|
139
|
-
* (skill-catalog 技能目录提醒、agent-instructions AGENTS.md
|
|
140
|
-
*
|
|
141
|
-
* 与
|
|
137
|
+
* (skill-catalog 技能目录提醒、agent-instructions AGENTS.md 摘要);
|
|
138
|
+
* false = 只在 bootstrap 未晋升阶段剥离,晋升后恢复注入(0.7.0 默认,
|
|
139
|
+
* 与 dsh-anchored-standard 一致:只锚定首轮、不减少后续功能)。
|
|
142
140
|
*/
|
|
143
|
-
suppressInjectedContext:
|
|
144
|
-
/** 晋升后常驻 skill_search / skill_load 两个按需技能发现工具。 */
|
|
145
|
-
skillDiscovery: true,
|
|
146
|
-
/** 晋升后注入一次"指令文件存在,需要时自读"的短提示(替代全文注入)。 */
|
|
147
|
-
instructionHint: true,
|
|
141
|
+
suppressInjectedContext: true,
|
|
148
142
|
/** 核心编码工具(不进入任何限制族;此处仅作文档展示)。 */
|
|
149
143
|
coreTools: [
|
|
150
144
|
'bash', 'read', 'write', 'edit', 'glob', 'grep', 'read_image',
|
|
@@ -177,7 +171,7 @@ export const DEFAULT_CONFIG = {
|
|
|
177
171
|
/** 首轮锚定(参照 dsh-anchored-standard)。 */
|
|
178
172
|
bootstrap: {
|
|
179
173
|
/** 是否启用首轮锚定。 */
|
|
180
|
-
enabled:
|
|
174
|
+
enabled: true,
|
|
181
175
|
/**
|
|
182
176
|
* 是否挂载真实 Minimal 工具对(0.5.0):把官方 minimal preset 的持久
|
|
183
177
|
* PTY bash + str_replace_editor 挂进 agent 作用域(按名阴影 standard 的
|
|
@@ -191,9 +185,7 @@ export const DEFAULT_CONFIG = {
|
|
|
191
185
|
promoteOn: 'either',
|
|
192
186
|
/** 请求 #1 剥离的自动注入上下文 source.kind;[] 关闭剥离。 */
|
|
193
187
|
suppressedContextSources: ['skill-catalog', 'agent-instructions'],
|
|
194
|
-
/**
|
|
195
|
-
compactionTools: ['read', 'write', 'edit', 'glob', 'grep', 'todo_write', 'ask_user_question'],
|
|
196
|
-
/** 晋升后常驻的按需发现工具(dsh-anchored-standard 的 resident discovery set)。 */
|
|
188
|
+
/** 晋升后 resident 目录的常驻发现工具(按需解锁完整目录)。 */
|
|
197
189
|
discoveryTools: ['dev_tool_search', 'skill_search', 'skill_load'],
|
|
198
190
|
/** 请求 #1 的输出预算封顶(token);0 = 不封顶(opt-in)。 */
|
|
199
191
|
maxTokens: 0,
|
|
@@ -213,7 +205,7 @@ export const DEFAULT_CONFIG = {
|
|
|
213
205
|
*/
|
|
214
206
|
minimalPrompt: {
|
|
215
207
|
/** 是否启用极简提示词层。 */
|
|
216
|
-
enabled:
|
|
208
|
+
enabled: true,
|
|
217
209
|
/** 替换后的 persona 文本;与极简模式相同可完全对齐语域。留空不替换。 */
|
|
218
210
|
persona: 'You are a helpful software engineer assistant.',
|
|
219
211
|
/** 屏蔽全局引导段(identity / source / web-surface)。 */
|
|
@@ -277,8 +269,7 @@ export function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
|
|
|
277
269
|
tools: stringList(rawBootstrap.tools, db.tools),
|
|
278
270
|
promoteOn,
|
|
279
271
|
suppressedContextSources: stringListOrEmpty(rawBootstrap.suppressedContextSources, db.suppressedContextSources),
|
|
280
|
-
|
|
281
|
-
discoveryTools: stringListOrEmpty(rawBootstrap.discoveryTools, db.discoveryTools),
|
|
272
|
+
discoveryTools: stringList(rawBootstrap.discoveryTools, db.discoveryTools),
|
|
282
273
|
maxTokens,
|
|
283
274
|
};
|
|
284
275
|
const rawMP = raw.minimalPrompt !== null && typeof raw.minimalPrompt === 'object' && !Array.isArray(raw.minimalPrompt)
|
|
@@ -298,8 +289,6 @@ export function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
|
|
|
298
289
|
escalateOnKeyword: boolValue(merged.escalateOnKeyword, defaults.escalateOnKeyword),
|
|
299
290
|
escalateOnUnknownTool: boolValue(merged.escalateOnUnknownTool, defaults.escalateOnUnknownTool),
|
|
300
291
|
suppressInjectedContext: boolValue(merged.suppressInjectedContext, defaults.suppressInjectedContext),
|
|
301
|
-
skillDiscovery: boolValue(merged.skillDiscovery, defaults.skillDiscovery),
|
|
302
|
-
instructionHint: boolValue(merged.instructionHint, defaults.instructionHint),
|
|
303
292
|
coreTools: stringList(merged.coreTools, defaults.coreTools),
|
|
304
293
|
families,
|
|
305
294
|
bootstrap,
|
|
@@ -313,7 +302,7 @@ export function validateConfig(partial) {
|
|
|
313
302
|
throw new TypeError('adaptive-perf config must be a plain object');
|
|
314
303
|
}
|
|
315
304
|
const boolFields = ['enabled', 'suppressRuntimeContext', 'leanByDefault', 'escalateOnKeyword', 'escalateOnUnknownTool',
|
|
316
|
-
'suppressInjectedContext'
|
|
305
|
+
'suppressInjectedContext'];
|
|
317
306
|
for (const key of boolFields) {
|
|
318
307
|
if (partial[key] !== void 0 && typeof partial[key] !== 'boolean') {
|
|
319
308
|
throw new TypeError(`adaptive-perf config field "${key}" must be a boolean`);
|
|
@@ -359,7 +348,7 @@ export function validateConfig(partial) {
|
|
|
359
348
|
if (b.maxTokens !== void 0 && !(Number.isSafeInteger(b.maxTokens) && b.maxTokens >= 0)) {
|
|
360
349
|
throw new TypeError('adaptive-perf config field "bootstrap.maxTokens" must be a non-negative safe integer');
|
|
361
350
|
}
|
|
362
|
-
for (const key of ['tools', 'suppressedContextSources', '
|
|
351
|
+
for (const key of ['tools', 'suppressedContextSources', 'discoveryTools']) {
|
|
363
352
|
if (b[key] !== void 0 && !(Array.isArray(b[key]) && b[key].every((v) => typeof v === 'string'))) {
|
|
364
353
|
throw new TypeError(`adaptive-perf config field "bootstrap.${key}" must be an array of strings`);
|
|
365
354
|
}
|
|
@@ -536,89 +525,7 @@ export function mountRealPair(agent, mounts, warn) {
|
|
|
536
525
|
return disposers;
|
|
537
526
|
}
|
|
538
527
|
|
|
539
|
-
// ── instruction-hint(0.5.0,参照 dsh-anchored-standard)──────────────────
|
|
540
528
|
|
|
541
|
-
/** 项目链候选指令文件名(探测顺序)与用户全局候选。 */
|
|
542
|
-
export const INSTRUCTION_PROJECT_CANDIDATES = ['AGENTS.md', 'CLAUDE.md', 'AGENTS.local.md', 'CLAUDE.local.md'];
|
|
543
|
-
export const INSTRUCTION_USER_CANDIDATE = 'AGENTS.md';
|
|
544
|
-
|
|
545
|
-
/** 平台无关的路径拼接(与参考实现一致)。 */
|
|
546
|
-
export function joinPath(dir, segment) {
|
|
547
|
-
if (dir.endsWith('/') || dir.endsWith('\\')) return dir + segment;
|
|
548
|
-
const sep = dir.includes('\\') ? '\\' : '/';
|
|
549
|
-
return dir + sep + segment;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/** 绝对 POSIX/Windows 路径的父目录。 */
|
|
553
|
-
export function parentPath(path) {
|
|
554
|
-
const idx = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
|
555
|
-
if (idx <= 0) return path;
|
|
556
|
-
const parent = path.slice(0, idx);
|
|
557
|
-
return parent.length === 0 ? path : parent;
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
/** 从 cwd 向上找项目根(含 .git/.hg/.svn 标记的最近祖先)。 */
|
|
561
|
-
export async function findProjectRoot(fs, cwd, signal) {
|
|
562
|
-
let current = cwd;
|
|
563
|
-
for (;;) {
|
|
564
|
-
for (const marker of ['.git', '.hg', '.svn']) {
|
|
565
|
-
try {
|
|
566
|
-
const target = await fs.resolve(joinPath(current, marker), { cwd, signal });
|
|
567
|
-
const info = await fs.stat(target, signal);
|
|
568
|
-
if (info !== void 0) return current;
|
|
569
|
-
} catch {
|
|
570
|
-
// 探测失败 = 标记不存在,继续。
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
const parent = parentPath(current);
|
|
574
|
-
if (parent === current || parent.length === 0) return cwd;
|
|
575
|
-
current = parent;
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
/** 列出某目录下存在的候选指令文件。 */
|
|
580
|
-
export async function presentInDir(fs, dir, candidates, signal) {
|
|
581
|
-
const found = [];
|
|
582
|
-
for (const candidate of candidates) {
|
|
583
|
-
try {
|
|
584
|
-
const target = await fs.resolve(joinPath(dir, candidate), { cwd: dir, signal });
|
|
585
|
-
const info = await fs.stat(target, signal);
|
|
586
|
-
if (info !== void 0 && info.type === 'file') found.push(candidate);
|
|
587
|
-
} catch {
|
|
588
|
-
// 不存在/不可读——跳过。
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
return found;
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
/**
|
|
595
|
-
* 探测会话的指令文件(项目链 + $DSH_HOME/AGENTS.md)。任何失败都返回
|
|
596
|
-
* null/空集——提示注入绝不能让会话受损。
|
|
597
|
-
* @returns { projectFiles, root, userGlobalFiles } 或 null(无 fs / 无 cwd)。
|
|
598
|
-
*/
|
|
599
|
-
export async function probeInstructionFiles(fs, cwd, dshHome, signal) {
|
|
600
|
-
if (fs === null || fs === void 0 || typeof cwd !== 'string' || cwd.length === 0) return null;
|
|
601
|
-
const projectFiles = [];
|
|
602
|
-
let root = cwd;
|
|
603
|
-
try {
|
|
604
|
-
root = await findProjectRoot(fs, cwd, signal);
|
|
605
|
-
projectFiles.push(...await presentInDir(fs, root, INSTRUCTION_PROJECT_CANDIDATES, signal));
|
|
606
|
-
} catch {
|
|
607
|
-
// 项目链探测失败——忽略,仍报告用户全局文件。
|
|
608
|
-
}
|
|
609
|
-
const userGlobalFiles = [];
|
|
610
|
-
if (typeof dshHome === 'string' && dshHome.length > 0) {
|
|
611
|
-
try {
|
|
612
|
-
userGlobalFiles.push(...await presentInDir(fs, dshHome, [INSTRUCTION_USER_CANDIDATE], signal));
|
|
613
|
-
} catch {
|
|
614
|
-
// 不可读——忽略。
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
if (projectFiles.length === 0 && userGlobalFiles.length === 0) return null;
|
|
618
|
-
return { projectFiles, root, userGlobalFiles };
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
// ── 按需发现工具(resident discovery set,参照 dsh-anchored-standard)─────
|
|
622
529
|
|
|
623
530
|
/**
|
|
624
531
|
* 从 agent/pre-step 的 decision.messages 剥离指定 source.kind 的消息。
|
|
@@ -633,7 +540,17 @@ export function stripSuppressedMessages(messages, suppressedSources) {
|
|
|
633
540
|
return kept.length === messages.length ? messages : kept;
|
|
634
541
|
}
|
|
635
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
|
+
|
|
636
552
|
/**
|
|
553
|
+
/**
|
|
637
554
|
* dev_tool_search:搜索完整可见目录并按名解锁工具(解锁结果下一请求生效,
|
|
638
555
|
* 经 syncBootstrap 重算 deny 集;解锁记录写入 a.unlocked,resume-safe 由
|
|
639
556
|
* loadUnlockedFromEvents 从持久事件恢复)。
|
|
@@ -845,49 +762,34 @@ export function extractSkillBody(skill) {
|
|
|
845
762
|
return '';
|
|
846
763
|
}
|
|
847
764
|
|
|
848
|
-
// ── bootstrap 锚定(参照 dsh-anchored-standard 的实测结论)──────────────
|
|
849
|
-
|
|
850
|
-
/** promoteOn 配置 → 晋升的持久事件类型集合。 */
|
|
851
|
-
export const PROMOTE_EVENTS = {
|
|
852
|
-
'tool-call': ['tool/call'],
|
|
853
|
-
'assistant-message': ['assistant/message'],
|
|
854
|
-
either: ['tool/call', 'assistant/message'],
|
|
855
|
-
};
|
|
856
765
|
|
|
857
766
|
/**
|
|
858
|
-
* 扫描一段持久会话日志,推导 {
|
|
859
|
-
*
|
|
860
|
-
*
|
|
767
|
+
* 扫描一段持久会话日志,推导 { promoted } 阶段。
|
|
768
|
+
* 晋升是持久信号(与 dsh-anchored-standard 一致):首次 promoteOn 事件后
|
|
769
|
+
* 恒为已晋升,resume/reload 不丢状态,compaction 不重置。
|
|
861
770
|
*/
|
|
862
771
|
export function scanPhase(events, promoteEvents) {
|
|
863
|
-
let boundary = -1;
|
|
864
772
|
let promoted = false;
|
|
865
773
|
if (Array.isArray(events)) {
|
|
866
774
|
for (const event of events) {
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
promoted = false;
|
|
871
|
-
continue;
|
|
775
|
+
if (promoteEvents.includes(event?.type)) {
|
|
776
|
+
promoted = true;
|
|
777
|
+
break;
|
|
872
778
|
}
|
|
873
|
-
if (promoteEvents.includes(event?.type) && seq > boundary) promoted = true;
|
|
874
779
|
}
|
|
875
780
|
}
|
|
876
|
-
return {
|
|
781
|
+
return { promoted };
|
|
877
782
|
}
|
|
878
783
|
|
|
879
784
|
/** 增量喂入一个持久事件(仅更新已存在条目;冷会话由 scanPhase 全量推导)。 */
|
|
880
785
|
export function observePhase(state, sessionId, event) {
|
|
881
786
|
const entry = state.get(sessionId);
|
|
882
787
|
if (entry === void 0) return;
|
|
883
|
-
const seq = typeof event?.seq === 'number' ? event.seq : 0;
|
|
884
|
-
if (event?.type === 'compaction/end') {
|
|
885
|
-
state.set(sessionId, { boundary: seq, promoted: false, promoteEvents: entry.promoteEvents });
|
|
886
|
-
return;
|
|
887
|
-
}
|
|
888
788
|
if (entry.promoted) return;
|
|
889
|
-
const promoteEvents = entry.promoteEvents
|
|
890
|
-
|
|
789
|
+
const promoteEvents = Array.isArray(entry.promoteEvents)
|
|
790
|
+
? entry.promoteEvents
|
|
791
|
+
: PROMOTE_EVENTS[entry.promoteOn] ?? [];
|
|
792
|
+
if (promoteEvents.includes(event?.type)) {
|
|
891
793
|
state.set(sessionId, { ...entry, promoted: true });
|
|
892
794
|
}
|
|
893
795
|
}
|
|
@@ -981,7 +883,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
981
883
|
|
|
982
884
|
/** 真实 Minimal 工具对模块(0.5.0)。加载失败 = null = 降级旧行为。
|
|
983
885
|
* options.realPairModules 仅供测试注入。 */
|
|
984
|
-
|
|
886
|
+
let realPairModules = options.realPairModules === void 0
|
|
985
887
|
? (cfg.bootstrap.enabled && cfg.bootstrap.realPair ? await loadRealPairModules() : null)
|
|
986
888
|
: options.realPairModules;
|
|
987
889
|
/** 是否已尝试加载过真实工具对模块(避免配置热更新时反复重试)。 */
|
|
@@ -992,9 +894,6 @@ export async function apply(ctx, config, options = {}) {
|
|
|
992
894
|
} catch {}
|
|
993
895
|
}
|
|
994
896
|
|
|
995
|
-
/** 已注入过 instruction-hint 的会话(内存去重,与参考实现一致)。 */
|
|
996
|
-
const hintedSessions = new Set();
|
|
997
|
-
|
|
998
897
|
/**
|
|
999
898
|
* 配置热更新把 realPair 从关闭切到开启时补加载官方模块(初始未启用时
|
|
1000
899
|
* 不加载)。加载成功后对已接管会话补挂。
|
|
@@ -1183,6 +1082,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1183
1082
|
a.realPairActive = a.realPairDisposers.length > 0;
|
|
1184
1083
|
}
|
|
1185
1084
|
|
|
1085
|
+
|
|
1186
1086
|
/**
|
|
1187
1087
|
* 给目标 agent 注册 on-demand 发现工具(dsh-anchored-standard 的 resident
|
|
1188
1088
|
* discovery pattern):晋升后常驻 bootstrap.discoveryTools 列出的工具——
|
|
@@ -1234,10 +1134,10 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1234
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.',
|
|
1235
1135
|
});
|
|
1236
1136
|
}
|
|
1237
|
-
if (name === 'skill_search'
|
|
1137
|
+
if (name === 'skill_search') {
|
|
1238
1138
|
return createSkillSearch({ skillsOf: () => ctx.get('skills') });
|
|
1239
1139
|
}
|
|
1240
|
-
if (name === 'skill_load'
|
|
1140
|
+
if (name === 'skill_load') {
|
|
1241
1141
|
return createSkillLoad({ skillsOf: () => ctx.get('skills') });
|
|
1242
1142
|
}
|
|
1243
1143
|
return null;
|
|
@@ -1295,10 +1195,6 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1295
1195
|
try { disposer(); } catch {}
|
|
1296
1196
|
}
|
|
1297
1197
|
a.promptDisposers = [];
|
|
1298
|
-
for (const disposer of a.toolDisposers.values()) {
|
|
1299
|
-
try { disposer(); } catch {}
|
|
1300
|
-
}
|
|
1301
|
-
a.toolDisposers.clear();
|
|
1302
1198
|
for (const dispose of a.realPairDisposers) {
|
|
1303
1199
|
try { dispose(); } catch {}
|
|
1304
1200
|
}
|
|
@@ -1306,6 +1202,10 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1306
1202
|
if (a.bootstrapDisposer !== null) {
|
|
1307
1203
|
try { a.bootstrapDisposer(); } catch {}
|
|
1308
1204
|
}
|
|
1205
|
+
for (const disposer of a.toolDisposers.values()) {
|
|
1206
|
+
try { disposer(); } catch {}
|
|
1207
|
+
}
|
|
1208
|
+
a.toolDisposers.clear();
|
|
1309
1209
|
for (const disposer of a.familyDisposers.values()) {
|
|
1310
1210
|
try { disposer(); } catch {}
|
|
1311
1211
|
}
|
|
@@ -1357,10 +1257,11 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1357
1257
|
});
|
|
1358
1258
|
|
|
1359
1259
|
// ── 首轮锚定(bootstrap,参照 dsh-anchored-standard 的实测结论)─────
|
|
1360
|
-
// 阶段状态:sessionId -> {
|
|
1260
|
+
// 阶段状态:sessionId -> { promoted, promoteEvents }。冷会话在
|
|
1361
1261
|
// handleAgent 时全量扫描持久日志(resume 安全),此后 session/event 增量
|
|
1362
|
-
//
|
|
1363
|
-
// prepend 注册,保证本插件是"最外层"
|
|
1262
|
+
// 更新。晋升是持久信号(compaction 不重置)。子代理(delegationDepth > 0)
|
|
1263
|
+
// 恒为已晋升。所有 waterfall 用 prepend 注册,保证本插件是"最外层"
|
|
1264
|
+
// 变换(剥离是最后一道)。
|
|
1364
1265
|
const bootstrapState = new Map();
|
|
1365
1266
|
let bootstrapWarned = false;
|
|
1366
1267
|
const warnBootstrapOnce = (message) => {
|
|
@@ -1371,14 +1272,14 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1371
1272
|
|
|
1372
1273
|
function bootstrapPhaseOf(agent) {
|
|
1373
1274
|
const session = agent !== null && typeof agent === 'object' ? agent.session : void 0;
|
|
1374
|
-
if (session === void 0 || typeof session.id !== 'string') return {
|
|
1275
|
+
if (session === void 0 || typeof session.id !== 'string') return { promoted: true };
|
|
1375
1276
|
// 子代理首轮即可用工具(与参考实现一致)。
|
|
1376
|
-
if ((session.header?.delegationDepth ?? 0) > 0) return {
|
|
1277
|
+
if ((session.header?.delegationDepth ?? 0) > 0) return { promoted: true };
|
|
1377
1278
|
let entry = bootstrapState.get(session.id);
|
|
1378
1279
|
if (entry === void 0) {
|
|
1379
1280
|
entry = {
|
|
1380
1281
|
...scanPhase(session.events, PROMOTE_EVENTS[cfg.bootstrap.promoteOn]),
|
|
1381
|
-
|
|
1282
|
+
promoteOn: cfg.bootstrap.promoteOn,
|
|
1382
1283
|
};
|
|
1383
1284
|
bootstrapState.set(session.id, entry);
|
|
1384
1285
|
}
|
|
@@ -1393,12 +1294,13 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1393
1294
|
}
|
|
1394
1295
|
|
|
1395
1296
|
/**
|
|
1396
|
-
* bootstrap
|
|
1397
|
-
* - 未晋升:bootstrap 工具对 + PTC 的直接调用工具(run_code
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
1400
|
-
*
|
|
1401
|
-
*
|
|
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 即可取用)。
|
|
1402
1304
|
*
|
|
1403
1305
|
* 注意:不能靠过滤 system-prompt/assemble 的 assembly.tools 来实现——
|
|
1404
1306
|
* PTC 模式下 assembly.tools 只有 [run_code],Minimal 工具对不在其中,
|
|
@@ -1416,14 +1318,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1416
1318
|
if (phase.promoted) {
|
|
1417
1319
|
for (const name of cfg.bootstrap.discoveryTools) keep.add(name);
|
|
1418
1320
|
for (const name of a.unlocked) keep.add(name);
|
|
1419
|
-
for (const [id, family] of Object.entries(cfg.families)) {
|
|
1420
|
-
if (a.escalated.has(id)) {
|
|
1421
|
-
for (const name of familyDeny(a, family)) keep.add(name);
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
return keep;
|
|
1425
1321
|
}
|
|
1426
|
-
if (phase.boundary >= 0) for (const name of cfg.bootstrap.compactionTools) keep.add(name);
|
|
1427
1322
|
return keep;
|
|
1428
1323
|
}
|
|
1429
1324
|
|
|
@@ -1431,7 +1326,7 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1431
1326
|
function syncBootstrap(a) {
|
|
1432
1327
|
const phase = bootstrapPhaseOf(a.agent);
|
|
1433
1328
|
const keep = bootstrapKeepSet(a);
|
|
1434
|
-
// discovery 工具只在晋升后的 resident 阶段注册;bootstrap
|
|
1329
|
+
// discovery 工具只在晋升后的 resident 阶段注册;bootstrap 阶段隐藏。
|
|
1435
1330
|
syncDiscoveryTools(a, keep !== null && phase.promoted);
|
|
1436
1331
|
const key = keep === null ? null : [...keep].sort().join(',');
|
|
1437
1332
|
if (key === a.bootstrapKey) return;
|
|
@@ -1460,65 +1355,29 @@ export async function apply(ctx, config, options = {}) {
|
|
|
1460
1355
|
const a = agents.get(session.id);
|
|
1461
1356
|
if (a !== void 0) {
|
|
1462
1357
|
const after = bootstrapState.get(session.id);
|
|
1463
|
-
if (before === void 0 || after === void 0 || before.promoted !== after.promoted
|
|
1358
|
+
if (before === void 0 || after === void 0 || before.promoted !== after.promoted) {
|
|
1464
1359
|
syncBootstrap(a);
|
|
1465
1360
|
}
|
|
1466
1361
|
}
|
|
1467
1362
|
});
|
|
1468
1363
|
|
|
1469
|
-
//
|
|
1470
|
-
//
|
|
1471
|
-
//
|
|
1472
|
-
//
|
|
1473
|
-
//
|
|
1474
|
-
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) => {
|
|
1475
1370
|
const decision = await next();
|
|
1476
1371
|
if (decision === null || typeof decision !== 'object' || decision.kind === 'reject') return decision;
|
|
1477
1372
|
try {
|
|
1478
1373
|
if (!cfg.enabled || !isTarget(agent)) return decision;
|
|
1479
1374
|
const phase = bootstrapPhaseOf(agent);
|
|
1480
1375
|
const suppressed = new Set(cfg.bootstrap.suppressedContextSources);
|
|
1481
|
-
|
|
1482
|
-
// 剥离条件:bootstrap 未晋升(旧行为)或常驻抑制开启(0.5.0 默认:
|
|
1483
|
-
// 整个会话不再注入技能目录与 AGENTS.md 摘要,替代品是按需发现工具)。
|
|
1376
|
+
if (suppressed.size === 0) return decision;
|
|
1484
1377
|
const stripActive = (cfg.bootstrap.enabled && !phase.promoted) || cfg.suppressInjectedContext;
|
|
1485
|
-
if (
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
}
|
|
1489
|
-
// instruction-hint:晋升后一次性注入"指令文件存在,需要时自读"。
|
|
1490
|
-
if (cfg.bootstrap.enabled && cfg.instructionHint && phase.promoted && Array.isArray(out.messages)) {
|
|
1491
|
-
const session = agent.session;
|
|
1492
|
-
if (session !== null && typeof session === 'object' && typeof session.id === 'string' && !hintedSessions.has(session.id)) {
|
|
1493
|
-
hintedSessions.add(session.id);
|
|
1494
|
-
const fs = ctx.get('fs');
|
|
1495
|
-
const dshHome = process.env.DSH_HOME?.trim() || `${process.env.HOME || process.env.USERPROFILE || ''}/.dsh`;
|
|
1496
|
-
const probe = await probeInstructionFiles(fs, session.header?.cwd, dshHome, signal);
|
|
1497
|
-
if (probe !== null) {
|
|
1498
|
-
const sections = [];
|
|
1499
|
-
if (probe.projectFiles.length > 0) {
|
|
1500
|
-
sections.push(`Workspace instruction files exist: ${probe.projectFiles.join(', ')} (project root: ${probe.root}).`);
|
|
1501
|
-
}
|
|
1502
|
-
if (probe.userGlobalFiles.length > 0) {
|
|
1503
|
-
sections.push(`A user-global instruction file exists: ${INSTRUCTION_USER_CANDIDATE}.`);
|
|
1504
|
-
}
|
|
1505
|
-
const text = [
|
|
1506
|
-
...sections,
|
|
1507
|
-
'Do NOT assume their content. When a task touches this workspace, read the relevant instruction files first and follow them.',
|
|
1508
|
-
].join(' ');
|
|
1509
|
-
out = {
|
|
1510
|
-
...out,
|
|
1511
|
-
messages: [...out.messages, {
|
|
1512
|
-
id: `instruction-hint-${session.id}`,
|
|
1513
|
-
role: 'user',
|
|
1514
|
-
content: [{ type: 'text', text }],
|
|
1515
|
-
source: { kind: 'instruction-hint', form: 'hint' },
|
|
1516
|
-
}],
|
|
1517
|
-
};
|
|
1518
|
-
}
|
|
1519
|
-
}
|
|
1520
|
-
}
|
|
1521
|
-
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 };
|
|
1522
1381
|
} catch (error) {
|
|
1523
1382
|
// 过滤失败绝不吞掉用户的上下文:原样放行。
|
|
1524
1383
|
warnBootstrapOnce(`bootstrap context filter failed, keeping injected context: ${String((error && error.message) || error)}`);
|
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",
|