@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 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.6.0,功能优先)**:本插件的性能收益本质是"砍上下文/砍工具换
4
- > token",没有无损形态——旧版把下列机制默认开启,导致 standard/PTC 模式功能
5
- > 缺失(编排类工具族从目录删除、PTC 程序调用直接 `UNKNOWN_TOOL`;技能目录与
6
- > AGENTS.md 摘要被剥离;官方引导段被屏蔽;首轮只剩两个工具)。**0.6.0 起全部
7
- > 机制默认关闭,安装后对会话零干预,官方 preset 功能完整**;需要省 token 时在
8
- > 设置页按需开启下列任意机制(补偿机制——关键词/失败信号放行、`skill_search`
9
- > 按需发现——随开启自动生效)。下文描述的是各机制**开启后**的行为。
10
-
11
- **标准模式(standard)** 与 **PTC 模式(code)** 达到 **极简模式(minimal)**
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`)后进入 **resident
28
- 目录**:bootstrap 工具对 + 常驻发现工具(`dev_tool_search` +
29
- `skill_search` + `skill_load`)+ 已解锁工具,而不是一次性 dump 完整
30
- Standard 目录(避免 post-promotion regression);阶段从持久会话事件推导,
31
- **resume / reload 不丢状态**;
32
- - `dev_tool_search` 可搜索完整目录并按名解锁工具,解锁结果下一请求生效;
33
- - compaction 会把会话打回"第二次首轮":`compaction/end` 之后回到受控目录
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)**:技能目录提醒与 AGENTS.md 摘要
37
- **不再在晋升后恢复**——参考实现实测它们即使在后置阶段也扰动轨迹,且每个请求
38
- 多耗数千 token。替代品:
39
- - `skill_search` / `skill_load`:晋升后常驻的两个小工具,按需发现并注入单个
40
- 技能的完整说明(替代 ~9KB `<available_skills>` 目录注入);
41
- - `instruction-hint`:晋升后只注入**一次**的短提示——"指令文件存在,需要时
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
- (子代理 / 工作流 / ralph / goal 等编排类),只保留核心编码工具
61
- (bash / 文件读写编辑 / 检索 / jobs / todo / 提问 / 网页搜索 / 技能 / 计划)。
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 逐字相同,首个持久信号后进 resident 目录 |
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 全文 | `suppressInjectedContext`:整个会话抑制;替代品为 `skill_search`/`skill_load` + 一次性 `instruction-hint` |
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` | 整个会话剥离技能目录/AGENTS.md 注入(0.5.0;`false` = 只在请求 #1 剥离) |
126
- | `skillDiscovery` | `true` | 晋升后常驻 `skill_search` / `skill_load`(0.5.0) |
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`:剥离的自动注入 source.kind;`[]` 关闭剥离。
159
- - `compactionTools`:compaction/end 之后、再次晋升前可用的核心工作集。
160
- - `discoveryTools`:晋升后 resident 目录里的按需发现工具
161
- (`dev_tool_search` 解锁工具;`skill_search`/`skill_load` 按需加载技能)。
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
- bootstrapCompactionTools: "压缩后工作集",
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
- bootstrapCompactionTools: "Post-compaction work set",
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 ? false : draft.enabled,
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 ? false : draft.suppressRuntimeContext,
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 ? false : draft.escalateOnKeyword,
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 ? false : draft.escalateOnUnknownTool,
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 ? false : !!draft.bootstrap && draft.bootstrap.enabled,
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 ? false : !!draft.bootstrap && draft.bootstrap.realPair,
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("bootstrapCompactionTools"),
365
- hint: t("fieldBootstrapCompactionTools"),
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 ? false : (draft.minimalPrompt && draft.minimalPrompt.enabled === true),
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 ? false : (draft.minimalPrompt && draft.minimalPrompt.suppressSections === true),
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: true
15
+ leanByDefault: false
16
16
  escalateOnKeyword: true
17
17
  escalateOnUnknownTool: true
18
- # 常驻上下文抑制(参照 dsh-anchored-standard):整个会话剥离技能目录
19
- # 提醒与 AGENTS.md 摘要注入,替代品是 skill_search/skill_load 按需
20
- # 发现与 instruction-hint 一次性提示。
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
- skillDiscovery: true
23
- instructionHint: true
24
- # 首轮锚定(参照 dsh-anchored-standard issue #11):请求 #1 只暴露
25
- # 真实 Minimal 工具对(realPair 挂载官方 minimal preset 同款插件,
26
- # schema 与 Minimal 逐字相同)并剥离自动注入上下文,首个持久信号后
27
- # 进入 resident 目录(工具对 + 发现工具 + 已解锁工具)。
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)」与「PTC 模式(code)」达到与「极简模式
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
- * 后进入 **resident 目录**:工具对 + 常驻发现工具(dev_tool_search /
34
- * skill_search / skill_load)+ 已解锁工具,而不是一次性 dump 完整
35
- * Standard 目录(避免 post-promotion regression);阶段从持久会话事件
36
- * 推导,resume/reload 不丢状态;compaction/end 之后回到受控目录
37
- * (工具对 + compactionTools)直到新的晋升信号(纪元感知);
38
- * bootstrap.maxTokens(可选)给请求 #1 封顶输出预算,晋升后剥离。
39
- * D. 常驻上下文抑制(suppressInjectedContext,0.5.0 新增):技能目录提醒
40
- * AGENTS.md 摘要**不再在晋升后恢复**——参考实现实测它们即使在后置
41
- * 阶段也扰动轨迹且每个请求多耗数千 token。替代品:
42
- * - skill_search / skill_load:按需发现并注入单个技能的完整说明
43
- * (晋升后常驻,替代 ~9KB <available_skills> 注入);
44
- * - instruction-hint:晋升后只注入**一次**的短提示——"这些指令文件
45
- * 存在,需要时自己读",而不是每请求 dump 全文(文件探测走宿主 fs,
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) 剥离自动注入上下文 + 注入 instruction-hint
60
+ * - agent/pre-step(waterfall) 首轮剥离自动注入上下文
65
61
  * - agent/request(waterfall) 首轮输出预算封顶
66
- * - session/event 增量喂入持久事件(晋升 / compaction 纪元)
62
+ * - session/event 增量喂入持久事件(晋升信号)
67
63
  * - agent/created、agent/disposed、agent/inbox/inserted、tools/result 事件
68
64
  * 监听器都注册在 host 根作用域(与 dsh-agent-presets 自身同款用法),事件按
69
- * scope 过滤分发,子作用域派发的事件根监听器可收到;两个 waterfall 均以
70
- * prepend 注册保证"最外层"(剥离是最后一道变换)。
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)后恢复;compaction/end 之后回到
112
- * 受控目录(bootstrap 工具对 + compactionTools)直到新的晋升信号;maxTokens>0
113
- * 时给请求 #1 封顶输出预算(晋升后剥离)。
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.6.0):功能优先,全部优化 opt-in。旧版把下列机制默认
122
- * 开启,导致 standard/PTC 模式功能缺失(工具族从目录删除 PTC 程序
123
- * 调用直接 UNKNOWN_TOOL;技能目录/指令摘要被剥离;官方引导段被屏蔽;
124
- * 首轮只剩两个工具)。这些机制的收益本质是"砍上下文换 token",没有
125
- * 无损形态,因此默认全部关闭,保持官方 preset 的完整功能;需要省
126
- * token 时按需开启(设置页或 config.json),开启即接受相应的功能
127
- * 约束(关键词/失败信号放行、skill_search 按需发现等补偿机制仍在)。
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: false,
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
- * 不再像旧版那样在晋升后恢复——替代品是 skill_search/skill_load 按需发现
141
- * 与 instruction-hint 一次性提示。false = 只在 bootstrap(请求 #1)剥离。
137
+ * (skill-catalog 技能目录提醒、agent-instructions AGENTS.md 摘要);
138
+ * false = 只在 bootstrap 未晋升阶段剥离,晋升后恢复注入(0.7.0 默认,
139
+ * 与 dsh-anchored-standard 一致:只锚定首轮、不减少后续功能)。
142
140
  */
143
- suppressInjectedContext: false,
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: false,
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
- /** compaction/end 之后、再次晋升前可用的额外核心工作集。 */
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: false,
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
- compactionTools: stringListOrEmpty(rawBootstrap.compactionTools, db.compactionTools),
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', 'skillDiscovery', 'instructionHint'];
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', 'compactionTools', 'discoveryTools']) {
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
- * 扫描一段持久会话日志,推导 { boundary, promoted } 阶段。
859
- * compaction/end 之后需要新的晋升信号(纪元感知);boundary -1 表示
860
- * 尚无压缩。事件无 seq 时按"边界之后"处理(与参考实现一致)。
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
- const seq = typeof event?.seq === 'number' ? event.seq : 0;
868
- if (event?.type === 'compaction/end') {
869
- boundary = seq;
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 { boundary, promoted };
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
- if (promoteEvents.includes(event?.type) && seq > entry.boundary) {
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
- const realPairModules = options.realPairModules === void 0
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' && cfg.skillDiscovery) {
1137
+ if (name === 'skill_search') {
1238
1138
  return createSkillSearch({ skillsOf: () => ctx.get('skills') });
1239
1139
  }
1240
- if (name === 'skill_load' && cfg.skillDiscovery) {
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 -> { boundary, promoted, promoteEvents }。冷会话在
1260
+ // 阶段状态:sessionId -> { promoted, promoteEvents }。冷会话在
1361
1261
  // handleAgent 时全量扫描持久日志(resume 安全),此后 session/event 增量
1362
- // 更新。子代理(delegationDepth > 0)恒为已晋升。所有 waterfall 用
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 { boundary: -1, promoted: true };
1275
+ if (session === void 0 || typeof session.id !== 'string') return { promoted: true };
1375
1276
  // 子代理首轮即可用工具(与参考实现一致)。
1376
- if ((session.header?.delegationDepth ?? 0) > 0) return { boundary: -1, promoted: true };
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
- promoteEvents: PROMOTE_EVENTS[cfg.bootstrap.promoteOn],
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
- * compaction 后恢复期的工作集;
1399
- * - 已晋升:bootstrap 工具对 + resident discovery 工具 +
1400
- * dev_tool_search 已解锁工具 + 已升级工具族(dsh-anchored-standard 的
1401
- * resident catalog,避免晋升后一次性 dump 完整 Standard 目录)。
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/compaction 阶段隐藏。
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 || before.boundary !== after.boundary) {
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
- // 上下文剥离 + instruction-hint。prepend + 根作用域注册保证是最后一道
1470
- // 变换(后注册的注入者无法再补回)。两个职责同在一个监听器里,顺序天然
1471
- // 正确:先剥离被抑制来源,再(晋升后一次性)追加指令文件提示。
1472
- // 剥离与 bootstrap 开关正交:常驻抑制(suppressInjectedContext)在
1473
- // bootstrap 关闭时也生效;instruction-hint 依赖晋升阶段(bootstrap 机制)。
1474
- ctx.on('agent/pre-step', async ({ agent, signal }, next) => {
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
- let out = decision;
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 (suppressed.size > 0 && stripActive && Array.isArray(out.messages)) {
1486
- const kept = stripSuppressedMessages(out.messages, suppressed);
1487
- out = kept === out.messages ? out : { ...out, messages: kept };
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.6.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.6",
35
- "@deepseek-ai/dsh-terminal": "^0.1.0-rc.6",
36
- "@deepseek-ai/dsh-terminal-bash": "^0.1.0-rc.6",
37
- "@deepseek-ai/dsh-tool-bash-persistent": "^0.1.0-rc.6",
38
- "@deepseek-ai/dsh-fs-local": "^0.1.0-rc.6",
39
- "@deepseek-ai/dsh-tool-str-replace-editor": "^0.1.0-rc.6"
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": "node ../../scripts/test.mjs",
46
- "prepublishOnly": "npm test"
44
+ "test": "bun ../../scripts/test.mjs",
45
+ "prepublishOnly": "bun ../../scripts/test.mjs"
47
46
  },
48
47
  "repository": {
49
48
  "type": "git",