dsh-subagent-profile 0.5.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 CHANGED
@@ -116,58 +116,13 @@ Delegation never lets a subagent gain more power than you already have — this
116
116
 
117
117
  ```
118
118
  dsh-subagent-profile/
119
- ├── index.mjs # host side: the plugin itself (dispatch tool, profile provider, service, HTTP routes)
119
+ ├── index.mjs # plugin entry: dispatch tool, profile provider, services, HTTP routes
120
120
  ├── lib/
121
- │ ├── client.js # browser side: settings page + ledger + dispatch tool-call card
122
- │ └── core/ # host-side modules (lightweight layering)
123
- ├── pure.mjs # dependency-free pure functions (sanitize / prune / guard math — unit-tested)
124
- ├── shims.mjs # the single @deepseek-ai import facade (guards fail loud, helpers degrade softly)
125
- │ ├── catalog.mjs # tool-name → zh / category tables (zero-dep)
126
- │ ├── catalog-cache.mjs # process-shared catalog snapshot (models / presets / tools, TTL cache)
127
- │ ├── cost-guard.mjs # runtime capability checks (provider / model / reasoning effort)
128
- │ ├── cost-evidence.mjs # counterfactual savings evidence (token-saving proof)
129
- │ ├── prices.mjs # per-dispatch price table (average-based placeholder)
130
- │ ├── decision-trace.mjs # decision trace (checks / effective / settled) + failure ledger
131
- │ ├── delegation.mjs # background one-shot settling + child usage collection
132
- │ ├── dispatch-gates.mjs # pre-dispatch safety checks (whitelist / cost / intersection / budget)
133
- │ ├── dispatch-guard.mjs # concurrency + per-parent token budget guard
134
- │ ├── dispatch-schema.mjs # dispatch tool input/output schema declarations
135
- │ ├── dispatch-tool.mjs # dispatch tool factory (defineTool + execute + syncTool)
136
- │ ├── draft-gates.mjs # draft profile gates (three checks)
137
- │ ├── drafts-store.mjs # draft profile persistence
138
- │ ├── escape.mjs # escape-hatch allow store
139
- │ ├── evolution-ledger.mjs # decision ledger (jsonl) + governance audit
140
- │ ├── evolution-summary.mjs # aggregate statistics (per-profile, capability/budget axes)
141
- │ ├── evolution-advice.mjs # read-only dispatch suggestion generation
142
- │ ├── adoption-tracker.mjs # child-result adoption verdict tracking (cross-restart)
143
- │ ├── adoption-reminder.mjs # adoption-verdict reminder generation
144
- │ ├── reminder-store.mjs # reminder store + audit-coupled records
145
- │ ├── background-ledger.mjs # background job ledger (in-memory, resets on restart)
146
- │ ├── http-routes.mjs # settings loopback HTTP routes
147
- │ ├── intersection.mjs # tool-intersection pure core
148
- │ ├── presets-sync.mjs # bundled preset self-install (hash-gated sync)
149
- │ ├── profile-provider.mjs # the `profile` subagent provider
150
- │ ├── profile-directory.mjs # read-only profile directory (for the model)
151
- │ ├── profiles-store.mjs # profile registry store + switch persistence
152
- │ └── whitelist.mjs # system-trust preset whitelist
153
- ├── presets/orchestrator-v2/ # bundled "orchestrator-v2" agent preset (self-installed, synced on every startup)
154
- ├── cordis.patch.yml # bundle patch: inserts the plugin row into the host composition
155
- ├── .gitea/workflows/ci.yml # bare-CI (Gitea Actions; needs an Act runner on the server)
156
- ├── package.json # metadata, files whitelist, exports (test / test:bare / preflight scripts)
157
- ├── scripts/
158
- │ ├── preflight.mjs # preflight: preset-tree reconciliation + no hardcoded version badge (zero-dep)
159
- │ └── leak-scan.mjs # public-release gate: scans all history + worktree for sensitive patterns
160
- ├── docs/
161
- │ └── screenshots/ # README screenshots
162
- ├── test/ # host-side tests (node:test, zero extra deps)
163
- │ ├── README.md / README.zh.md # test directory guide (EN/ZH) — two-tier split explained
164
- │ ├── harness/ctx.mjs # fake Cordis ctx + ~/.dsh isolation
165
- │ ├── pure / input-schema / catalog-integrity.test.mjs # bare tier (import-free, runs in bare CI)
166
- │ └── *.test.mjs # junction tier (local only): characterization / facade / gating / persist / recycle /
167
- │ # cost-guard / continuable-guard / decision-trace / dispatch-guard / escape-hatch /
168
- │ # evolution-* / csrf / label-preset-sync / percall-spec / trust-label / audit-meta / …
169
- ├── README.md / README.zh.md # this document (EN/ZH)
170
- └── LICENSE
121
+ │ ├── client.js # browser side: settings page, per-session ledger, dispatch tool-call card
122
+ │ └── core/ # host-side modules by domain: gates, ledger, evolution, profiles, query, sync
123
+ ├── presets/ # bundled agent presets (e.g. orchestrator-v2)
124
+ ├── test/ # node:test suite (pure and junction tiers)
125
+ └── docs/ # screenshots
171
126
  ```
172
127
 
173
128
  ## Contributing
package/README.zh.md CHANGED
@@ -116,58 +116,13 @@ dispatch(
116
116
 
117
117
  ```
118
118
  dsh-subagent-profile/
119
- ├── index.mjs # 宿主侧:插件本体(dispatch 工具、方案提供者、服务、HTTP 路由)
119
+ ├── index.mjs # 插件入口:dispatch 工具、方案提供方、服务与 HTTP 路由
120
120
  ├── lib/
121
- │ ├── client.js # 浏览器侧:设置页 + 台账 + dispatch 工具卡片
122
- │ └── core/ # 宿主侧模块(轻量分层)
123
- ├── pure.mjs # 零依赖纯函数(清洗/剪枝/护栏数学——单测覆盖)
124
- ├── shims.mjs # 唯一的 @deepseek-ai 导入门面(护栏大声失败,辅助软降级)
125
- │ ├── catalog.mjs # 工具名 → 中文/分类表(零依赖)
126
- │ ├── catalog-cache.mjs # 进程共享目录快照(模型/预设/工具,TTL 缓存)
127
- │ ├── cost-guard.mjs # 运行时能力检查(提供方/模型/推理强度)
128
- │ ├── cost-evidence.mjs # 省 token 反事实对照证据
129
- │ ├── prices.mjs # 每次派发单价表(当前为均价占位)
130
- │ ├── decision-trace.mjs # 决策轨迹(检查/生效/结算)+ 失败台账
131
- │ ├── delegation.mjs # 后台一次性结算 + 子用量采集
132
- │ ├── dispatch-gates.mjs # 派发前安全检查(白名单/成本/交集/预算)
133
- │ ├── dispatch-guard.mjs # 并发 + 每父会话 token 预算护栏
134
- │ ├── dispatch-schema.mjs # dispatch 工具输入/输出 schema 声明
135
- │ ├── dispatch-tool.mjs # dispatch 工具工厂(defineTool + execute + syncTool)
136
- │ ├── draft-gates.mjs # 草稿方案三道闸
137
- │ ├── drafts-store.mjs # 草稿方案持久化
138
- │ ├── escape.mjs # 逃生舱放行存储
139
- │ ├── evolution-ledger.mjs # 派发决策台账(jsonl)+ 治理审计
140
- │ ├── evolution-summary.mjs # 聚合统计(按方案,能力/预算双轴)
141
- │ ├── evolution-advice.mjs # 只读派发建议生成
142
- │ ├── adoption-tracker.mjs # 子结果采纳判定跟踪(跨重启)
143
- │ ├── adoption-reminder.mjs # 采纳判定提醒生成
144
- │ ├── reminder-store.mjs # 提醒存储 + 审计耦合记录
145
- │ ├── background-ledger.mjs # 后台任务台账(内存态,重启即失)
146
- │ ├── http-routes.mjs # 设置回环 HTTP 路由
147
- │ ├── intersection.mjs # 工具交集纯核心
148
- │ ├── presets-sync.mjs # 内置预设自动安装(哈希门控同步)
149
- │ ├── profile-provider.mjs # `profile` 子代理提供者
150
- │ ├── profile-directory.mjs # 只读方案目录(供模型参考)
151
- │ ├── profiles-store.mjs # 方案注册表存储 + 开关持久化
152
- │ └── whitelist.mjs # system-trust 预设白名单
153
- ├── presets/orchestrator-v2/ # 内置「orchestrator-v2」预设(自动安装,每次启动同步)
154
- ├── cordis.patch.yml # bundle patch:把插件行插入宿主组合
155
- ├── .gitea/workflows/ci.yml # bare-CI(Gitea Actions;需服务器上的 Act runner)
156
- ├── package.json # 元数据、files 白名单、exports(test / test:bare / preflight 脚本)
157
- ├── scripts/
158
- │ ├── preflight.mjs # 预检:预设树一致 + 无硬编码版本徽章(零依赖)
159
- │ └── leak-scan.mjs # 公开发布门:扫描全历史与工作区的敏感模式
160
- ├── docs/
161
- │ └── screenshots/ # README 截图
162
- ├── test/ # 宿主侧测试(node:test,零额外依赖)
163
- │ ├── README.md / README.zh.md # 测试目录指南(中英)——两层拆分说明
164
- │ ├── harness/ctx.mjs # 假 Cordis ctx + ~/.dsh 隔离
165
- │ ├── pure / input-schema / catalog-integrity.test.mjs # bare 层(免导入,bare CI 可跑)
166
- │ └── *.test.mjs # junction 层(仅本地):characterization / facade / gating / persist / recycle /
167
- │ # cost-guard / continuable-guard / decision-trace / dispatch-guard / escape-hatch /
168
- │ # evolution-* / csrf / label-preset-sync / percall-spec / trust-label / audit-meta / …
169
- ├── README.md / README.zh.md # 本文档(英/中)
170
- └── LICENSE
121
+ │ ├── client.js # 浏览器端:设置页、每会话台账、派发工具调用卡片
122
+ │ └── core/ # 宿主端核心模块(按域:闸门、台账、自进化、方案、查询、同步)
123
+ ├── presets/ # 内置 Agent 预设(如 orchestrator-v2)
124
+ ├── test/ # node:test 测试套件(纯函数层 + 接线层)
125
+ └── docs/ # 截图
171
126
  ```
172
127
 
173
128
  ## 贡献
package/index.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  // index.mjs — dsh-subagent-profile 宿主侧插件 bundle(只做装配)。宿主导入面收敛在
2
2
  // lib/core/shims.mjs,装配块按模块拆分驻留 lib/core/;`enabled` 经 getter 实时读取。
3
- import { readdirSync, renameSync, rmSync } from 'node:fs';
4
3
  import { createRequire } from 'node:module';
5
4
  import { join } from 'node:path';
6
5
  import { syncBundledPresets } from './lib/core/presets-sync.mjs';
7
6
  import { dshHome, createProfileStore } from './lib/core/profiles-store.mjs';
7
+ import { removeOwnedData, cleanRemovedBackups } from './lib/core/cleanup.mjs';
8
8
  import { createHttpRoutes } from './lib/core/http-routes.mjs';
9
9
  import { createProfileProvider } from './lib/core/profile-provider.mjs';
10
10
  import { createDispatchTool } from './lib/core/dispatch-tool.mjs';
@@ -14,13 +14,14 @@ import { createFailureLedger } from './lib/core/decision-trace.mjs';
14
14
  import { createEvolutionLedger } from './lib/core/evolution-ledger.mjs';
15
15
  import { createAdoptionTracker } from './lib/core/adoption-tracker.mjs';
16
16
  import { createReminderStore } from './lib/core/reminder-store.mjs';
17
+ import { createSnapshotStore } from './lib/core/snapshots.mjs';
17
18
  import { mintReminder, mintUnadoptedReminder } from './lib/core/adoption-reminder.mjs';
18
19
  import { createBackgroundLedger } from './lib/core/background-ledger.mjs';
19
20
  import { createDraftsStore } from './lib/core/drafts-store.mjs';
20
21
  import { assessDraftProfile } from './lib/core/draft-gates.mjs';
21
22
  import { createEscapeStore, recordEscapeAllowProvider } from './lib/core/escape.mjs';
22
23
  import { resolveWhitelist, FALLBACK_WHITELIST } from './lib/core/whitelist.mjs';
23
- import { readModelSelectionPolicy } from './lib/core/model-policy.mjs';
24
+ import { readModelSelectionPolicy, appendOfficialVisibilityLine } from './lib/core/model-policy.mjs';
24
25
  import { profileSectionText } from './lib/core/profile-directory.mjs';
25
26
  import { createProfilesQueryTool } from './lib/core/profiles-query.mjs';
26
27
  import { budgetSectionText } from './lib/core/dispatch-gates.mjs';
@@ -45,33 +46,7 @@ function syncBundledPresetsToHome(ctx) {
45
46
  }
46
47
  }
47
48
 
48
- // 卸载清理:本插件 3 个数据文件 + orchestrator-v2 预设目录改名备份(effect 清理器
49
- // 在禁用/热重载/退出时都可能触发;硬删除会静默丢用户数据)。fail-soft:改名失败不抛。
50
- function removeOwnedData(home) {
51
- const stamp = Date.now();
52
- for (const file of ['subagent-profiles.json', 'subagent-profiles.state.json', 'subagent-profiles.failed-traces.json']) {
53
- try { renameSync(join(home, file), join(home, `${file}.removed-${stamp}`)); } catch { /* 源不存在(正常首启)等:best effort */ }
54
- }
55
- try { renameSync(join(home, '.agent-presets', 'orchestrator-v2'), join(home, '.agent-presets', `orchestrator-v2.removed-${stamp}`)); } catch { /* best effort */ }
56
- }
57
-
58
- // 启动清理 .removed-* 备份残留(只清本插件前缀,防热重载反复产生新备份积累);
59
- // 卸载后用户手动改回原名的场景不受影响。fail-soft。
60
- function cleanRemovedBackups(home) {
61
- for (const dir of [home, join(home, '.agent-presets')]) {
62
- let names;
63
- try { names = readdirSync(dir); } catch { continue; }
64
- for (const name of names) {
65
- try {
66
- if (name.startsWith('subagent-profiles.') && name.includes('.removed-')) {
67
- rmSync(join(dir, name), { force: true });
68
- } else if (name.startsWith('orchestrator-v2.removed-')) {
69
- rmSync(join(dir, name), { recursive: true, force: true });
70
- }
71
- } catch { /* best effort */ }
72
- }
73
- }
74
- }
49
+ // 卸载清理与启动清理辅助驻留 lib/core/cleanup.mjs(装配文件行门)。
75
50
 
76
51
  // parent_adopted 装配:tracker 工厂 + 根 ctx 订阅 session/event(按 session.id 过滤),
77
52
  // 卸载退订并清理定时器。onDecision 经 getRefreshAdvice 延迟读取(先判定后聚合时为空
@@ -175,7 +150,7 @@ function orchestrationGate(ctx, getEnabled, context) {
175
150
  // orchestrator:mode section 文本(编排者模式 V2 定稿文案;与 profiles 同门控。
176
151
  // 追加官方错误对照句:官方全英文报错补中文行动引导,与 dispatch 的 stopReason
177
152
  // 映射共同构成错误闭环)。
178
- 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)。';
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)。官方工具没出现=模型选择未启用。';
179
154
 
180
155
  // 建议注入候选池:受信任预设白名单(解析失败回退内置名单)。不叠加逃生舱放行集。
181
156
  async function resolveAdviceWhitelist(ctx) {
@@ -211,7 +186,7 @@ function registerSystemPromptSections(ctx, store, getEnabled, getEvolutionAdvice
211
186
  pluginSystemPrompt.section({
212
187
  name: 'dispatch:profiles',
213
188
  order: 116.5,
214
- text: (context) => profileSectionText(store, profileGate, context),
189
+ text: (context) => appendOfficialVisibilityLine(profileSectionText(store, profileGate, context), ctx, context, ctx.logger),
215
190
  });
216
191
  // 宣告自装的 orchestrator-v2 预设(门控与 0.4.0 相同:仅编排者 agent 可见)。
217
192
  pluginSystemPrompt.section({
@@ -247,7 +222,7 @@ function registerSystemPromptSections(ctx, store, getEnabled, getEvolutionAdvice
247
222
  // webServer 可选——无头部署保留 dispatch 工具、只丢设置页。webServer 的激活
248
223
  // (listen)是异步的,可能晚于本插件 inject 依赖解析完成,故在等它的 inject
249
224
  // 子 scope 内注册(apply 时 ctx.get 会读到 undefined)。
250
- 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) {
251
226
  ctx.inject(['webServer'], (scope) => {
252
227
  scope.effect(createHttpRoutes({
253
228
  webServer: scope.webServer,
@@ -274,6 +249,8 @@ function registerSettingsRoutes(ctx, store, getEnabled, setEnabled, syncTool, ca
274
249
  reminderStore,
275
250
  adviceSource,
276
251
  evolution,
252
+ snapshots,
253
+ recordAudit,
277
254
  logger: ctx.logger,
278
255
  }), 'dsh-subagent-profile: settings routes');
279
256
  });
@@ -322,7 +299,7 @@ function setupEscape(ctx, store, evoLedger, home, getReminderStore) {
322
299
 
323
300
  // `dispatch` 工具装配(defineTool + execute + syncTool)。须在 HTTP inject 之前
324
301
  // 构造,使 createHttpRoutes 能拿 dispatch.syncTool 供 /set-enabled 注册/注销。
325
- 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) {
326
303
  return createDispatchTool({
327
304
  register: (tool) => ctx.tools.register(tool),
328
305
  store,
@@ -338,6 +315,7 @@ function createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroun
338
315
  adoptionTracker,
339
316
  getEscapeSet,
340
317
  getEvolutionAdvice,
318
+ snapshots,
341
319
  });
342
320
  }
343
321
 
@@ -386,8 +364,10 @@ export async function apply(ctx) {
386
364
  const backgroundLedger = createBackgroundLedger({ warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`) });
387
365
  const draftsStore = createDraftsStore({ dshHome: home, onGovernanceFailure: () => evoLedger.markGovernanceFailure() });
388
366
  reminderStore = createReminderStore({ dshHome: home, audit: (entry) => evoLedger.recordGovernanceAudit(entry), warn: (message) => ctx.logger.warn(`[dsh-subagent-profile] ${message}`) });
367
+ // 配置快照域:开关关恒跳过(store.loadSnapshotEnabled 默认关);写审计与治理失败同源。
368
+ const snapshots = createSnapshotStore({ dshHome: home, logger: ctx.logger, getEnabled: () => store.loadSnapshotEnabled(), recordAudit: (entry) => evoLedger.recordGovernanceAudit(entry), onGovernanceFailure: () => evoLedger.markGovernanceFailure() });
389
369
  let refreshAdvice = () => {}; const adoptionTracker = setupAdoptionTracker(ctx, home, () => refreshAdvice, (rec) => mintUnadoptedReminder(reminderStore, evoLedger, rec));
390
- const dispatch = createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroundLedger, adoptionTracker, escapeCtl.getEscapeSet, () => enabled, () => evolutionAdvice);
370
+ const dispatch = createDispatch(ctx, store, catalog, ledger, guard, evoLedger, backgroundLedger, adoptionTracker, escapeCtl.getEscapeSet, () => enabled, () => evolutionAdvice, snapshots);
391
371
  provideProfileService(ctx, store);
392
372
  const { adviceEnv, adviceSource, evolution } = await setupEvolution(ctx, home, store, catalog, evoLedger, () => evolutionAdvice, () => reminderStore);
393
373
  const profilesQuery = createProfilesQuery(ctx, store, catalog, adviceEnv, () => enabled);
@@ -411,6 +391,6 @@ export async function apply(ctx) {
411
391
  source: body !== null && typeof body === 'object' && typeof body.source === 'string' ? body.source : 'human',
412
392
  },
413
393
  });
414
- 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);
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));
415
395
  registerTeardown(ctx, dispatch, profilesQuery, ledger, guard, home, backgroundLedger, adoptionTracker);
416
396
  }