dsh-subagent-profile 0.4.0 → 0.5.1
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 +16 -57
- package/README.zh.md +16 -57
- package/index.mjs +97 -122
- package/lib/client.js +631 -88
- package/lib/core/cleanup.mjs +33 -0
- package/lib/core/data-maintenance.mjs +139 -0
- package/lib/core/decision-trace.mjs +20 -1
- package/lib/core/delegation.mjs +16 -0
- package/lib/core/dispatch-gates.mjs +122 -9
- package/lib/core/dispatch-schema.mjs +45 -7
- package/lib/core/dispatch-tool.mjs +30 -46
- package/lib/core/evolution-advice.mjs +31 -10
- package/lib/core/evolution-assets.mjs +13 -0
- package/lib/core/evolution-canary.mjs +427 -0
- package/lib/core/evolution-draft.mjs +21 -0
- package/lib/core/evolution-engine.mjs +176 -31
- package/lib/core/evolution-generate.mjs +7 -23
- package/lib/core/evolution-lag.mjs +78 -0
- package/lib/core/evolution-ledger.mjs +4 -1
- package/lib/core/evolution-routes.mjs +157 -16
- package/lib/core/http-routes.mjs +22 -7
- package/lib/core/model-policy.mjs +104 -0
- package/lib/core/presets-sync.mjs +24 -2
- package/lib/core/profile-directory.mjs +17 -0
- package/lib/core/profile-provider.mjs +7 -2
- package/lib/core/profiles-query.mjs +178 -0
- package/lib/core/profiles-store.mjs +40 -6
- package/lib/core/pure.mjs +1 -1
- package/lib/core/reminder-store.mjs +37 -3
- package/lib/core/snapshots.mjs +148 -0
- package/package.json +2 -2
- package/presets/orchestrator-v2/NOTICE +7 -0
- package/presets/{orchestrator → orchestrator-v2}/agent.cordis.yml +78 -7
- package/presets/orchestrator-v2/custom-bash.mjs +213 -0
- package/presets/orchestrator-v2/preset.yml +2 -0
- package/presets/orchestrator-v2/tool-bootstrap.mjs +620 -0
- package/presets/orchestrator/preset.yml +0 -2
package/index.mjs
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
// index.mjs — dsh-subagent-profile 宿主侧插件 bundle(只做装配)。宿主导入面收敛在
|
|
2
|
-
// lib/core/shims.mjs,装配块按模块拆分驻留 lib/core
|
|
3
|
-
// section 文本与门控逐字不变,`enabled` 始终经 getter 注入(门控读取当前值)。
|
|
4
|
-
|
|
5
|
-
import { readdirSync, renameSync, rmSync } from 'node:fs';
|
|
2
|
+
// lib/core/shims.mjs,装配块按模块拆分驻留 lib/core/;`enabled` 经 getter 实时读取。
|
|
6
3
|
import { createRequire } from 'node:module';
|
|
7
4
|
import { join } from 'node:path';
|
|
8
5
|
import { syncBundledPresets } from './lib/core/presets-sync.mjs';
|
|
9
6
|
import { dshHome, createProfileStore } from './lib/core/profiles-store.mjs';
|
|
7
|
+
import { removeOwnedData, cleanRemovedBackups } from './lib/core/cleanup.mjs';
|
|
10
8
|
import { createHttpRoutes } from './lib/core/http-routes.mjs';
|
|
11
9
|
import { createProfileProvider } from './lib/core/profile-provider.mjs';
|
|
12
10
|
import { createDispatchTool } from './lib/core/dispatch-tool.mjs';
|
|
@@ -16,20 +14,24 @@ import { createFailureLedger } from './lib/core/decision-trace.mjs';
|
|
|
16
14
|
import { createEvolutionLedger } from './lib/core/evolution-ledger.mjs';
|
|
17
15
|
import { createAdoptionTracker } from './lib/core/adoption-tracker.mjs';
|
|
18
16
|
import { createReminderStore } from './lib/core/reminder-store.mjs';
|
|
17
|
+
import { createSnapshotStore } from './lib/core/snapshots.mjs';
|
|
19
18
|
import { mintReminder, mintUnadoptedReminder } from './lib/core/adoption-reminder.mjs';
|
|
20
19
|
import { createBackgroundLedger } from './lib/core/background-ledger.mjs';
|
|
21
20
|
import { createDraftsStore } from './lib/core/drafts-store.mjs';
|
|
22
21
|
import { assessDraftProfile } from './lib/core/draft-gates.mjs';
|
|
23
22
|
import { createEscapeStore, recordEscapeAllowProvider } from './lib/core/escape.mjs';
|
|
24
23
|
import { resolveWhitelist, FALLBACK_WHITELIST } from './lib/core/whitelist.mjs';
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
24
|
+
import { readModelSelectionPolicy, appendOfficialVisibilityLine } from './lib/core/model-policy.mjs';
|
|
25
|
+
import { profileSectionText } from './lib/core/profile-directory.mjs';
|
|
26
|
+
import { createProfilesQueryTool } from './lib/core/profiles-query.mjs';
|
|
27
|
+
import { budgetSectionText } from './lib/core/dispatch-gates.mjs';
|
|
28
|
+
import { refreshSummaries } from './lib/core/evolution-advice.mjs';
|
|
27
29
|
import { createEvolutionAssembly } from './lib/core/evolution-engine.mjs';
|
|
28
30
|
|
|
29
31
|
export const name = 'dsh-subagent-profile';
|
|
30
32
|
export const inject = ['subagents', 'tools', 'agents'];
|
|
31
33
|
|
|
32
|
-
// 自装 bundled 的 "orchestrator" 预设到 agent-presets 根(与官方自安装相同)。
|
|
34
|
+
// 自装 bundled 的 "orchestrator-v2" 预设到 agent-presets 根(与官方自安装相同)。
|
|
33
35
|
// 幂等:字节一致跳过、bundle 变更重写;fail-soft:写入被拒时工具与设置页照常工作。
|
|
34
36
|
function syncBundledPresetsToHome(ctx) {
|
|
35
37
|
try {
|
|
@@ -37,40 +39,14 @@ function syncBundledPresetsToHome(ctx) {
|
|
|
37
39
|
const sync = syncBundledPresets(presetRoot);
|
|
38
40
|
for (const { id, error } of sync.failed) ctx.logger.warn(`[dsh-subagent-profile] preset ${id} sync failed: ${error}`);
|
|
39
41
|
if (sync.synced.length > 0) ctx.logger.info(`[dsh-subagent-profile] presets synced into ${presetRoot}: ${sync.synced.join(', ')}`);
|
|
42
|
+
if (sync.archived.length > 0) ctx.logger.info(`[dsh-subagent-profile] legacy preset archived into ${presetRoot}: ${sync.archived.join(', ')}`);
|
|
40
43
|
if (sync.userModified.length > 0) ctx.logger.warn(`[dsh-subagent-profile] presets left untouched (user-modified): ${sync.userModified.join(', ')}`);
|
|
41
44
|
} catch (error) {
|
|
42
45
|
ctx.logger.warn('[dsh-subagent-profile] preset sync failed:', error instanceof Error ? error.message : String(error));
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
|
|
46
|
-
//
|
|
47
|
-
// 在禁用/热重载/退出时都可能触发,硬删除会静默丢用户数据)。备份保留可恢复副本;
|
|
48
|
-
// 规范路径清空后重启按需重建。fail-soft:改名失败不抛。
|
|
49
|
-
function removeOwnedData(home) {
|
|
50
|
-
const stamp = Date.now();
|
|
51
|
-
for (const file of ['subagent-profiles.json', 'subagent-profiles.state.json', 'subagent-profiles.failed-traces.json']) {
|
|
52
|
-
try { renameSync(join(home, file), join(home, `${file}.removed-${stamp}`)); } catch { /* 源不存在(正常首启)等:best effort */ }
|
|
53
|
-
}
|
|
54
|
-
try { renameSync(join(home, '.agent-presets', 'orchestrator'), join(home, '.agent-presets', `orchestrator.removed-${stamp}`)); } catch { /* best effort */ }
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 启动清理 .removed-* 备份残留(只清本插件前缀,防热重载反复产生新备份积累);
|
|
58
|
-
// 卸载后用户手动改回原名的场景不受影响。fail-soft。
|
|
59
|
-
function cleanRemovedBackups(home) {
|
|
60
|
-
for (const dir of [home, join(home, '.agent-presets')]) {
|
|
61
|
-
let names;
|
|
62
|
-
try { names = readdirSync(dir); } catch { continue; }
|
|
63
|
-
for (const name of names) {
|
|
64
|
-
try {
|
|
65
|
-
if (name.startsWith('subagent-profiles.') && name.includes('.removed-')) {
|
|
66
|
-
rmSync(join(dir, name), { force: true });
|
|
67
|
-
} else if (name.startsWith('orchestrator.removed-')) {
|
|
68
|
-
rmSync(join(dir, name), { recursive: true, force: true });
|
|
69
|
-
}
|
|
70
|
-
} catch { /* best effort */ }
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
49
|
+
// 卸载清理与启动清理辅助驻留 lib/core/cleanup.mjs(装配文件行门)。
|
|
74
50
|
|
|
75
51
|
// parent_adopted 装配:tracker 工厂 + 根 ctx 订阅 session/event(按 session.id 过滤),
|
|
76
52
|
// 卸载退订并清理定时器。onDecision 经 getRefreshAdvice 延迟读取(先判定后聚合时为空
|
|
@@ -82,16 +58,16 @@ function setupAdoptionTracker(ctx, home, getRefreshAdvice, onUnadopted) {
|
|
|
82
58
|
onDecision: () => { try { getRefreshAdvice()(); } catch { /* 聚合重算失败不影响判定(fail-soft) */ } },
|
|
83
59
|
onDecided: onUnadopted,
|
|
84
60
|
});
|
|
85
|
-
const onSessionEvent = (session, event) => adoptionTracker.handleEvent(session, event);
|
|
86
|
-
ctx.on('session/event', onSessionEvent);
|
|
61
|
+
const onSessionEvent = (session, event) => adoptionTracker.handleEvent(session, event); ctx.on('session/event', onSessionEvent);
|
|
87
62
|
ctx.effect(() => () => { ctx.off('session/event', onSessionEvent); adoptionTracker.dispose(); });
|
|
88
63
|
return adoptionTracker;
|
|
89
64
|
}
|
|
90
65
|
|
|
91
66
|
// 卸载接线:注销工具 + 清空台账 + 级联取消 + 删数据文件与预设目录。agent/disposed
|
|
92
67
|
// 挂钩按父释放并发/token 记账,并把该父会话未决采纳判定判「明确未采纳」。
|
|
93
|
-
function registerTeardown(ctx, dispatch, ledger, guard, home, backgroundLedger, adoptionTracker) {
|
|
68
|
+
function registerTeardown(ctx, dispatch, profilesQuery, ledger, guard, home, backgroundLedger, adoptionTracker) {
|
|
94
69
|
ctx.effect(() => dispatch.dispose);
|
|
70
|
+
ctx.effect(() => profilesQuery.dispose);
|
|
95
71
|
ctx.effect(() => ledger.clear);
|
|
96
72
|
ctx.effect(() => backgroundLedger.clear);
|
|
97
73
|
ctx.effect(() => () => {
|
|
@@ -101,10 +77,7 @@ function registerTeardown(ctx, dispatch, ledger, guard, home, backgroundLedger,
|
|
|
101
77
|
});
|
|
102
78
|
const onAgentDisposed = ({ agent }) => {
|
|
103
79
|
const parentSessionId = agent?.session?.header?.id;
|
|
104
|
-
if (parentSessionId !== undefined && parentSessionId !== null && parentSessionId !== '') {
|
|
105
|
-
guard.resetParent(parentSessionId);
|
|
106
|
-
adoptionTracker.parentDisposed(parentSessionId);
|
|
107
|
-
}
|
|
80
|
+
if (parentSessionId !== undefined && parentSessionId !== null && parentSessionId !== '') { guard.resetParent(parentSessionId); adoptionTracker.parentDisposed(parentSessionId); }
|
|
108
81
|
};
|
|
109
82
|
ctx.on('agent/disposed', onAgentDisposed);
|
|
110
83
|
ctx.effect(() => () => ctx.off('agent/disposed', onAgentDisposed));
|
|
@@ -139,56 +112,45 @@ function provideProfileService(ctx, store) {
|
|
|
139
112
|
});
|
|
140
113
|
}
|
|
141
114
|
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
// dispatch → 必空;回退 无 agentPresets → 保守不注入。
|
|
147
|
-
function sectionGatePasses(ctx, getEnabled, context) {
|
|
115
|
+
// 系统提示门控(门控拆分):dispatch:profiles 按「能力」判据(schemas 含 dispatch 即注入);
|
|
116
|
+
// orchestrator:mode / dispatch:budget 保持「模式」判据(composedPreset==='orchestrator-v2')。禁止成对放宽共享 gate。
|
|
117
|
+
// evolution:advice 自本轮起模型侧恒空(下架),不挂任何注入门控——见注册处注释。
|
|
118
|
+
function profileSectionGate(ctx, getEnabled, context) {
|
|
148
119
|
if (!getEnabled()) return false;
|
|
149
|
-
//
|
|
120
|
+
// schemas(agent) 明确不含 dispatch → 必空(否决);缺失 → 注入机会 + debug。
|
|
150
121
|
if (context !== undefined && context.agent !== undefined) {
|
|
151
122
|
const schemas = ctx.tools.schemas(context.agent);
|
|
152
|
-
if (Array.isArray(schemas)
|
|
153
|
-
|
|
154
|
-
|
|
123
|
+
if (Array.isArray(schemas)) return schemas.some((s) => s && s.name === 'dispatch');
|
|
124
|
+
}
|
|
125
|
+
if (ctx.logger !== undefined && typeof ctx.logger.debug === 'function') {
|
|
126
|
+
ctx.logger.debug('[dsh-subagent-profile] dispatch:profiles 门控:schemas 不可用,按注入机会处理');
|
|
127
|
+
}
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
// 编排者专属 gate:与 0.4.0 原判据一致(enabled + 否决 + composedPreset,预设名随 V2 收编更新为 orchestrator-v2)。
|
|
131
|
+
function orchestrationGate(ctx, getEnabled, context) {
|
|
132
|
+
if (!getEnabled()) return false;
|
|
133
|
+
if (context !== undefined && context.agent !== undefined) {
|
|
134
|
+
const schemas = ctx.tools.schemas(context.agent);
|
|
135
|
+
if (Array.isArray(schemas) && !schemas.some((s) => s && s.name === 'dispatch')) return false;
|
|
155
136
|
}
|
|
156
|
-
// 主判据:preset 特征 —— composedPreset(agentCtx) === 'orchestrator'
|
|
137
|
+
// 主判据:preset 特征 —— composedPreset(agentCtx) === 'orchestrator-v2'(精确匹配新名;旧名 orchestrator 已由 V2 收编取代,不构成注入判据)。
|
|
157
138
|
const agentPresets = ctx.get('agentPresets');
|
|
158
139
|
if (agentPresets !== undefined && typeof agentPresets.composedPreset === 'function') {
|
|
159
140
|
const agentCtx = context !== undefined && context.agent !== undefined && context.agent.ctx !== undefined
|
|
160
141
|
? context.agent.ctx
|
|
161
142
|
: ctx;
|
|
162
|
-
try { return agentPresets.composedPreset(agentCtx) === 'orchestrator'; }
|
|
143
|
+
try { return agentPresets.composedPreset(agentCtx) === 'orchestrator-v2'; }
|
|
163
144
|
catch { return false; }
|
|
164
145
|
}
|
|
165
146
|
// 无 agentPresets → 无法判别 → 保守不注入。
|
|
166
147
|
return false;
|
|
167
148
|
}
|
|
168
149
|
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
if (!gate(context)) return '';
|
|
174
|
-
const rows = applyProfileStats(profileDirectoryRows(store), profileStatsFromSummaries(store, summaries));
|
|
175
|
-
if (rows.length === 0) return '';
|
|
176
|
-
const lines = rows.map((p) => {
|
|
177
|
-
const desc = p.description.length > 0 ? `"${p.description}"` : '(无描述)';
|
|
178
|
-
const meta = [];
|
|
179
|
-
if (p.preset !== undefined) meta.push(`preset: ${p.preset}`);
|
|
180
|
-
else if (p.model !== undefined) meta.push(`model: ${p.model}`);
|
|
181
|
-
if (p.tokenTier !== undefined) meta.push(`tier: ${p.tokenTier}`);
|
|
182
|
-
if (p.avgCost !== undefined) meta.push(`平均 ${p.avgCost} 元/次`);
|
|
183
|
-
if (p.successRate !== undefined) meta.push(`成功率 ${p.successRate}${p.n !== undefined ? ` (N=${p.n})` : ''}`);
|
|
184
|
-
return `- ${p.id}: ${desc}${meta.length > 0 ? ` (${meta.join(' · ')})` : ''}`;
|
|
185
|
-
});
|
|
186
|
-
const note = '- 选择方案时先匹配任务复杂度与方案描述的能力边界,成本只在能力都胜任的方案之间比较——复杂任务不得为省 token 改用能力不足的便宜方案。\n- 别把 1-2 步即可自查/可搜完的小事委派出去 —— 几分钟内能自查完的直接做。\n- 优先使用已保存的 profile;只有少数字段需要临时覆盖时才传 per-call 参数。查资料/汇总/读文件等轻任务优先 cheap 或 flash。';
|
|
187
|
-
return `Available dispatch profiles (dispatch.profile):\n${lines.join('\n')}\n${note}`;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// orchestrator:mode section 文本(逐字保留;与 profiles 同门控)。
|
|
191
|
-
const ORCHESTRATOR_MODE_TEXT = '本机已安装 dsh-subagent-profile 插件的「编排者模式」agent preset:新建会话的预设选择器中可选「编排者模式」。该模式把 Agent 定位为主协调者——拆解任务后按场景用 dispatch(内置 swap-standard=标准编码、researcher=调研检索,可在「子 Agent 方案」设置页自定义)与 subagent/subagent_fork/workflow 委派给子 Agent,再整合结果。preset 文件由插件维护于 ~/.dsh/.agent-presets,安装/升级时自动同步;用户提到「编排者模式 / orchestrator / 主协调模式」时即指本预设,请据此协作。';
|
|
150
|
+
// orchestrator:mode section 文本(编排者模式 V2 定稿文案;与 profiles 同门控。
|
|
151
|
+
// 追加官方错误对照句:官方全英文报错补中文行动引导,与 dispatch 的 stopReason
|
|
152
|
+
// 映射共同构成错误闭环)。
|
|
153
|
+
const ORCHESTRATOR_MODE_TEXT = '本机已安装「编排者模式 V2」agent 预设(字段 orchestrator-v2):新建会话可选。把自己定位为主协调者——拆解任务后用 dispatch(方案)或官方 subagent/workflow 委派子 Agent,再整合结果;第一期采用 Minimal 双工具锚定轨迹,稳定后升满编排能力。需要 1-2 步自查的小事直接自己干,别派;方案在「子 Agent 方案」设置页管理。官方子代理工具若报 “not allowed for this Session”,意为该模型不在本会话白名单——改用 list_subagent_models 查允许路由,或转用 dispatch(profile)。官方工具没出现=模型选择未启用。';
|
|
192
154
|
|
|
193
155
|
// 建议注入候选池:受信任预设白名单(解析失败回退内置名单)。不叠加逃生舱放行集。
|
|
194
156
|
async function resolveAdviceWhitelist(ctx) {
|
|
@@ -199,61 +161,58 @@ async function resolveAdviceWhitelist(ctx) {
|
|
|
199
161
|
}
|
|
200
162
|
}
|
|
201
163
|
|
|
202
|
-
|
|
164
|
+
// 回滚建议提醒 mint:仅 regress 判定时触发一次(提醒写失败吞掉);标题人话化,
|
|
165
|
+
// 内容引导到自进化与建议页签查看建议(只读跳转)。
|
|
166
|
+
async function setupEvolution(ctx, home, store, catalog, evoLedger, getEvolutionAdvice, getReminderStore) {
|
|
203
167
|
const adviceWhitelist = await resolveAdviceWhitelist(ctx);
|
|
204
168
|
const adviceEnv = { summariesFile: join(home, 'subagent-evolution', 'summaries.json'), dispatchFile: join(home, 'subagent-evolution', 'dispatch.jsonl'), whitelist: adviceWhitelist, logger: ctx.logger, onLoss: () => evoLedger.markMigrationLoss() };
|
|
205
|
-
const
|
|
169
|
+
const mintRollbackReminder = (asset) => mintReminder(getReminderStore(), {
|
|
170
|
+
severity: 'P2', kind: 'suggestion-rollback',
|
|
171
|
+
title: '自进化撤销建议:' + ((asset && asset.applied_config && typeof asset.applied_config.name === 'string' && asset.applied_config.name !== '') ? asset.applied_config.name : ((asset && typeof asset.name === 'string' && asset.name !== '') ? asset.name : '未知方案')),
|
|
172
|
+
detail: '该方案调整后观察期表现下降,建议撤销。可在「自进化与建议」页签查看并处理。', sessionId: null,
|
|
173
|
+
});
|
|
174
|
+
const { adviceSource, evolution } = createEvolutionAssembly({ ctx, home, store, catalog, evoLedger, adviceWhitelist, getEvolutionAdvice, getReminderStore: mintRollbackReminder, pluginVersion: readPluginVersion() });
|
|
206
175
|
return { adviceEnv, adviceSource, evolution };
|
|
207
176
|
}
|
|
208
177
|
|
|
209
|
-
// 系统提示各 section
|
|
178
|
+
// 系统提示各 section:profiles 段挂 profileSectionGate(能力判据),orchestrator:mode
|
|
179
|
+
// 与 dispatch:budget 挂 orchestrationGate(模式判据,编排者专属);evolution:advice
|
|
180
|
+
// 自本轮起模型侧恒空(下架,见注册处注释)。`enabled` 经 getter 实时读取。
|
|
210
181
|
function registerSystemPromptSections(ctx, store, getEnabled, getEvolutionAdvice, adviceEnv, guard) {
|
|
211
182
|
const pluginSystemPrompt = ctx.get('systemPrompt');
|
|
212
183
|
if (pluginSystemPrompt === undefined) return;
|
|
213
|
-
const
|
|
184
|
+
const profileGate = (context) => profileSectionGate(ctx, getEnabled, context);
|
|
185
|
+
const orchestratorGate = (context) => orchestrationGate(ctx, getEnabled, context);
|
|
214
186
|
pluginSystemPrompt.section({
|
|
215
187
|
name: 'dispatch:profiles',
|
|
216
188
|
order: 116.5,
|
|
217
|
-
text: (context) =>
|
|
218
|
-
let summaries = null;
|
|
219
|
-
try { summaries = readSummaries(adviceEnv.summariesFile, adviceEnv.logger, { onLoss: adviceEnv.onLoss }); } catch { /* 统计只增强,不影响目录注入 */ }
|
|
220
|
-
return profileSectionText(store, gate, context, summaries);
|
|
221
|
-
},
|
|
189
|
+
text: (context) => appendOfficialVisibilityLine(profileSectionText(store, profileGate, context), ctx, context, ctx.logger),
|
|
222
190
|
});
|
|
223
|
-
// 宣告自装的 orchestrator
|
|
224
|
-
// 门控与 profiles 段相同——只有可派发的 agent 才看得到。
|
|
191
|
+
// 宣告自装的 orchestrator-v2 预设(门控与 0.4.0 相同:仅编排者 agent 可见)。
|
|
225
192
|
pluginSystemPrompt.section({
|
|
226
193
|
name: 'orchestrator:mode',
|
|
227
194
|
order: 117,
|
|
228
|
-
text: (context) => (
|
|
195
|
+
text: (context) => (orchestratorGate(context) ? ORCHESTRATOR_MODE_TEXT : ''),
|
|
229
196
|
});
|
|
230
|
-
//
|
|
197
|
+
// 派发优化建议段(模型侧下架):建议文本曾注入模型可见面却自称「仅供人类
|
|
198
|
+
// 参考」——噪声 + 诱导降档,故恒不注入(text 恒 '',不再过任何门控/开关)。
|
|
199
|
+
// 注册结构保留(section 名与 order 不变,客户端依赖其存在性);人类面板
|
|
200
|
+
// (建议卡/提醒中心/审计行)经 http-routes 读取同一数据源,不受影响。勿在此恢复注入。
|
|
231
201
|
pluginSystemPrompt.section({
|
|
232
202
|
name: 'evolution:advice',
|
|
233
203
|
order: 116.8,
|
|
234
|
-
text: (
|
|
235
|
-
if (!getEvolutionAdvice() || !gate(context)) return '';
|
|
236
|
-
try {
|
|
237
|
-
return buildAdviceText(adviceEnv);
|
|
238
|
-
} catch (error) {
|
|
239
|
-
// 候选校验失败(如逃生舱放行的非 system 预设)不再静默空转——logger.warn
|
|
240
|
-
// 留痕 + 注入段标注「建议暂不可用及原因」,父 Agent 可见、派发行为不受影响。
|
|
241
|
-
const reason = error instanceof Error ? error.message : String(error);
|
|
242
|
-
adviceEnv.logger.warn(`[dsh-subagent-profile] evolution:advice 生成失败:${reason}`);
|
|
243
|
-
return `(派发优化建议暂不可用:${reason}。派发行为不受影响。)`;
|
|
244
|
-
}
|
|
245
|
-
},
|
|
204
|
+
text: () => '',
|
|
246
205
|
});
|
|
247
|
-
//
|
|
206
|
+
// 派发预算余量段(三态文案,见 dispatch-gates.mjs):门控 = orchestrator-v2 预设;
|
|
207
|
+
// 无父 sessionId 不注入(无法关联具体会话)。
|
|
248
208
|
pluginSystemPrompt.section({
|
|
249
209
|
name: 'dispatch:budget',
|
|
250
210
|
order: 116.7,
|
|
251
211
|
text: (context) => {
|
|
252
|
-
if (!
|
|
212
|
+
if (!orchestratorGate(context)) return '';
|
|
253
213
|
const parentSessionId = context?.agent?.session?.header?.id;
|
|
254
214
|
if (typeof parentSessionId !== 'string' || parentSessionId === '') return '';
|
|
255
|
-
|
|
256
|
-
return `派发预算余量:本会话在途 ${b.concurrency}/${b.maxConcurrent},累计 token ${b.tokens}/${b.maxParentTokens},剩余 ${Math.max(0, b.maxParentTokens - b.tokens)} token。`;
|
|
215
|
+
return budgetSectionText(guard.snapshot(parentSessionId));
|
|
257
216
|
},
|
|
258
217
|
});
|
|
259
218
|
}
|
|
@@ -263,7 +222,7 @@ function registerSystemPromptSections(ctx, store, getEnabled, getEvolutionAdvice
|
|
|
263
222
|
// webServer 可选——无头部署保留 dispatch 工具、只丢设置页。webServer 的激活
|
|
264
223
|
// (listen)是异步的,可能晚于本插件 inject 依赖解析完成,故在等它的 inject
|
|
265
224
|
// 子 scope 内注册(apply 时 ctx.get 会读到 undefined)。
|
|
266
|
-
function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, catalog, ledger, backgroundLedger, draftsStore, applyDraft, getAudit, getEvolutionAdvice, setEvolutionAdvice, getEscapeEnabled, setEscapeEnabled, escape, refreshAdvice, summariesFile, dispatchFile, adviceWhitelist, previewDraft, reminderStore, adviceSource, evolution) {
|
|
225
|
+
function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, catalog, ledger, backgroundLedger, draftsStore, applyDraft, getAudit, getEvolutionAdvice, setEvolutionAdvice, getEscapeEnabled, setEscapeEnabled, escape, refreshAdvice, summariesFile, dispatchFile, adviceWhitelist, previewDraft, reminderStore, adviceSource, evolution, snapshots, recordAudit) {
|
|
267
226
|
ctx.inject(['webServer'], (scope) => {
|
|
268
227
|
scope.effect(createHttpRoutes({
|
|
269
228
|
webServer: scope.webServer,
|
|
@@ -290,6 +249,8 @@ function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, ca
|
|
|
290
249
|
reminderStore,
|
|
291
250
|
adviceSource,
|
|
292
251
|
evolution,
|
|
252
|
+
snapshots,
|
|
253
|
+
recordAudit,
|
|
293
254
|
logger: ctx.logger,
|
|
294
255
|
}), 'dsh-subagent-profile: settings routes');
|
|
295
256
|
});
|
|
@@ -338,7 +299,7 @@ function setupEscape(ctx, store, evoLedger, home, getReminderStore) {
|
|
|
338
299
|
|
|
339
300
|
// `dispatch` 工具装配(defineTool + execute + syncTool)。须在 HTTP inject 之前
|
|
340
301
|
// 构造,使 createHttpRoutes 能拿 dispatch.syncTool 供 /set-enabled 注册/注销。
|
|
341
|
-
function createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroundLedger, adoptionTracker, getEscapeSet, getEnabled, getEvolutionAdvice) {
|
|
302
|
+
function createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroundLedger, adoptionTracker, getEscapeSet, getEnabled, getEvolutionAdvice, snapshots) {
|
|
342
303
|
return createDispatchTool({
|
|
343
304
|
register: (tool) => ctx.tools.register(tool),
|
|
344
305
|
store,
|
|
@@ -354,6 +315,21 @@ function createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroun
|
|
|
354
315
|
adoptionTracker,
|
|
355
316
|
getEscapeSet,
|
|
356
317
|
getEvolutionAdvice,
|
|
318
|
+
snapshots,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// profiles_query 工具装配(apply 前抽出):syncTool 机制自动可见、不依赖静态段;
|
|
323
|
+
// 与 dispatch 共用 enabled 开关(/set-enabled 一并注册/注销)。
|
|
324
|
+
function createProfilesQuery(ctx, store, catalog, adviceEnv, getEnabled) {
|
|
325
|
+
return createProfilesQueryTool({
|
|
326
|
+
register: (tool) => ctx.tools.register(tool),
|
|
327
|
+
store,
|
|
328
|
+
catalog,
|
|
329
|
+
summariesFile: adviceEnv.summariesFile,
|
|
330
|
+
onLoss: adviceEnv.onLoss,
|
|
331
|
+
logger: ctx.logger,
|
|
332
|
+
getEnabled,
|
|
357
333
|
});
|
|
358
334
|
}
|
|
359
335
|
|
|
@@ -364,15 +340,14 @@ async function applyDraftProfile({ ctx, store, catalog, getEscapeSet, evoLedger,
|
|
|
364
340
|
const failed = checks.find((c) => c.verdict === 'fail');
|
|
365
341
|
throw new Error(failed !== undefined && typeof failed.reason === 'string' ? failed.reason : 'draft 未通过安全检查');
|
|
366
342
|
}
|
|
367
|
-
store.
|
|
343
|
+
store.saveProfile({ ...clean, persisted: true });
|
|
368
344
|
const persisted = store.persistProfiles();
|
|
369
345
|
evoLedger.recordGovernanceAudit({ kind: 'draft-apply', profileId: clean.id, source: typeof draft.source === 'string' ? draft.source : 'human' });
|
|
370
346
|
return { id: clean.id, persisted: persisted.persisted };
|
|
371
347
|
}
|
|
372
348
|
|
|
373
349
|
export async function apply(ctx) {
|
|
374
|
-
const home = dshHome();
|
|
375
|
-
cleanRemovedBackups(home); // 上个生命周期留下的 .removed-* 备份残留。
|
|
350
|
+
const home = dshHome(); cleanRemovedBackups(home); // 上个生命周期留下的 .removed-* 备份残留。
|
|
376
351
|
// reminderStore 变量提前声明:evoLedger.onAlert 与 escape mint 钩子延迟读取。
|
|
377
352
|
let reminderStore;
|
|
378
353
|
// 派发台账 + 审计分级须在 store 之前构造:store 的治理审计钩子指向其 markGovernanceFailure。
|
|
@@ -382,7 +357,6 @@ export async function apply(ctx) {
|
|
|
382
357
|
let enabled = store.loadEnabled();
|
|
383
358
|
let evolutionAdvice = store.loadEvolutionAdvice();
|
|
384
359
|
store.loadProfiles();
|
|
385
|
-
// 自装 bundled 的 orchestrator 预设(幂等、fail-soft)。
|
|
386
360
|
syncBundledPresetsToHome(ctx);
|
|
387
361
|
const catalog = createSharedCatalog(ctx);
|
|
388
362
|
const ledger = createFailureLedger({ warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`), stateFile: join(home, 'subagent-profiles.failed-traces.json') });
|
|
@@ -390,16 +364,18 @@ export async function apply(ctx) {
|
|
|
390
364
|
const backgroundLedger = createBackgroundLedger({ warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`) });
|
|
391
365
|
const draftsStore = createDraftsStore({ dshHome: home, onGovernanceFailure: () => evoLedger.markGovernanceFailure() });
|
|
392
366
|
reminderStore = createReminderStore({ dshHome: home, audit: (entry) => evoLedger.recordGovernanceAudit(entry), warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`) });
|
|
393
|
-
|
|
394
|
-
const
|
|
395
|
-
|
|
367
|
+
// 配置快照域:开关关恒跳过(store.loadSnapshotEnabled 默认关);写审计与治理失败同源。
|
|
368
|
+
const snapshots = createSnapshotStore({ dshHome: home, logger: ctx.logger, getEnabled: () => store.loadSnapshotEnabled(), recordAudit: (entry) => evoLedger.recordGovernanceAudit(entry), onGovernanceFailure: () => evoLedger.markGovernanceFailure() });
|
|
369
|
+
let refreshAdvice = () => {}; const adoptionTracker = setupAdoptionTracker(ctx, home, () => refreshAdvice, (rec) => mintUnadoptedReminder(reminderStore, evoLedger, rec));
|
|
370
|
+
const dispatch = createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroundLedger, adoptionTracker, escapeCtl.getEscapeSet, () => enabled, () => evolutionAdvice, snapshots);
|
|
396
371
|
provideProfileService(ctx, store);
|
|
397
|
-
const { adviceEnv, adviceSource, evolution } = await setupEvolution(ctx, home, store, catalog, evoLedger, () => evolutionAdvice);
|
|
398
|
-
|
|
399
|
-
|
|
372
|
+
const { adviceEnv, adviceSource, evolution } = await setupEvolution(ctx, home, store, catalog, evoLedger, () => evolutionAdvice, () => reminderStore);
|
|
373
|
+
const profilesQuery = createProfilesQuery(ctx, store, catalog, adviceEnv, () => enabled);
|
|
374
|
+
const syncTools = () => { dispatch.syncTool(); profilesQuery.syncTool(); };
|
|
375
|
+
// 生产聚合触发点(T1 修复):/options/refresh 与 /list 的惰性重算。
|
|
400
376
|
refreshAdvice = () => refreshSummaries({ dispatchFile: adviceEnv.dispatchFile, summariesFile: adviceEnv.summariesFile, logger: ctx.logger, opts: { parentAdoptedConfirmedFalse: adoptionTracker.confirmedFalseCounts() } });
|
|
401
377
|
registerSystemPromptSections(ctx, store, () => enabled, () => evolutionAdvice, adviceEnv, guard);
|
|
402
|
-
const disposeProvider = createProfileProvider({ subagents: ctx.subagents, store, getEnabled: () => enabled, logger: ctx.logger, catalog, getEscapeSet: escapeCtl.getEscapeSet, recordEscapeAllowProvider: escapeCtl.recordEscapeAllowProvider });
|
|
378
|
+
const disposeProvider = createProfileProvider({ subagents: ctx.subagents, store, getEnabled: () => enabled, logger: ctx.logger, catalog, getEscapeSet: escapeCtl.getEscapeSet, recordEscapeAllowProvider: escapeCtl.recordEscapeAllowProvider, readModelSelectionPolicy, evoLedger });
|
|
403
379
|
if (typeof disposeProvider === 'function') ctx.effect(() => disposeProvider);
|
|
404
380
|
const applyDraft = (draft) => applyDraftProfile({ ctx, store, catalog, getEscapeSet: escapeCtl.getEscapeSet, evoLedger, draft });
|
|
405
381
|
// /draft/preview:只读安全检查预览(不落库)。body 为 {config, name, description, source}。
|
|
@@ -415,7 +391,6 @@ export async function apply(ctx) {
|
|
|
415
391
|
source: body !== null && typeof body === 'object' && typeof body.source === 'string' ? body.source : 'human',
|
|
416
392
|
},
|
|
417
393
|
});
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
registerTeardown(ctx, dispatch, ledger, guard, home, backgroundLedger, adoptionTracker);
|
|
394
|
+
registerSettingsRoutes(ctx, store, () => enabled, (next) => { enabled = next; }, syncTools, catalog, ledger, backgroundLedger, draftsStore, applyDraft, () => evoLedger.auditState(), () => evolutionAdvice, (next) => { evolutionAdvice = next; }, escapeCtl.getEscapeEnabled, escapeCtl.setEscapeEnabled, escapeCtl.escape, refreshAdvice, join(home, 'subagent-evolution', 'summaries.json'), adviceEnv.dispatchFile, adviceEnv.whitelist, previewDraft, reminderStore, adviceSource, evolution, snapshots, (entry) => evoLedger.recordGovernanceAudit(entry));
|
|
395
|
+
registerTeardown(ctx, dispatch, profilesQuery, ledger, guard, home, backgroundLedger, adoptionTracker);
|
|
421
396
|
}
|