@lmzhen/dsh-evolution-curator 0.1.0-rc.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 +24 -0
- package/lib/index.js +252 -0
- package/lib/invariant.js +8 -0
- package/lib/types/index.d.ts +74 -0
- package/lib/types/invariant.d.ts +5 -0
- package/package.json +52 -0
package/README.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @deepseek-ai/dsh-evolution-curator
|
|
2
|
+
|
|
3
|
+
Deterministic skill lifecycle and recovery
|
|
4
|
+
|
|
5
|
+
## Model Experience
|
|
6
|
+
|
|
7
|
+
### Indirect model surface
|
|
8
|
+
|
|
9
|
+
#### What the model sees
|
|
10
|
+
|
|
11
|
+
`@deepseek-ai/dsh-evolution-curator` registers no direct prompt or tool schema itself. Model-visible effects are owned by the packages that consume this service.
|
|
12
|
+
|
|
13
|
+
#### Token effect
|
|
14
|
+
|
|
15
|
+
Zero direct token effect from this package; consumers add any model-visible tokens.
|
|
16
|
+
|
|
17
|
+
#### KV Cache effect
|
|
18
|
+
|
|
19
|
+
Independent of request-prefix construction. This package does not alter the assembled prompt or tool list.
|
|
20
|
+
|
|
21
|
+
## Known Limitations and Deferred Work
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
- - LLM nomination pass is advisory and disabled by default; deterministic lifecycle remains authoritative.
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
5
|
+
import z from "@deepseek-ai/schemastery";
|
|
6
|
+
import { CURATOR_PROMPT, SkillLibrary, buildCuratorRunReport, computeLifecycleTransitions, evolutionHome, evolutionIoAdapter, loadUsage, saveUsage } from "@lmzhen/dsh-evolution-core";
|
|
7
|
+
//#region lib/types/index.js
|
|
8
|
+
/**
|
|
9
|
+
* Deterministic skill lifecycle curator with interval gate and archive.
|
|
10
|
+
* @module @lmzhen/dsh-evolution-curator
|
|
11
|
+
*/
|
|
12
|
+
var EvolutionCurator = class extends Service {
|
|
13
|
+
static inject = ["evolutionIo"];
|
|
14
|
+
static Config = z.object({
|
|
15
|
+
enabled: z.boolean().default(true),
|
|
16
|
+
intervalHours: z.number().default(168),
|
|
17
|
+
staleAfterDays: z.number().default(30),
|
|
18
|
+
archiveAfterDays: z.number().default(90),
|
|
19
|
+
llmReview: z.boolean().default(false),
|
|
20
|
+
curatorProvider: z.string().default("deepseek-official"),
|
|
21
|
+
qualityWarnStaleAfterDays: z.number().default(7),
|
|
22
|
+
minIdleHours: z.number().default(0),
|
|
23
|
+
excludeSkillNames: z.array(z.string()).default([]),
|
|
24
|
+
manageUnmanaged: z.boolean().default(false),
|
|
25
|
+
curatorReviewMaxTokens: z.number().default(2048)
|
|
26
|
+
});
|
|
27
|
+
skills;
|
|
28
|
+
io;
|
|
29
|
+
enabled;
|
|
30
|
+
intervalHours;
|
|
31
|
+
staleAfterDays;
|
|
32
|
+
archiveAfterDays;
|
|
33
|
+
llmReview;
|
|
34
|
+
curatorProvider;
|
|
35
|
+
qualityWarnStaleAfterDays;
|
|
36
|
+
minIdleHours;
|
|
37
|
+
excludeSkillNames;
|
|
38
|
+
manageUnmanaged;
|
|
39
|
+
curatorReviewMaxTokens;
|
|
40
|
+
lastRun = 0;
|
|
41
|
+
timer;
|
|
42
|
+
constructor(ctx, config = {}) {
|
|
43
|
+
super(ctx, "evolutionCurator");
|
|
44
|
+
this.io = evolutionIoAdapter(() => ctx.evolutionIo.provider());
|
|
45
|
+
this.skills = new SkillLibrary(void 0, this.io);
|
|
46
|
+
this.enabled = config.enabled ?? true;
|
|
47
|
+
this.intervalHours = config.intervalHours ?? 168;
|
|
48
|
+
this.staleAfterDays = config.staleAfterDays ?? 30;
|
|
49
|
+
this.archiveAfterDays = config.archiveAfterDays ?? 90;
|
|
50
|
+
this.llmReview = config.llmReview ?? false;
|
|
51
|
+
this.curatorProvider = config.curatorProvider ?? "deepseek-official";
|
|
52
|
+
this.qualityWarnStaleAfterDays = config.qualityWarnStaleAfterDays ?? 7;
|
|
53
|
+
this.minIdleHours = config.minIdleHours ?? 0;
|
|
54
|
+
this.excludeSkillNames = new Set(config.excludeSkillNames ?? []);
|
|
55
|
+
this.manageUnmanaged = config.manageUnmanaged ?? false;
|
|
56
|
+
this.curatorReviewMaxTokens = config.curatorReviewMaxTokens ?? 2048;
|
|
57
|
+
this.lastRun = Date.now();
|
|
58
|
+
this.ctx.effect(() => {
|
|
59
|
+
return () => {
|
|
60
|
+
this.stop();
|
|
61
|
+
};
|
|
62
|
+
}, "evolution-curator.stop");
|
|
63
|
+
}
|
|
64
|
+
lifecycle() {
|
|
65
|
+
const snapshot = this.ctx.get("evolutionPolicy")?.get();
|
|
66
|
+
return {
|
|
67
|
+
intervalHours: snapshot?.curatorIntervalHours ?? this.intervalHours,
|
|
68
|
+
staleAfterDays: snapshot?.staleAfterDays ?? this.staleAfterDays,
|
|
69
|
+
archiveAfterDays: snapshot?.archiveAfterDays ?? this.archiveAfterDays
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
start() {
|
|
73
|
+
if (!this.enabled || this.timer) return;
|
|
74
|
+
this.timer = setInterval(() => {
|
|
75
|
+
if (Date.now() - this.lastRun >= this.lifecycle().intervalHours * 36e5) this.run();
|
|
76
|
+
}, 3600 * 1e3);
|
|
77
|
+
this.timer.unref();
|
|
78
|
+
}
|
|
79
|
+
stop() {
|
|
80
|
+
if (this.timer) clearInterval(this.timer);
|
|
81
|
+
this.timer = void 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Optional Hermes-curator LLM pass. The model may only NOMINATE pruning
|
|
85
|
+
* candidates; archive/restore remains a control-plane operation and every
|
|
86
|
+
* nominated name is still checked against lifecycle thresholds and
|
|
87
|
+
* protected markers before any file move.
|
|
88
|
+
*/
|
|
89
|
+
async recommend(candidates) {
|
|
90
|
+
if (candidates.length === 0) return [];
|
|
91
|
+
const llm = this.ctx.get("llm");
|
|
92
|
+
if (!llm) return [];
|
|
93
|
+
const model = this.ctx.get("evolutionPolicy")?.get().curatorModel ?? "deepseek-v4-pro";
|
|
94
|
+
const prompt = [
|
|
95
|
+
CURATOR_PROMPT,
|
|
96
|
+
"",
|
|
97
|
+
"Stale candidates observed by the deterministic lifecycle scanner:",
|
|
98
|
+
...candidates.map((name) => `- ${name}`),
|
|
99
|
+
"",
|
|
100
|
+
"Return a YAML summary with a prunings list. Nominate only candidates whose archival is clearly safe."
|
|
101
|
+
].join("\n");
|
|
102
|
+
try {
|
|
103
|
+
const assembler = new BlockAssembler();
|
|
104
|
+
for await (const chunk of llm.stream({
|
|
105
|
+
provider: this.curatorProvider,
|
|
106
|
+
model,
|
|
107
|
+
messages: [createUserMessage({
|
|
108
|
+
content: [{
|
|
109
|
+
type: "text",
|
|
110
|
+
text: prompt
|
|
111
|
+
}],
|
|
112
|
+
source: {
|
|
113
|
+
kind: "plugin",
|
|
114
|
+
plugin: "dsh-evolution-curator",
|
|
115
|
+
form: "notice",
|
|
116
|
+
summary: "curator review"
|
|
117
|
+
}
|
|
118
|
+
})],
|
|
119
|
+
maxTokens: this.curatorReviewMaxTokens,
|
|
120
|
+
purpose: "evolution-curator"
|
|
121
|
+
})) assembler.push(chunk);
|
|
122
|
+
const text = assembler.blocks().filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
123
|
+
const names = /* @__PURE__ */ new Set();
|
|
124
|
+
const section = text.slice(text.indexOf("prunings:"));
|
|
125
|
+
for (const [, name] of section.matchAll(/^\s*-\s*name:\s*([a-z0-9][a-z0-9-]*)\s*$/gm)) if (name) names.add(name);
|
|
126
|
+
return [...names].filter((name) => candidates.includes(name));
|
|
127
|
+
} catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
skippedReport(runId, startedAt) {
|
|
132
|
+
return buildCuratorRunReport({
|
|
133
|
+
runId,
|
|
134
|
+
startedAt,
|
|
135
|
+
finishedAt: startedAt,
|
|
136
|
+
staleCandidates: [],
|
|
137
|
+
llmNominations: [],
|
|
138
|
+
archiveCandidates: [],
|
|
139
|
+
archived: [],
|
|
140
|
+
failed: []
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async run() {
|
|
144
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
145
|
+
const runId = randomUUID();
|
|
146
|
+
const stateService = this.ctx.get("evolutionState");
|
|
147
|
+
const lifecycle = this.lifecycle();
|
|
148
|
+
const persisted = await stateService?.loadCuratorState();
|
|
149
|
+
if (persisted && Date.now() - persisted.lastRunAt < lifecycle.intervalHours * 36e5) return {
|
|
150
|
+
stale: [],
|
|
151
|
+
archived: [],
|
|
152
|
+
errors: [],
|
|
153
|
+
report: this.skippedReport(runId, startedAt),
|
|
154
|
+
skipped: "interval"
|
|
155
|
+
};
|
|
156
|
+
if (this.minIdleHours > 0 && this.recentSessionActive()) return {
|
|
157
|
+
stale: [],
|
|
158
|
+
archived: [],
|
|
159
|
+
errors: [],
|
|
160
|
+
report: this.skippedReport(runId, startedAt),
|
|
161
|
+
skipped: "active-session"
|
|
162
|
+
};
|
|
163
|
+
const root = this.skills.root;
|
|
164
|
+
const snapshotPath = await this.skills.snapshotAll("pre-curator-run");
|
|
165
|
+
const usage = await loadUsage(root, this.io);
|
|
166
|
+
const result = computeLifecycleTransitions(usage, {
|
|
167
|
+
staleAfterDays: lifecycle.staleAfterDays,
|
|
168
|
+
archiveAfterDays: lifecycle.archiveAfterDays,
|
|
169
|
+
qualityWarnStaleAfterDays: this.qualityWarnStaleAfterDays,
|
|
170
|
+
excludeSkillNames: this.excludeSkillNames,
|
|
171
|
+
manageUnmanaged: this.manageUnmanaged
|
|
172
|
+
});
|
|
173
|
+
const errors = [];
|
|
174
|
+
const archivedSkills = [];
|
|
175
|
+
const llmNominations = this.llmReview ? await this.recommend(result.markStale) : [];
|
|
176
|
+
const archiveCandidates = [...new Set([...result.archive, ...llmNominations])];
|
|
177
|
+
for (const name of archiveCandidates) {
|
|
178
|
+
const archived = await this.skills.archive(name, "Lifecycle: reached archive threshold");
|
|
179
|
+
if (!archived.ok) {
|
|
180
|
+
const record = usage.get(name);
|
|
181
|
+
if (record) record.state = "active";
|
|
182
|
+
errors.push(`${name}: ${archived.message}`);
|
|
183
|
+
} else archivedSkills.push({
|
|
184
|
+
name,
|
|
185
|
+
path: archived.path ?? "",
|
|
186
|
+
reason: "Lifecycle: reached archive threshold"
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
await saveUsage(root, usage, this.io);
|
|
190
|
+
this.lastRun = Date.now();
|
|
191
|
+
const report = buildCuratorRunReport({
|
|
192
|
+
runId,
|
|
193
|
+
startedAt,
|
|
194
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
195
|
+
staleCandidates: result.markStale,
|
|
196
|
+
llmNominations,
|
|
197
|
+
archiveCandidates,
|
|
198
|
+
archived: archivedSkills,
|
|
199
|
+
failed: archiveCandidates.filter((name) => errors.some((error) => error.startsWith(`${name}:`))).map((name) => {
|
|
200
|
+
return {
|
|
201
|
+
name,
|
|
202
|
+
reason: errors.find((item) => item.startsWith(`${name}:`))?.slice(name.length + 2) ?? "unknown"
|
|
203
|
+
};
|
|
204
|
+
}),
|
|
205
|
+
snapshotPath
|
|
206
|
+
});
|
|
207
|
+
const reportsRoot = join(evolutionHome(), "reports");
|
|
208
|
+
try {
|
|
209
|
+
await this.io.writeText(join(reportsRoot, `curator-${runId}.json`), JSON.stringify(report, null, 2));
|
|
210
|
+
} catch (error) {
|
|
211
|
+
this.ctx.logger.warn(`evolution-curator: failed to persist report ${runId}`);
|
|
212
|
+
this.ctx.logger.warn(error);
|
|
213
|
+
}
|
|
214
|
+
await stateService?.saveCuratorState({
|
|
215
|
+
lastRunAt: this.lastRun,
|
|
216
|
+
runCount: (persisted?.runCount ?? 0) + 1,
|
|
217
|
+
lastSummary: `stale:${result.markStale.length} archived:${archivedSkills.length}`,
|
|
218
|
+
paused: false
|
|
219
|
+
});
|
|
220
|
+
return {
|
|
221
|
+
stale: result.markStale,
|
|
222
|
+
archived: archivedSkills.map((item) => item.name),
|
|
223
|
+
errors,
|
|
224
|
+
report
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
recentSessionActive() {
|
|
228
|
+
const agents = this.ctx.get("agents");
|
|
229
|
+
if (!agents) return false;
|
|
230
|
+
let latest = 0;
|
|
231
|
+
for (const agent of agents.list()) {
|
|
232
|
+
const events = agent.session.events;
|
|
233
|
+
const last = events.length === 0 ? 0 : events[events.length - 1]?.time ?? 0;
|
|
234
|
+
latest = Math.max(latest, last);
|
|
235
|
+
}
|
|
236
|
+
return latest > 0 && Date.now() - latest < this.minIdleHours * 36e5;
|
|
237
|
+
}
|
|
238
|
+
async latestReport() {
|
|
239
|
+
const reportsRoot = join(evolutionHome(), "reports");
|
|
240
|
+
const latest = (await this.io.list(reportsRoot)).filter((name) => name.startsWith("curator-") && name.endsWith(".json")).sort().reverse()[0];
|
|
241
|
+
if (!latest) return null;
|
|
242
|
+
const raw = await this.io.readText(join(reportsRoot, latest));
|
|
243
|
+
if (raw === null) return null;
|
|
244
|
+
try {
|
|
245
|
+
return JSON.parse(raw);
|
|
246
|
+
} catch {
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
//#endregion
|
|
252
|
+
export { EvolutionCurator, EvolutionCurator as default };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
const PACKAGE_NAME = "@deepseek-ai/dsh-evolution-curator";
|
|
3
|
+
const name = "evolution-curator-invariant";
|
|
4
|
+
const inject = ["invariants"];
|
|
5
|
+
const install = () => {};
|
|
6
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
7
|
+
//#endregion
|
|
8
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic skill lifecycle curator with interval gate and archive.
|
|
3
|
+
* @module @deepseek-ai/dsh-evolution-curator
|
|
4
|
+
*/
|
|
5
|
+
import { Context, Service } from '@deepseek-ai/cordis';
|
|
6
|
+
import type Schema from '@deepseek-ai/schemastery';
|
|
7
|
+
import { SkillLibrary } from '@deepseek-ai/dsh-evolution-core';
|
|
8
|
+
import { type CuratorRunReport } from '@deepseek-ai/dsh-evolution-core';
|
|
9
|
+
declare module '@deepseek-ai/cordis' {
|
|
10
|
+
interface Context {
|
|
11
|
+
evolutionCurator: EvolutionCurator;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export interface Config {
|
|
15
|
+
enabled?: boolean;
|
|
16
|
+
intervalHours?: number;
|
|
17
|
+
staleAfterDays?: number;
|
|
18
|
+
archiveAfterDays?: number;
|
|
19
|
+
/** Spend one LLM review pass on stale candidates before the deterministic archive step. */
|
|
20
|
+
llmReview?: boolean;
|
|
21
|
+
curatorProvider?: string;
|
|
22
|
+
/** Quality-warned skills may turn stale after this many idle days. */
|
|
23
|
+
qualityWarnStaleAfterDays?: number;
|
|
24
|
+
/** Skip automatic runs while any session was active within this many hours (0 disables). */
|
|
25
|
+
minIdleHours?: number;
|
|
26
|
+
/** Skill names excluded from the automated lifecycle. */
|
|
27
|
+
excludeSkillNames?: string[];
|
|
28
|
+
/** Include usage records whose created_by is not 'agent' in lifecycle decisions. */
|
|
29
|
+
manageUnmanaged?: boolean;
|
|
30
|
+
/** Max tokens for the optional LLM nomination pass. */
|
|
31
|
+
curatorReviewMaxTokens?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare class EvolutionCurator extends Service {
|
|
34
|
+
static inject: string[];
|
|
35
|
+
static Config: Schema<Config>;
|
|
36
|
+
readonly skills: SkillLibrary;
|
|
37
|
+
private readonly io;
|
|
38
|
+
private readonly enabled;
|
|
39
|
+
private readonly intervalHours;
|
|
40
|
+
private readonly staleAfterDays;
|
|
41
|
+
private readonly archiveAfterDays;
|
|
42
|
+
private readonly llmReview;
|
|
43
|
+
private readonly curatorProvider;
|
|
44
|
+
private readonly qualityWarnStaleAfterDays;
|
|
45
|
+
private readonly minIdleHours;
|
|
46
|
+
private readonly excludeSkillNames;
|
|
47
|
+
private readonly manageUnmanaged;
|
|
48
|
+
private readonly curatorReviewMaxTokens;
|
|
49
|
+
private lastRun;
|
|
50
|
+
private timer;
|
|
51
|
+
constructor(ctx: Context, config?: Config);
|
|
52
|
+
private lifecycle;
|
|
53
|
+
start(): void;
|
|
54
|
+
stop(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Optional Hermes-curator LLM pass. The model may only NOMINATE pruning
|
|
57
|
+
* candidates; archive/restore remains a control-plane operation and every
|
|
58
|
+
* nominated name is still checked against lifecycle thresholds and
|
|
59
|
+
* protected markers before any file move.
|
|
60
|
+
*/
|
|
61
|
+
recommend(candidates: string[]): Promise<string[]>;
|
|
62
|
+
private skippedReport;
|
|
63
|
+
run(): Promise<{
|
|
64
|
+
stale: string[];
|
|
65
|
+
archived: string[];
|
|
66
|
+
errors: string[];
|
|
67
|
+
report: CuratorRunReport;
|
|
68
|
+
skipped?: string;
|
|
69
|
+
}>;
|
|
70
|
+
private recentSessionActive;
|
|
71
|
+
latestReport(): Promise<CuratorRunReport | null>;
|
|
72
|
+
}
|
|
73
|
+
export default EvolutionCurator;
|
|
74
|
+
//# sourceMappingURL=index.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lmzhen/dsh-evolution-curator",
|
|
3
|
+
"description": "Deterministic skill lifecycle and recovery (community build)",
|
|
4
|
+
"version": "0.1.0-rc.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "public"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/lmzhen/dsh-evolution.git",
|
|
11
|
+
"directory": "packages/dsh-evolution-curator"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./package.json": "./package.json"
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"lib/index.js",
|
|
29
|
+
"lib/invariant.js",
|
|
30
|
+
"lib/types/**/*.d.ts",
|
|
31
|
+
"lib/types/invariant.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"license": "MIT",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@deepseek-ai/schemastery": "^3.18.1",
|
|
36
|
+
"@lmzhen/dsh-evolution-core": "^0.1.0-rc.1"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@deepseek-ai/cordis": "^4.0.1",
|
|
40
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
41
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
42
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.1",
|
|
43
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
47
|
+
"@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
|
|
48
|
+
"@lmzhen/dsh-evolution-core": "^0.1.0-rc.1",
|
|
49
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.1",
|
|
50
|
+
"@lmzhen/dsh-evolution-state": "^0.1.0-rc.1"
|
|
51
|
+
}
|
|
52
|
+
}
|