@lmzhen/dsh-evolution-curator 0.22.0 → 0.24.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/lib/index.js CHANGED
@@ -3,7 +3,8 @@ import { randomUUID } from "node:crypto";
3
3
  import { join } from "node:path";
4
4
  import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
5
5
  import z from "@deepseek-ai/schemastery";
6
- import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_SKILL_LIMITS, DEFAULT_STALE_AFTER_DAYS, EvolutionGateSet, INSTANCE_KEYS, MAX_TIMER_DELAY_MS, PROMPT_BUNDLE, SKILL_NAME_RE, buildCuratorRunReport, claimInstance, clampOnce, clampedNumber, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, emptyRecord, entryTarget, evolutionHome, evolutionIoAdapter, foldCuratorFields, isPresent, isUnknown, liveBool, loadSuppressedNames, loadUsage, markerEntryName, mutateUsage, newSkillLibrary, paramRowId, parseCuratorNominations, parseFrontmatter, pickWithPolicy, policyStageLimits, probeList, probeText, readNumberParam, relatedSkillNames, releaseInstance, renderCuratorReportMarkdown, reportTime, reportsSweepLockTarget, sessionLastEventTime, sweepReports, transactIo, unwrapLive, updateSuppressedNames, usageObserved, userSetKeys, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
6
+ import { preferProvider } from "@lmzhen/dsh-evolution-io";
7
+ import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEFAULT_CURATOR_BOOT_GRACE_SECONDS, DEFAULT_CURATOR_INTERVAL_HOURS, DEFAULT_CURATOR_MODEL, DEFAULT_CURATOR_REVIEW_MAX_TOKENS, DEFAULT_HEALTH_THRESHOLDS, DEFAULT_MIN_IDLE_HOURS, DEFAULT_REVIEW_TIMEOUT_MS, DEFAULT_SKILL_LIMITS, DEFAULT_STALE_AFTER_DAYS, EvolutionGateSet, INSTANCE_KEYS, MAX_TIMER_DELAY_MS, PROMPT_BUNDLE, SKILL_NAME_RE, buildCuratorRunReport, claimInstance, clampOnce, clampedNumber, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, emptyRecord, entryTarget, evolutionHome, evolutionIoAdapter, foldCuratorFields, isPresent, isUnknown, liveBool, loadSuppressedNames, loadUsage, markerEntryName, mutateUsage, newSkillLibrary, paramRowId, parseCuratorNominations, parseFrontmatter, pickWithPolicy, policyStageLimits, probeList, probeText, readNumberParam, relatedSkillNames, releaseInstance, renderCuratorReportMarkdown, reportTime, reportsSweepLockTarget, sessionLastEventTime, sweepReports, transactIo, unwrapLive, updateSuppressedNames, usageObserved, userSetKeys, verifyPromptBundle } from "@lmzhen/dsh-evolution-core";
7
8
  //#region lib/types/index.js
8
9
  /**
9
10
  * Deterministic skill lifecycle curator with interval gate and archive.
@@ -11,12 +12,6 @@ import { CURATOR_DRY_RUN_BANNER, CURATOR_PROMPT, DEFAULT_ARCHIVE_AFTER_DAYS, DEF
11
12
  */
12
13
  /** Quality-warned skills may turn stale after this many idle days (package-private tunable, P2-8). */
13
14
  const DEFAULT_QUALITY_WARN_STALE_AFTER_DAYS = 7;
14
- /** B-10 (v18): the optional LLM nomination pass gets its own timeout so a
15
- * hung/unresponsive provider cannot hold the control-plane mutex forever
16
- * (`run()` never returns; `restore()`/`consolidate()` queue behind it).
17
- * 120s matches the review subagent default; the 32-bit ceiling is Node's
18
- * timer-delay limit (`AbortSignal.timeout` throws above it). */
19
- const DEFAULT_CURATOR_REVIEW_TIMEOUT_MS = 12e4;
20
15
  /**
21
16
  * The cross-field rule the schema cannot express (A2-17): an archive threshold
22
17
  * below the stale threshold makes the engine reactivate stale records instead of
@@ -59,7 +54,7 @@ var EvolutionCurator = class extends Service {
59
54
  autoStart: z.boolean().default(true),
60
55
  bootGraceSeconds: z.number().min(0).default(DEFAULT_CURATOR_BOOT_GRACE_SECONDS),
61
56
  curatorReviewMaxTokens: z.number().min(1).default(DEFAULT_CURATOR_REVIEW_MAX_TOKENS).volatile(),
62
- curatorReviewTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS).default(DEFAULT_CURATOR_REVIEW_TIMEOUT_MS).volatile(),
57
+ curatorReviewTimeoutMs: z.number().min(1).max(MAX_TIMER_DELAY_MS).default(DEFAULT_REVIEW_TIMEOUT_MS).volatile(),
63
58
  healthSoftBodyChars: z.number().min(1).default(DEFAULT_HEALTH_THRESHOLDS.softBodyChars).volatile(),
64
59
  healthStampDensityPerKb: z.number().min(1).default(DEFAULT_HEALTH_THRESHOLDS.stampDensityPerKb).volatile(),
65
60
  healthChurnMinPatches: z.number().min(1).default(DEFAULT_HEALTH_THRESHOLDS.churnMinPatches).volatile()
@@ -121,7 +116,7 @@ var EvolutionCurator = class extends Service {
121
116
  if (typeof disposer === "function") ctx.effect(() => disposer, "evolution-curator: settings presentation");
122
117
  });
123
118
  if (!verifyPromptBundle(PROMPT_BUNDLE)) throw new Error("dsh-evolution prompt bundle integrity check failed; refusing to run the curator");
124
- this.io = evolutionIoAdapter(() => ctx.evolutionIo.provider());
119
+ this.io = evolutionIoAdapter(() => preferProvider(ctx.evolutionIo, ["fs"]));
125
120
  this.skills = newSkillLibrary({
126
121
  config,
127
122
  io: this.io,
@@ -230,7 +225,7 @@ var EvolutionCurator = class extends Service {
230
225
  minIdleFailOpen: this.rowBoolean("minIdleFailOpen", true),
231
226
  llmReview: this.rowBoolean("llmReview", false),
232
227
  curatorReviewMaxTokens: number("curatorReviewMaxTokens", DEFAULT_CURATOR_REVIEW_MAX_TOKENS, 1),
233
- curatorReviewTimeoutMs: number("curatorReviewTimeoutMs", DEFAULT_CURATOR_REVIEW_TIMEOUT_MS, 1, MAX_TIMER_DELAY_MS),
228
+ curatorReviewTimeoutMs: number("curatorReviewTimeoutMs", DEFAULT_REVIEW_TIMEOUT_MS, 1, MAX_TIMER_DELAY_MS),
234
229
  healthSoftBodyChars: number("healthSoftBodyChars", DEFAULT_HEALTH_THRESHOLDS.softBodyChars, 1),
235
230
  healthStampDensityPerKb: number("healthStampDensityPerKb", DEFAULT_HEALTH_THRESHOLDS.stampDensityPerKb, 1),
236
231
  healthChurnMinPatches: number("healthChurnMinPatches", DEFAULT_HEALTH_THRESHOLDS.churnMinPatches, 1)
@@ -614,7 +609,7 @@ var EvolutionCurator = class extends Service {
614
609
  instanceHeldRefusal(operation) {
615
610
  return {
616
611
  ok: false,
617
- message: `instance-held: this curator row yielded the per-home claim to another curator row of this process — ${operation} is refused alongside run() so the holder row stays the only control-plane writer (run it on the holder row)`
612
+ message: `instance-held:本 curator 行已把每 home 的占用权让给本进程的另一 curator 行。为保证持有行是唯一的控制面写入方,${operation} 与 run() 一并被拒绝(请在持有它的那一行上运行)。`
618
613
  };
619
614
  }
620
615
  async runCore(options = {}) {
@@ -973,7 +968,7 @@ var EvolutionCurator = class extends Service {
973
968
  continue;
974
969
  }
975
970
  const archived = await this.skills.archive(name, {
976
- reason: "Lifecycle: reached archive threshold",
971
+ reason: "生命周期:已达归档阈值",
977
972
  allowBundled: this.pruneBuiltins
978
973
  });
979
974
  if (!archived.ok) {
@@ -995,7 +990,7 @@ var EvolutionCurator = class extends Service {
995
990
  archivedSkills.push({
996
991
  name,
997
992
  path: archived.path ?? "",
998
- reason: "Lifecycle: reached archive threshold"
993
+ reason: "生命周期:已达归档阈值"
999
994
  });
1000
995
  alreadyArchived.add(name);
1001
996
  if (bundledNames.has(name)) {
@@ -1040,7 +1035,7 @@ var EvolutionCurator = class extends Service {
1040
1035
  });
1041
1036
  archivedSkills.push({
1042
1037
  name: nomination.from,
1043
- reason: `Consolidated into ${nomination.into} (exact archive path: the evolution/skill-mutated event, archivedPath)`
1038
+ reason: `已合并进 ${nomination.into}(精确归档路径见 evolution/skill-mutated 事件里的 archivedPath)`
1044
1039
  });
1045
1040
  }
1046
1041
  if (suppressedChanged) try {
@@ -1267,7 +1262,7 @@ var EvolutionCurator = class extends Service {
1267
1262
  const blocked = [...new Set([target, ...sources])].filter((name) => gates.isBlocked(name));
1268
1263
  if (blocked.length > 0) return {
1269
1264
  ok: false,
1270
- message: `Skill(s) protected from consolidation (excluded / referenced / suppressed / protected builtin): ${blocked.join(", ")}`
1265
+ message: `以下技能受保护、无法合并(被排除/被引用/被抑制/受保护的内置):${blocked.join(", ")}`
1271
1266
  };
1272
1267
  await this.snapshotFull("pre-consolidate");
1273
1268
  const result = await this.skills.consolidate(target, sources);
@@ -1365,46 +1360,46 @@ var EvolutionCurator = class extends Service {
1365
1360
  const versions = await this.skills.listVersions(name);
1366
1361
  if (versions.length === 0) return {
1367
1362
  ok: false,
1368
- message: `No content versions are recorded for "${name}" yet — nothing to undo.`
1363
+ message: `技能「${name}」还没有记录内容版本 —— 没有可撤销的。`
1369
1364
  };
1370
1365
  const live = await this.skills.read(name);
1371
1366
  if (live === null) return {
1372
1367
  ok: false,
1373
- message: `"${name}" is not in the active tree — undo restores CONTENT of a live skill; use '/evolution skill restore ${name}' for an archived one.`
1368
+ message: `「${name}」不在活动目录树里 —— 撤销恢复的是存活技能的正文;若它已归档,请用 '/evolution skill restore ${name}'。`
1374
1369
  };
1375
1370
  const liveHash = contentHash(live);
1376
1371
  const target = v ?? this.previousVersionOf(versions, liveHash);
1377
1372
  if (target === void 0) return {
1378
1373
  ok: false,
1379
- message: `"${name}" is already at its recorded content — no earlier version to undo to.`
1374
+ message: `「${name}」已处于其记录的内容 —— 没有更早的版本可撤销。`
1380
1375
  };
1381
1376
  if (!versions.some((candidate) => candidate.v === target)) {
1382
1377
  const listed = versions.map((candidate) => `v${candidate.v}`).join(", ");
1383
1378
  return {
1384
1379
  ok: false,
1385
- message: `Version v${target} of "${name}" is not in the recorded history (recorded: ${listed === "" ? "none" : listed}).`
1380
+ message: `技能「${name}」的版本 v${target} 不在已记录的历史里(已记录:${listed === "" ? "无" : listed})。`
1386
1381
  };
1387
1382
  }
1388
1383
  const entry = versions.find((candidate) => candidate.v === target);
1389
1384
  if (entry !== void 0 && entryTarget(entry, versions) === "support") return {
1390
1385
  ok: false,
1391
- message: `Version v${target} of "${name}" ${entry.path === void 0 ? `was written by '${entry.action}' and` : `was written by '${entry.action}' to '${entry.path}' and`} holds a SUPPORT FILE's bytes, not this skill's SKILL.md — undo restores the body only. The recorded bytes stay in the history store; copy the blob back over that file to restore them.`
1386
+ message: `技能「${name}」的版本 v${target} ${entry.path === void 0 ? `由 ${entry.action} 写入,并且` : `由 ${entry.action} 写入到 ${entry.path},并且`} 存的是支撑文件的字节,而不是该技能的 SKILL.md —— 撤销只会恢复正文。 已记录的字节仍留在历史存储里;要恢复它们,请把该 blob 复制回那个文件。`
1392
1387
  };
1393
1388
  const content = await this.skills.readVersion(name, target);
1394
1389
  if (content === null) return {
1395
1390
  ok: false,
1396
- message: `Version v${target} of "${name}" is listed in the index but its stored content could not be read — refusing rather than writing a guess.`
1391
+ message: `技能「${name}」的版本 v${target} 虽在索引里,但它的存储内容读不出来 —— 拒绝瞎猜、不做写入。`
1397
1392
  };
1398
1393
  const anchor = { sha256: liveHash };
1399
1394
  const result = await this.skills.update(name, content, "foreground", anchor);
1400
1395
  if (!result.ok) return result;
1401
1396
  if (result.noop === true) return {
1402
1397
  ok: true,
1403
- message: `"${name}" already holds the content of v${target} — nothing was written and no new version was recorded.`
1398
+ message: `「${name}」已持有 v${target} 的内容 —— 未写入任何内容,也没有记录新版本。`
1404
1399
  };
1405
1400
  return {
1406
1401
  ok: true,
1407
- message: `${result.message} (undone to v${target} — content only: markers, usage counts and curation state are unchanged, and this undo is itself a new version.)`
1402
+ message: `${result.message} (已撤销到 v${target} —— 仅内容:标记、使用计数与策展状态不变,且这次撤销本身就是一个新版本。)`
1408
1403
  };
1409
1404
  }
1410
1405
  /** The content the live bytes replaced: the CHAIN first, the recorded order as the fallback.
@@ -17,6 +17,11 @@ import { EvolutionGateSet, type SkillLibrary } from '@lmzhen/dsh-evolution-core'
17
17
  import { type CuratorConsolidation, type CuratorNominations, type CuratorRunReport, type ScopeView, type SkillActionResult, type SkillHealthVerdict } from '@lmzhen/dsh-evolution-core';
18
18
  import type { SkillVersion } from '@lmzhen/dsh-evolution-core';
19
19
  import type { CuratorStateRecord } from '@lmzhen/dsh-evolution-state';
20
+ /** B-10 (v18): the optional LLM nomination pass gets its own timeout so a
21
+ * hung/unresponsive provider cannot hold the control-plane mutex forever
22
+ * (`run()` never returns; `restore()`/`consolidate()` queue behind it).
23
+ * 120s matches the review subagent default; the 32-bit ceiling is Node's
24
+ * timer-delay limit (`AbortSignal.timeout` throws above it). */
20
25
  declare module '@deepseek-ai/cordis' {
21
26
  interface Context {
22
27
  evolutionCurator: EvolutionCurator;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@lmzhen/dsh-evolution-curator",
3
3
  "description": "Deterministic skill lifecycle and recovery (community build)",
4
- "version": "0.22.0",
4
+ "version": "0.24.0",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,20 +27,20 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.4",
30
- "@lmzhen/dsh-evolution-core": "^0.22.0"
30
+ "@lmzhen/dsh-evolution-core": "^0.24.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@deepseek-ai/cordis": "^4.0.1",
34
34
  "@deepseek-ai/dsh-llm": "^0.2.0-rc.1",
35
35
  "@deepseek-ai/dsh-session": "^0.2.0-rc.1",
36
- "@lmzhen/dsh-evolution-io": "^0.22.0",
37
- "@lmzhen/dsh-evolution-state": "^0.22.0"
36
+ "@lmzhen/dsh-evolution-io": "^0.24.0",
37
+ "@lmzhen/dsh-evolution-state": "^0.24.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@deepseek-ai/dsh-llm": "^0.2.0-rc.1",
41
41
  "@deepseek-ai/dsh-session": "^0.2.0-rc.1",
42
- "@lmzhen/dsh-evolution-core": "^0.22.0",
43
- "@lmzhen/dsh-evolution-io": "^0.22.0",
44
- "@lmzhen/dsh-evolution-state": "^0.22.0"
42
+ "@lmzhen/dsh-evolution-core": "^0.24.0",
43
+ "@lmzhen/dsh-evolution-io": "^0.24.0",
44
+ "@lmzhen/dsh-evolution-state": "^0.24.0"
45
45
  }
46
46
  }