@lmzhen/dsh-evolution-curator 0.30.37 → 0.30.39

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
@@ -1,7 +1,7 @@
1
1
  import { Service } from "@deepseek-ai/cordis";
2
- import { BASELINE_ACTION, 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, acquireTreeWriteLock, authorityFrom, blobPath, buildCuratorRunReport, claimInstance, clampOnce, clampedNumber, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, emptyRecord, entryTarget, evolutionHome, evolutionIoAdapter, executeUnitUndo, foldCuratorFields, historyIndexFile, isPresent, isUnknown, lintSkill, liveBool, loadSuppressedNames, loadUsage, markerEntryName, mutateUsage, newSkillLibrary, paramRowId, parseCuratorNominations, parseFrontmatter, parseHistoryIndex, pickWithPolicy, planUnitUndoAcross, policyStageLimits, probeList, probeText, readNumberParam, relatedSkillNames, releaseInstance, renderCuratorReportMarkdown, reportTime, reportsSweepLockTarget, resolveUnitMembers, sessionLastEventTime, staleRouteStubs, sweepReports, transactIo, unitMembersAreComplete, unwrapLive, updateSuppressedNames, usageObserved, userSetKeys, verifyPromptBundle, withTreeWrite } from "@lmzhen/dsh-evolution-core";
2
+ import { BASELINE_ACTION, 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, EXTRACT_PROMPT_HASH, EvolutionGateSet, INSTANCE_KEYS, MAX_TIMER_DELAY_MS, PROMPT_BUNDLE, SKILL_NAME_RE, acquireTreeWriteLock, authorityFrom, blobPath, buildCuratorRunReport, claimInstance, clampOnce, clampedNumber, computeDedupGroups, computeLifecycleTransitions, computePrefixClusters, computeQualityScores, computeScopeView, contentHash, emptyRecord, entryTarget, eventsFile, evolutionHome, evolutionIoAdapter, executeUnitUndo, foldCuratorFields, historyIndexFile, isPresent, isUnknown, lintSkill, liveBool, loadSuppressedNames, loadUsage, markerEntryName, mutateUsage, newSkillLibrary, paramRowId, parseCuratorNominations, parseFrontmatter, parseHistoryIndex, pickWithPolicy, planUnitUndoAcross, policyStageLimits, probeList, probeText, readEvolutionEvents, readNumberParam, relatedSkillNames, releaseInstance, renderCuratorReportMarkdown, reportTime, reportsSweepLockTarget, resolveUnitMembers, sessionLastEventTime, staleRouteStubs, sweepReports, transactIo, unitMembersAreComplete, unwrapLive, updateSuppressedNames, usageObserved, userSetKeys, verifyPromptBundle, withTreeWrite, writeProposalReport } from "@lmzhen/dsh-evolution-core";
3
3
  import { randomUUID } from "node:crypto";
4
- import { join } from "node:path";
4
+ import { dirname, join } from "node:path";
5
5
  import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
6
6
  import z from "@deepseek-ai/schemastery";
7
7
  //#region lib/types/unit-undo-plan.js
@@ -1372,23 +1372,33 @@ var EvolutionCurator = class extends Service {
1372
1372
  const thresholds = {
1373
1373
  softBodyChars: settings.healthSoftBodyChars,
1374
1374
  stampDensityPerKb: settings.healthStampDensityPerKb,
1375
- churnMinPatches: settings.healthChurnMinPatches
1375
+ churnMinPatches: settings.healthChurnMinPatches,
1376
+ hardCapMinHits: DEFAULT_HEALTH_THRESHOLDS.hardCapMinHits
1376
1377
  };
1378
+ const eventLog = await readEvolutionEvents(this.io, eventsFile(dirname(this.skills.root)));
1379
+ const hardCapHits = /* @__PURE__ */ new Map();
1380
+ for (const event of eventLog.events) {
1381
+ if (event.type !== "hard-cap" || event.target === void 0) continue;
1382
+ hardCapHits.set(event.target, (hardCapHits.get(event.target) ?? 0) + 1);
1383
+ }
1377
1384
  const usage = await loadUsage(this.skills.root, this.io);
1378
1385
  const observed = usageObserved(usage);
1379
1386
  const rows = [];
1380
- for (const summary of await this.skills.list()) {
1387
+ for (const summary of await this.skills.model.list()) {
1381
1388
  const record = usage.get(summary.name);
1382
1389
  const assessment = await this.skills.assessHealth(summary.name, thresholds, observed && record ? {
1383
1390
  patchCount: record.patch_count,
1384
- readCount: record.view_count
1385
- } : void 0);
1391
+ readCount: record.view_count,
1392
+ ...eventLog.malformed ? { hardCapHits: null } : { hardCapHits: hardCapHits.get(summary.name) ?? 0 }
1393
+ } : eventLog.malformed ? { hardCapHits: null } : { hardCapHits: hardCapHits.get(summary.name) ?? 0 });
1386
1394
  if (assessment !== null) {
1387
1395
  rows.push({
1388
1396
  name: summary.name,
1389
1397
  verdict: assessment.verdict,
1390
1398
  dims: assessment.dims,
1391
- findings: assessment.findings
1399
+ findings: assessment.findings,
1400
+ hardCapHits: eventLog.malformed ? null : hardCapHits.get(summary.name) ?? 0,
1401
+ ...summary.protectedBy === null ? {} : { protection: summary.protectedBy }
1392
1402
  });
1393
1403
  continue;
1394
1404
  }
@@ -1402,18 +1412,20 @@ var EvolutionCurator = class extends Service {
1402
1412
  supportGroups: 0,
1403
1413
  supportFiles: null,
1404
1414
  churnPatches: null,
1405
- churnReads: null
1415
+ churnReads: null,
1416
+ hardCapHits: null
1406
1417
  },
1407
1418
  softLineChars: thresholds.softBodyChars,
1408
1419
  overSoftLine: false,
1409
1420
  overTwiceSoftLine: false,
1410
1421
  stampLimitPerKb: thresholds.stampDensityPerKb,
1411
1422
  churnMinPatches: thresholds.churnMinPatches,
1423
+ hardCapMinHits: thresholds.hardCapMinHits,
1412
1424
  readable: false
1413
1425
  });
1414
1426
  rows.push({
1415
1427
  name: summary.name,
1416
- verdict: "warn",
1428
+ verdict: "unreadable",
1417
1429
  dims: {
1418
1430
  bodyChars: null,
1419
1431
  bodyTokens: null,
@@ -1421,7 +1433,8 @@ var EvolutionCurator = class extends Service {
1421
1433
  supportGroups: 0,
1422
1434
  supportFiles: null,
1423
1435
  churnPatches: null,
1424
- churnReads: null
1436
+ churnReads: null,
1437
+ readable: false
1425
1438
  },
1426
1439
  findings
1427
1440
  });
@@ -1614,6 +1627,48 @@ var EvolutionCurator = class extends Service {
1614
1627
  * @param unitId - the unit to undo; omitted takes the newest unit the index carries.
1615
1628
  * @returns the outcome; the message names what was reverted, or why nothing was written.
1616
1629
  */
1630
+ /** E12: curator proposer — oversized skills get a keep proposal, never an automatic split. */
1631
+ async proposeKeeps() {
1632
+ const model = this.ctx.get("evolutionPolicy")?.get?.()?.curatorModel ?? DEFAULT_CURATOR_MODEL;
1633
+ const rows = await this.healthView();
1634
+ let count = 0;
1635
+ const at = Date.now();
1636
+ for (const row of rows) {
1637
+ if (row.protection !== void 0 && row.protection !== null) continue;
1638
+ if (!row.findings.some((finding) => finding.rule === "body-oversize" || finding.rule === "body-oversize-2x")) continue;
1639
+ const runId = "curator-keep-" + at.toString(36) + "-" + String(count);
1640
+ const proposal = {
1641
+ version: 1,
1642
+ id: "extract-" + runId + "-" + row.name,
1643
+ source: row.name,
1644
+ outcome: "keep",
1645
+ parts: [],
1646
+ owner: {
1647
+ mode: "covered",
1648
+ name: row.name,
1649
+ triggerCovers: true,
1650
+ outcomeCovers: true,
1651
+ reason: "keep: the current owner remains"
1652
+ },
1653
+ warnings: ["keep is valid: size alone does not require a split"],
1654
+ provenance: {
1655
+ promptVersion: "v1",
1656
+ promptHash: EXTRACT_PROMPT_HASH,
1657
+ model,
1658
+ runId,
1659
+ at
1660
+ },
1661
+ status: "proposed"
1662
+ };
1663
+ await writeProposalReport(this.io, proposal);
1664
+ count += 1;
1665
+ }
1666
+ return {
1667
+ ok: true,
1668
+ count,
1669
+ message: count === 0 ? "没有需要 keep 提案的 body-oversize 技能。" : "已写入 " + String(count) + " 条 keep 提案。"
1670
+ };
1671
+ }
1617
1672
  async undoUnit(name, unitId) {
1618
1673
  const versions = await this.skills.listVersions(name);
1619
1674
  if (versions.length === 0) return {
@@ -1635,7 +1690,7 @@ var EvolutionCurator = class extends Service {
1635
1690
  ok: false,
1636
1691
  message: `单元 ${unit} 不在「${name}」已记录的历史里 —— 拒绝瞎猜、不做写入。`
1637
1692
  };
1638
- const head = members[0];
1693
+ const head = members.find((entry) => entry.unitHead?.sources !== void 0) ?? members[0];
1639
1694
  const headLike = head !== void 0 && head.unitHead !== void 0 ? head.unitHead : void 0;
1640
1695
  const sources = headLike?.sources;
1641
1696
  if (sources !== void 0 && sources.length > 0) {
@@ -522,6 +522,7 @@ export declare class EvolutionCurator extends Service {
522
522
  supportFiles: number | null;
523
523
  churnPatches: number | null;
524
524
  churnReads: number | null;
525
+ readable?: boolean;
525
526
  };
526
527
  findings: Array<{
527
528
  severity: 'warn' | 'error';
@@ -529,6 +530,8 @@ export declare class EvolutionCurator extends Service {
529
530
  message: string;
530
531
  count: number;
531
532
  }>;
533
+ hardCapHits?: number | null;
534
+ protection?: string | null;
532
535
  }>>;
533
536
  /** Whether the library has ANY observed read evidence (C observation-window gate for churn signals). */
534
537
  usageObserved(): Promise<boolean>;
@@ -582,6 +585,12 @@ export declare class EvolutionCurator extends Service {
582
585
  * @param unitId - the unit to undo; omitted takes the newest unit the index carries.
583
586
  * @returns the outcome; the message names what was reverted, or why nothing was written.
584
587
  */
588
+ /** E12: curator proposer — oversized skills get a keep proposal, never an automatic split. */
589
+ proposeKeeps(): Promise<{
590
+ ok: true;
591
+ count: number;
592
+ message: string;
593
+ }>;
585
594
  undoUnit(name: string, unitId?: string): Promise<SkillActionResult>;
586
595
  /** The content the live bytes replaced: the CHAIN first, the recorded order as the fallback.
587
596
  *
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.30.37",
4
+ "version": "0.30.39",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -27,21 +27,21 @@
27
27
  "license": "MIT",
28
28
  "dependencies": {
29
29
  "@deepseek-ai/schemastery": "^3.18.5",
30
- "@lmzhen/dsh-evolution-core": "^0.30.37"
30
+ "@lmzhen/dsh-evolution-core": "^0.30.39"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@deepseek-ai/cordis": "^4.0.5",
34
34
  "@deepseek-ai/dsh-llm": "^0.2.1-rc.2",
35
35
  "@deepseek-ai/dsh-session": "^0.2.1-rc.2",
36
- "@lmzhen/dsh-evolution-io": "^0.30.37",
37
- "@lmzhen/dsh-evolution-state": "^0.30.37"
36
+ "@lmzhen/dsh-evolution-io": "^0.30.39",
37
+ "@lmzhen/dsh-evolution-state": "^0.30.39"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@deepseek-ai/dsh-llm": "^0.2.1-rc.2",
41
41
  "@deepseek-ai/dsh-session": "^0.2.1-rc.2",
42
- "@lmzhen/dsh-evolution-core": "^0.30.37",
43
- "@lmzhen/dsh-evolution-io": "^0.30.37",
44
- "@lmzhen/dsh-evolution-state": "^0.30.37"
42
+ "@lmzhen/dsh-evolution-core": "^0.30.39",
43
+ "@lmzhen/dsh-evolution-io": "^0.30.39",
44
+ "@lmzhen/dsh-evolution-state": "^0.30.39"
45
45
  },
46
46
  "engines": {
47
47
  "dsh": ">=0.2.1-rc.2 <0.2.2-0"