@chaoset/adaptive-perf 0.3.1 → 0.4.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/README.md CHANGED
@@ -20,6 +20,17 @@
20
20
  - **运行时上下文抑制**:对目标 preset 的会话调用 `suppressRuntimeContext()`
21
21
  (与极简模式的 `includeRuntimeContext: false` 同一机制),每次模型请求省掉
22
22
  "Current runtime context" 快照文本,**零功能损失**。
23
+ - **极简提示词层(minimalPrompt)**:参考实现的完整锚定条件——首轮轨迹由
24
+ **Minimal 的完整 system prompt** 决定,只收窄工具 schema 不够(标准/PTC 的
25
+ 系统提示仍带 `harness:identity` / `harness:source` / `app:web-surface` 三个
26
+ 全局引导段与标准 persona,思维链仍是 standard-like 的 "Let me…" 叙述)。本层
27
+ 对目标会话:
28
+ - 按名阴影屏蔽三个全局引导段(空段在装配时被丢弃,等同极简 `complete`
29
+ persona 的效果;plan-mode 与 PTC 的 SDK 段不受影响);
30
+ - 按名阴影替换 `deployment:persona` 为极简语域 persona(默认与极简模式
31
+ 逐字相同:`You are a helpful software engineer assistant.`)。
32
+ 两处均为 agent 作用域的 prompt 段阴影,仅影响目标会话;**对已恢复/已晋升的
33
+ 旧会话同样生效**(不依赖 bootstrap 阶段)。
23
34
  - **工具目录自适应精简**:会话启动时按"工具族"默认隐藏高开销低频工具
24
35
  (子代理 / 工作流 / ralph / goal 等编排类),只保留核心编码工具
25
36
  (bash / 文件读写编辑 / 检索 / jobs / todo / 提问 / 网页搜索 / 技能 / 计划)。
@@ -36,6 +47,7 @@
36
47
  |---|---|---|---|
37
48
  | 首轮轨迹(决定后续行为风格) | Minimal 工具对锚定(5/5) | 任何 standard 系 schema 落入 standard-like(11/11) | 请求 #1 收窄到真实 Minimal 工具对,首个持久信号后恢复 |
38
49
  | 首轮注入提醒 | 无 skill 目录/AGENTS.md 注入 | 自动注入(在场时锚定 0/9) | 请求 #1 剥离 `skill-catalog` + `agent-instructions` |
50
+ | 系统提示语域(persona + 全局引导段) | 一句话 persona,无任何引导段 | identity/source/web-surface 引导段 + 标准 persona("Let me…" 叙述来源) | minimalPrompt 层按名阴影屏蔽 3 个全局段 + 替换极简 persona(对旧会话同样生效) |
39
51
  | 运行时上下文快照 | 关闭(`includeRuntimeContext: false`) | 每次请求注入文件策略/审批策略等快照 | 同一机制:`suppressRuntimeContext()` |
40
52
  | 模型可见工具目录 | 2 个(bash、str_replace_editor) | ~22 个工具 schema 全部进请求 | bootstrap 后默认隐藏 4 个编排类工具族(~11 个工具),按需放行 |
41
53
  | PTC SDK 参考段 | 无 | 全部工具的类型+描述渲染进提示 | 随目录缩小(同一 `visible` 视图驱动) |
@@ -89,6 +101,26 @@ dsh plugin --profile web remove @chaoset/adaptive-perf
89
101
  | `escalateOnUnknownTool` | `true` | 工具调用失败(UNKNOWN_TOOL)→ 放行该族 |
90
102
  | `coreTools` | 核心编码工具 | 展示用:不进入任何限制族 |
91
103
  | `families` | 见下 | 工具族:`{ 族名: { enabled, tools, keywords } }` |
104
+ | `bootstrap` | 见下 | 首轮锚定(工具对 / 晋升信号 / 剥离上下文 / 压缩恢复集 / 预算封顶) |
105
+ | `minimalPrompt` | 见下 | 极简提示词层(语域锚定) |
106
+
107
+ `minimalPrompt`(极简提示词层,0.4.0 新增):
108
+
109
+ ```json
110
+ {
111
+ "enabled": true,
112
+ "persona": "You are a helpful software engineer assistant.",
113
+ "suppressSections": true
114
+ }
115
+ ```
116
+
117
+ - `persona`:按名阴影替换 `deployment:persona`;默认与极简模式逐字相同,留空
118
+ (`""`)则不替换。
119
+ - `suppressSections`:屏蔽 `harness:identity` / `harness:source` / `app:web-surface`
120
+ 三个全局引导段(等同极简 `complete` persona 的效果;plan-mode 与 PTC 的
121
+ SDK 段不受影响)。
122
+ - 生效范围:目标 preset 的全部会话,**包括已恢复/已晋升的旧会话**(不依赖
123
+ bootstrap 阶段);配置保存后热生效。
92
124
 
93
125
  默认工具族(可整族禁用、改名、增删触发词):
94
126
 
package/client/client.cjs CHANGED
@@ -55,7 +55,13 @@ window.__ModuleLoader__.load({
55
55
  bootstrapCompactionTools: "压缩后工作集",
56
56
  fieldBootstrapCompactionTools: "compaction/end 之后、再次晋升前额外可用的工具(核心工作集)。逗号分隔。",
57
57
  bootstrapMaxTokens: "首轮输出预算封顶(token)",
58
- fieldBootstrapMaxTokens: "请求 #1 的 maxTokens 封顶,晋升后自动剥离;0 = 不封顶(opt-in)"
58
+ fieldBootstrapMaxTokens: "请求 #1 的 maxTokens 封顶,晋升后自动剥离;0 = 不封顶(opt-in)",
59
+ minimalPromptEnabled: "极简提示词层(语域锚定)",
60
+ fieldMinimalPromptEnabled: "把系统提示拉回极简语域:屏蔽全局引导段 + 替换 persona。参考实现的完整锚定条件——仅收窄工具不足以翻转思维链风格(let me → let's/i'll/i need)",
61
+ minimalPromptPersona: "persona 文本",
62
+ fieldMinimalPromptPersona: "按名替换 deployment:persona;默认与极简模式逐字相同。留空 = 不替换(保留原 persona)",
63
+ minimalPromptSuppressSections: "屏蔽全局引导段",
64
+ fieldMinimalPromptSuppressSections: "屏蔽 harness:identity / harness:source / app:web-surface 三个全局段(等同极简 complete persona 的效果;plan-mode 与 PTC 的 SDK 段不受影响)"
59
65
  };
60
66
  const en = {
61
67
  title: "Adaptive performance (adaptive-perf)",
@@ -94,7 +100,13 @@ window.__ModuleLoader__.load({
94
100
  bootstrapCompactionTools: "Post-compaction work set",
95
101
  fieldBootstrapCompactionTools: "Extra tools available after compaction/end until a new promotion signal (core work set). Comma-separated.",
96
102
  bootstrapMaxTokens: "First-request output cap (tokens)",
97
- fieldBootstrapMaxTokens: "maxTokens cap for request #1, stripped after promotion; 0 = no cap (opt-in)"
103
+ fieldBootstrapMaxTokens: "maxTokens cap for request #1, stripped after promotion; 0 = no cap (opt-in)",
104
+ minimalPromptEnabled: "Minimal prompt layer (register anchor)",
105
+ fieldMinimalPromptEnabled: "Pull the system prompt back to the minimal register: suppress the global orientation sections and replace the persona. The reference's full anchoring condition — tool narrowing alone does not flip the thinking style (let me → let's/i'll/i need)",
106
+ minimalPromptPersona: "Persona text",
107
+ fieldMinimalPromptPersona: "Shadows deployment:persona by name; defaults to the exact minimal-mode text. Empty = keep the original persona",
108
+ minimalPromptSuppressSections: "Suppress global orientation sections",
109
+ fieldMinimalPromptSuppressSections: "Shadow harness:identity / harness:source / app:web-surface to empty (same effect as minimal's complete persona; plan-mode and the PTC SDK sections are unaffected)"
98
110
  };
99
111
 
100
112
  // ── 表单字段 ─────────────────────────────────────────────────────────
@@ -317,6 +329,27 @@ window.__ModuleLoader__.load({
317
329
  setDraft({ ...draft, bootstrap: { ...(draft.bootstrap || {}), maxTokens: Number.isSafeInteger(n) && n > 0 ? n : 0 } });
318
330
  }
319
331
  }),
332
+ react.createElement(BoolField, {
333
+ label: t("minimalPromptEnabled"),
334
+ hint: t("fieldMinimalPromptEnabled"),
335
+ value: draft === null ? false : (draft.minimalPrompt && draft.minimalPrompt.enabled === true),
336
+ disabled: draft === null,
337
+ onChange: (v) => setDraft({ ...draft, minimalPrompt: { ...(draft.minimalPrompt || {}), enabled: v } })
338
+ }),
339
+ react.createElement(BoolField, {
340
+ label: t("minimalPromptSuppressSections"),
341
+ hint: t("fieldMinimalPromptSuppressSections"),
342
+ value: draft === null ? false : (draft.minimalPrompt && draft.minimalPrompt.suppressSections === true),
343
+ disabled: draft === null || (draft.minimalPrompt && draft.minimalPrompt.enabled === false),
344
+ onChange: (v) => setDraft({ ...draft, minimalPrompt: { ...(draft.minimalPrompt || {}), suppressSections: v } })
345
+ }),
346
+ react.createElement(TextField, {
347
+ label: t("minimalPromptPersona"),
348
+ hint: t("fieldMinimalPromptPersona"),
349
+ value: draft === null ? "" : ((draft.minimalPrompt && draft.minimalPrompt.persona) || ""),
350
+ disabled: draft === null || (draft.minimalPrompt && draft.minimalPrompt.enabled === false),
351
+ onChange: (v) => setDraft({ ...draft, minimalPrompt: { ...(draft.minimalPrompt || {}), persona: v } })
352
+ }),
320
353
  react.createElement(Field, {
321
354
  label: t("families"),
322
355
  hint: familiesInvalid ? t("invalidFamilies") + (familiesError !== null ? " — " + familiesError : "") : t("fieldFamilies")
package/lib/index.mjs CHANGED
@@ -42,7 +42,9 @@
42
42
  * - agent.ctx.systemPrompt.suppressRuntimeContext() 抑制上下文快照
43
43
  * - agent.ctx.tools.schemas(agent) 读取该 agent 当前可见工具(限制交集)
44
44
  * - agent.ctx.tools.restrict({ deny }) 按 agent 作用域裁剪继承工具
45
- * - system-prompt/assemble(waterfall) 按会话阶段收窄装配后的工具目录
45
+ * - system-prompt/assemble(waterfall) 请求装配(早期实现;bootstrap 目录
46
+ * 收窄改用 tools.restrict——PTC 模式下 assembly.tools 只有 run_code,
47
+ * 过滤会降级失效,而 restrict 同时驱动 API 目录与 PTC SDK 参考段)
46
48
  * - agent/pre-step(waterfall) 剥离自动注入的上下文消息(source.kind)
47
49
  * - agent/request(waterfall) 首轮输出预算封顶
48
50
  * - session/event 增量喂入持久事件(晋升 / compaction 纪元)
@@ -141,6 +143,27 @@ export const DEFAULT_CONFIG = {
141
143
  /** 请求 #1 的输出预算封顶(token);0 = 不封顶(opt-in)。 */
142
144
  maxTokens: 0,
143
145
  },
146
+ /**
147
+ * 极简提示词层(语域锚定,参照 dsh-anchored-standard 的完整条件)。
148
+ *
149
+ * 参考实现指出:首轮"轨迹"(思维链风格)锚定需要 **Minimal 的完整 system
150
+ * prompt**——只收窄工具 schema 不够。本层对目标 preset 的会话:
151
+ * - suppressSections:按名阴影屏蔽全局引导段(harness:identity、
152
+ * harness:source、app:web-surface),空段在装配时被丢弃,等同极简
153
+ * complete persona 的效果(plan-mode 段、PTC 的 SDK 段不受影响);
154
+ * - persona:按名阴影替换 deployment:persona 为极简语域的短 persona
155
+ * (默认与极简模式逐字相同;留空则不替换)。
156
+ * 两处都是 agent 作用域的 prompt 段阴影,仅影响目标会话,随会话销毁自动
157
+ * 释放;对已恢复/已晋升的旧会话同样生效(不依赖 bootstrap 阶段)。
158
+ */
159
+ minimalPrompt: {
160
+ /** 是否启用极简提示词层。 */
161
+ enabled: true,
162
+ /** 替换后的 persona 文本;与极简模式相同可完全对齐语域。留空不替换。 */
163
+ persona: 'You are a helpful software engineer assistant.',
164
+ /** 屏蔽全局引导段(identity / source / web-surface)。 */
165
+ suppressSections: true,
166
+ },
144
167
  };
145
168
 
146
169
  /** 轻量配置校验/归一化:非法字段回退默认值,避免 config.json 或 remote.set
@@ -201,6 +224,15 @@ export function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
201
224
  compactionTools: stringListOrEmpty(rawBootstrap.compactionTools, db.compactionTools),
202
225
  maxTokens,
203
226
  };
227
+ const rawMP = raw.minimalPrompt !== null && typeof raw.minimalPrompt === 'object' && !Array.isArray(raw.minimalPrompt)
228
+ ? raw.minimalPrompt
229
+ : defaults.minimalPrompt;
230
+ const dm = defaults.minimalPrompt;
231
+ const minimalPrompt = {
232
+ enabled: boolValue(rawMP.enabled, dm.enabled),
233
+ persona: typeof rawMP.persona === 'string' ? rawMP.persona.trim() : dm.persona,
234
+ suppressSections: boolValue(rawMP.suppressSections, dm.suppressSections),
235
+ };
204
236
  return {
205
237
  enabled: boolValue(merged.enabled, defaults.enabled),
206
238
  presets: stringList(merged.presets, defaults.presets),
@@ -211,6 +243,7 @@ export function normalizeConfig(source, defaults = DEFAULT_CONFIG) {
211
243
  coreTools: stringList(merged.coreTools, defaults.coreTools),
212
244
  families,
213
245
  bootstrap,
246
+ minimalPrompt,
214
247
  };
215
248
  }
216
249
 
@@ -268,10 +301,37 @@ export function validateConfig(partial) {
268
301
  }
269
302
  }
270
303
  }
304
+ if (partial.minimalPrompt !== void 0) {
305
+ const m = partial.minimalPrompt;
306
+ if (m === null || typeof m !== 'object' || Array.isArray(m)) {
307
+ throw new TypeError('adaptive-perf config field "minimalPrompt" must be an object');
308
+ }
309
+ if (m.enabled !== void 0 && typeof m.enabled !== 'boolean') {
310
+ throw new TypeError('adaptive-perf config field "minimalPrompt.enabled" must be a boolean');
311
+ }
312
+ if (m.suppressSections !== void 0 && typeof m.suppressSections !== 'boolean') {
313
+ throw new TypeError('adaptive-perf config field "minimalPrompt.suppressSections" must be a boolean');
314
+ }
315
+ if (m.persona !== void 0 && typeof m.persona !== 'string') {
316
+ throw new TypeError('adaptive-perf config field "minimalPrompt.persona" must be a string');
317
+ }
318
+ }
271
319
  }
272
320
 
273
321
  // ── 纯函数(导出便于回归测试)────────────────────────────────────────────
274
322
 
323
+ /** 被极简提示词层屏蔽的全局引导段(name, order,与 dsh-app-boot /
324
+ * dsh-web-app / dsh-system-prompt 的注册一致)。 */
325
+ export const SECTION_SHADOWS = [
326
+ ['harness:identity', -100],
327
+ ['harness:source', -99],
328
+ ['app:web-surface', -98],
329
+ ];
330
+
331
+ /** persona 段的注册名与顺序(dsh-system-prompt 的 PERSONA_SECTION/PERSONA_ORDER)。 */
332
+ export const PERSONA_SECTION_NAME = 'deployment:persona';
333
+ export const PERSONA_SECTION_ORDER = 0;
334
+
275
335
  /** 提取一条用户消息的顶层文本(text 块拼接)。 */
276
336
  export function extractUserText(message) {
277
337
  if (message === null || typeof message !== 'object' || !Array.isArray(message.content)) return '';
@@ -457,9 +517,32 @@ export function apply(ctx, config) {
457
517
  }
458
518
  }
459
519
 
520
+ /** 应用/更新极简提示词层(persona 阴影 + 全局引导段屏蔽)。 */
521
+ function applyMinimalPrompt(a) {
522
+ for (const disposer of a.promptDisposers) {
523
+ try { disposer(); } catch {}
524
+ }
525
+ a.promptDisposers = [];
526
+ if (!cfg.enabled || !cfg.minimalPrompt.enabled) return;
527
+ const sp = a.agent.ctx.systemPrompt;
528
+ try {
529
+ if (cfg.minimalPrompt.suppressSections) {
530
+ for (const [name, order] of SECTION_SHADOWS) {
531
+ a.promptDisposers.push(sp.section({ name, order, text: '' }));
532
+ }
533
+ }
534
+ const persona = cfg.minimalPrompt.persona.trim();
535
+ if (persona.length > 0) {
536
+ a.promptDisposers.push(sp.section({ name: PERSONA_SECTION_NAME, order: PERSONA_SECTION_ORDER, text: persona }));
537
+ }
538
+ info(`agent ${a.agent.id}: minimal prompt layer active`);
539
+ } catch (error) {
540
+ warn(`minimalPrompt failed for agent ${a.agent.id}: ${error && error.message || error}`);
541
+ }
542
+ }
543
+
460
544
  /** 应用/更新工具族限制(lean 模式)。总开关关闭、族被禁用、已升级、
461
- * 已从配置删除的族一律放行。 */
462
- function applyFamilies(a) {
545
+ * 已从配置删除的族一律放行。 */ function applyFamilies(a) {
463
546
  if (!cfg.enabled || !cfg.leanByDefault) {
464
547
  for (const [id, disposer] of a.familyDisposers) {
465
548
  try { disposer(); } catch {}
@@ -519,7 +602,10 @@ export function apply(ctx, config) {
519
602
  const a = {
520
603
  agent,
521
604
  suppressDisposer: null,
605
+ promptDisposers: [],
522
606
  familyDisposers: new Map(),
607
+ bootstrapDisposer: null,
608
+ bootstrapKey: null,
523
609
  escalated: new Set(),
524
610
  visibleNames: new Set(),
525
611
  };
@@ -536,7 +622,9 @@ export function apply(ctx, config) {
536
622
  }
537
623
  agents.set(agent.id, a);
538
624
  applySuppression(a);
625
+ applyMinimalPrompt(a);
539
626
  applyFamilies(a);
627
+ syncBootstrap(a);
540
628
  info(`agent ${agent.id}: adaptive-perf active (preset ${String(agentPresets.composedPreset(agent.ctx))})`);
541
629
  }
542
630
 
@@ -549,6 +637,13 @@ export function apply(ctx, config) {
549
637
  if (a.suppressDisposer !== null) {
550
638
  try { a.suppressDisposer(); } catch {}
551
639
  }
640
+ for (const disposer of a.promptDisposers) {
641
+ try { disposer(); } catch {}
642
+ }
643
+ a.promptDisposers = [];
644
+ if (a.bootstrapDisposer !== null) {
645
+ try { a.bootstrapDisposer(); } catch {}
646
+ }
552
647
  for (const disposer of a.familyDisposers.values()) {
553
648
  try { disposer(); } catch {}
554
649
  }
@@ -635,37 +730,60 @@ export function apply(ctx, config) {
635
730
  return isTarget(agent);
636
731
  }
637
732
 
638
- // 持久事件增量喂入:晋升信号 / compaction 纪元。
733
+ /**
734
+ * bootstrap 阶段的保留工具集:bootstrap 工具对 + PTC 的直接调用工具
735
+ * (run_code)+ compaction 后恢复期的工作集。已晋升返回 null。
736
+ *
737
+ * 注意:不能靠过滤 system-prompt/assemble 的 assembly.tools 来实现——
738
+ * PTC 模式下 assembly.tools 只有 [run_code],Minimal 工具对不在其中,
739
+ * 过滤会触发降级(实测首轮模型仍看到 15 个工具)。正确做法是
740
+ * tools.restrict(与族限制同一机制):它同时驱动 API 目录和 PTC 的
741
+ * SDK 参考段,且天然保留 run_code(不在 deny 里)。
742
+ */
743
+ function bootstrapKeepSet(a) {
744
+ const phase = bootstrapPhaseOf(a.agent);
745
+ if (phase.promoted) return null;
746
+ const keep = new Set(cfg.bootstrap.tools);
747
+ if (a.visibleNames.has('run_code')) keep.add('run_code');
748
+ if (phase.boundary >= 0) for (const name of cfg.bootstrap.compactionTools) keep.add(name);
749
+ return keep;
750
+ }
751
+
752
+ /** 按当前阶段同步 bootstrap 的临时 restrict(幂等;仅阶段/配置变化时重挂)。 */
753
+ function syncBootstrap(a) {
754
+ const keep = bootstrapKeepSet(a);
755
+ const key = keep === null ? null : [...keep].sort().join(',');
756
+ if (key === a.bootstrapKey) return;
757
+ if (a.bootstrapDisposer !== null) {
758
+ try { a.bootstrapDisposer(); } catch {}
759
+ a.bootstrapDisposer = null;
760
+ a.bootstrapKey = null;
761
+ }
762
+ if (keep === null) return;
763
+ const deny = [...a.visibleNames].filter((name) => !keep.has(name));
764
+ if (deny.length === 0) return;
765
+ try {
766
+ a.bootstrapDisposer = a.agent.ctx.tools.restrict({ deny });
767
+ a.bootstrapKey = key;
768
+ info(`agent ${a.agent.id}: bootstrap restrict deny=${deny.length} keep=[${[...keep].join(',')}]`);
769
+ } catch (error) {
770
+ warn(`bootstrap restrict failed for agent ${a.agent.id}: ${String((error && error.message) || error)}`);
771
+ }
772
+ }
773
+
774
+ // 持久事件增量喂入:晋升信号 / compaction 纪元;阶段变化时同步 bootstrap 限制。
639
775
  ctx.on('session/event', (session, event) => {
640
776
  if (session === null || typeof session !== 'object' || typeof session.id !== 'string') return;
777
+ const before = bootstrapState.get(session.id);
641
778
  observePhase(bootstrapState, session.id, event);
642
- });
643
-
644
- // 请求装配时按阶段收窄工具目录。请求 #1(未晋升):只保留 bootstrap
645
- // 工具对(compaction/end 之后额外放行 compactionTools 核心工作集);
646
- // 已晋升:放行(leanByDefault 的族限制仍由 tools.restrict 生效)。
647
- ctx.on('system-prompt/assemble', async (assembly, _context, next) => {
648
- const resolved = await next();
649
- try {
650
- const agent = agentsService?.currentInitiator?.() ?? void 0;
651
- if (!bootstrapActiveFor(agent)) return resolved;
652
- const phase = bootstrapPhaseOf(agent);
653
- if (phase.promoted) return resolved;
654
- const keep = new Set(cfg.bootstrap.tools);
655
- if (phase.boundary >= 0) for (const name of cfg.bootstrap.compactionTools) keep.add(name);
656
- const filtered = filterBootstrapTools(resolved, keep);
657
- if (filtered.missing.length > 0) {
658
- // 目录缺失 bootstrap 工具对:降级为全目录(一次告警),绝不能
659
- // 把每个请求都卡死在空目录。
660
- warnBootstrapOnce(`bootstrap tools missing from catalog (${JSON.stringify(filtered.missing)}); exposing the full catalog`);
661
- return resolved;
779
+ const a = agents.get(session.id);
780
+ if (a !== void 0) {
781
+ const after = bootstrapState.get(session.id);
782
+ if (before === void 0 || after === void 0 || before.promoted !== after.promoted || before.boundary !== after.boundary) {
783
+ syncBootstrap(a);
662
784
  }
663
- return { ...resolved, tools: filtered.tools };
664
- } catch (error) {
665
- warnBootstrapOnce(`bootstrap tool filter failed, exposing the full catalog: ${String((error && error.message) || error)}`);
666
- return resolved;
667
785
  }
668
- }, { prepend: true });
786
+ });
669
787
 
670
788
  // 首轮剥离自动注入的上下文(技能目录提醒 / AGENTS.md 摘要)。prepend +
671
789
  // 根作用域注册保证是最后一道变换(后注册的注入者无法再补回)。
@@ -722,7 +840,9 @@ export function apply(ctx, config) {
722
840
  cfg = normalizeConfig({ ...patchConfig, ...merged });
723
841
  info('config hot-updated; recomputing restrictions for live agents');
724
842
  // 补挂此前创建的会话(例如插件从 disabled 切换为 enabled,或新增了
725
- // 目标 preset):新符合条件的 agent 立即接管。
843
+ // 目标 preset):新符合条件的 agent 立即接管(handleAgent 已应用全部
844
+ // 层,下面的重算只针对此前已接管的 agent,避免同一 onUpdate 内重复)。
845
+ const existing = new Set(agents.keys());
726
846
  if (agentsService !== void 0 && typeof agentsService.list === 'function') {
727
847
  try {
728
848
  for (const agent of agentsService.list()) handleAgent(agent);
@@ -730,9 +850,13 @@ export function apply(ctx, config) {
730
850
  warn(`config-update agent sweep failed: ${error && error.message || error}`);
731
851
  }
732
852
  }
733
- for (const a of agents.values()) {
853
+ for (const agentId of existing) {
854
+ const a = agents.get(agentId);
855
+ if (a === void 0) continue;
734
856
  applySuppression(a);
857
+ applyMinimalPrompt(a);
735
858
  applyFamilies(a);
859
+ syncBootstrap(a);
736
860
  }
737
861
  },
738
862
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chaoset/adaptive-perf",
3
- "version": "0.3.1",
3
+ "version": "0.4.0",
4
4
  "description": "DSH host plugin: bring standard / PTC presets to minimal-mode-level performance via runtime-context suppression and adaptive tool-catalog trimming with demand-driven escalation",
5
5
  "type": "module",
6
6
  "license": "MIT",