@lmzhen/dsh-evolution-state-json 0.3.50 → 0.3.52
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 +1 -1
- package/lib/index.js +101 -40
- package/lib/types/index.d.ts +2 -0
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -22,5 +22,5 @@ Independent of request-prefix construction. This package does not alter the asse
|
|
|
22
22
|
## Known Limitations and Deferred Work
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
- JSON provider serializes writers inside one process AND through the IO backend's cross-process transact lock (
|
|
25
|
+
- JSON provider serializes writers inside one process AND through the IO backend's cross-process transact lock (an internal transact wrapper — not public API, audit v10 S-03 — wraps every mutation, 0.3.20/0.3.27) — this provider is NOT limited to single-process safety. The caveat below is about the DSH storage-domain providers (`storage-json` documents no cross-process write locking) when the DOMAIN provider is used instead; multi-process deployments should route the evolution domain to a backend with cross-process semantics such as SQLite or remote storage.
|
|
26
26
|
|
package/lib/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
2
|
import { evolutionHome, makeSerialQueue, transactIo } from "@lmzhen/dsh-evolution-core";
|
|
3
|
-
import { canClaimPending, canResolvePending, releasedStatus } from "@lmzhen/dsh-evolution-state-storage";
|
|
4
|
-
import { join } from "node:path";
|
|
3
|
+
import { CURATOR_STATE_FILE, CURATOR_STATE_KEY, PENDING_ARCHIVE_BAK_FILE, PENDING_ARCHIVE_FILE, PENDING_LEGACY_FILE, PENDING_STATE_FILE, PROVIDER_JSON, REVIEW_STATE_FILE, canClaimPending, canResolvePending, releasedStatus } from "@lmzhen/dsh-evolution-state-storage";
|
|
4
|
+
import { isAbsolute, join } from "node:path";
|
|
5
5
|
//#region lib/types/index.js
|
|
6
6
|
/**
|
|
7
7
|
* JSON-file evolution state provider over the IO seam.
|
|
@@ -35,22 +35,62 @@ const pendingArchiveKey = (record) => `${record.id}\u0000${record.status}\u0000$
|
|
|
35
35
|
* is a top-level ARRAY and must NOT be gated by this predicate. */
|
|
36
36
|
const isPlainRecord = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
37
37
|
const RECORD_MAP_FILES = new Set([
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
REVIEW_STATE_FILE,
|
|
39
|
+
CURATOR_STATE_FILE,
|
|
40
|
+
PENDING_STATE_FILE,
|
|
41
|
+
PENDING_LEGACY_FILE
|
|
42
42
|
]);
|
|
43
|
+
/** V10-05 (P2-5): discriminant stamped on every quarantine error so
|
|
44
|
+
* best-effort wrappers (retireLegacyOnce) can tell "corrupt state — fail
|
|
45
|
+
* loud" from a transient IO failure and let the quarantine propagate. */
|
|
46
|
+
const QUARANTINE_ERROR_NAME = "EvolutionStateCorruptFile";
|
|
43
47
|
/** 0.3.17 (E-9): a malformed state file used to parse to `null` and was then
|
|
44
48
|
* OVERWRITTEN by the next save — every other session's review state / the
|
|
45
49
|
* whole pending table vanished silently. Fail loud instead: preserve the
|
|
46
50
|
* original bytes beside it and throw, so the operator can rescue and the
|
|
47
|
-
* corruption is never accepted as "empty".
|
|
51
|
+
* corruption is never accepted as "empty".
|
|
52
|
+
* V10-05 (P2-5): the copy is the FIXED name `<file>.corrupt`, written through
|
|
53
|
+
* the IO seam's atomic write (tmp+rename under the node backend), so it
|
|
54
|
+
* overwrite-commits — at most ONE preserved copy per file can ever
|
|
55
|
+
* accumulate. The old `.corrupt-<stamp>-<rand>` name minted a fresh file on
|
|
56
|
+
* EVERY read of a corrupt file: unbounded growth with no sweep. The fixed
|
|
57
|
+
* copy is swept after 7 days by the node backend's sweepStaleTmps (S-10). */
|
|
48
58
|
async function quarantine(io, root, file, raw, reason) {
|
|
49
|
-
const
|
|
50
|
-
const dest = `${join(root, file)}.corrupt-${stamp}-${Math.random().toString(36).slice(2, 6)}`;
|
|
59
|
+
const dest = `${join(root, file)}.corrupt`;
|
|
51
60
|
await io().writeText(dest, raw).catch(() => {});
|
|
52
|
-
throw new Error(`evolution state file "${file}" is not valid JSON (${reason}); original preserved at ${dest} — inspect and fix it, then retry.`);
|
|
61
|
+
throw Object.assign(/* @__PURE__ */ new Error(`evolution state file "${file}" is not valid JSON (${reason}); original preserved at ${dest} — inspect and fix it, then retry.`), { name: QUARANTINE_ERROR_NAME });
|
|
53
62
|
}
|
|
63
|
+
/** S-05: the V8-16 record-shape gate existed as two verbatim ~15-line
|
|
64
|
+
* copies (jsonTransact + readJson); this private helper is the single owner.
|
|
65
|
+
* Returns the quarantine reason for the first non-plain-object record value,
|
|
66
|
+
* or null when every value of the map is a plain object. */
|
|
67
|
+
function firstNonRecordValue(parsed) {
|
|
68
|
+
if (!isPlainRecord(parsed)) return null;
|
|
69
|
+
for (const [recordId, record] of Object.entries(parsed)) if (!isPlainRecord(record)) return `expected a plain object for record "${recordId}", got ${record === null ? "null" : Array.isArray(record) ? "an array" : typeof record}`;
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const isNonNegInt = (value) => typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
73
|
+
const optionalString = (value) => value === void 0 || typeof value === "string";
|
|
74
|
+
const PENDING_KINDS = new Set([
|
|
75
|
+
"memory",
|
|
76
|
+
"skill",
|
|
77
|
+
"capability"
|
|
78
|
+
]);
|
|
79
|
+
const PENDING_STATUSES = new Set([
|
|
80
|
+
"pending",
|
|
81
|
+
"executing",
|
|
82
|
+
"approved",
|
|
83
|
+
"rejected"
|
|
84
|
+
]);
|
|
85
|
+
const gateReviewRecord = (record) => isNonNegInt(record.turnsSinceMemory) && isNonNegInt(record.turnsSinceSkill) && isNonNegInt(record.lastTurn);
|
|
86
|
+
const gateCuratorRecord = (record) => typeof record.lastRunAt === "number" && Number.isFinite(record.lastRunAt) && record.lastRunAt >= 0 && isNonNegInt(record.runCount) && typeof record.lastSummary === "string" && typeof record.paused === "boolean";
|
|
87
|
+
const gatePendingRecord = (record) => typeof record.id === "string" && typeof record.kind === "string" && PENDING_KINDS.has(record.kind) && typeof record.summary === "string" && typeof record.createdAt === "string" && typeof record.status === "string" && PENDING_STATUSES.has(record.status) && optionalString(record.resolvedAt) && optionalString(record.claimedBy) && optionalString(record.claimedAt) && optionalString(record.origin) && optionalString(record.sessionId);
|
|
88
|
+
const RECORD_FIELD_GATES = {
|
|
89
|
+
[REVIEW_STATE_FILE]: gateReviewRecord,
|
|
90
|
+
[CURATOR_STATE_FILE]: gateCuratorRecord,
|
|
91
|
+
[PENDING_STATE_FILE]: gatePendingRecord,
|
|
92
|
+
[PENDING_LEGACY_FILE]: gatePendingRecord
|
|
93
|
+
};
|
|
54
94
|
/**
|
|
55
95
|
* Cross-process JSON-file RMW (v3-audit M-8): every read-modify-write state
|
|
56
96
|
* mutation runs inside the IO backend's transact lock (via transactIo) so a
|
|
@@ -61,6 +101,8 @@ async function quarantine(io, root, file, raw, reason) {
|
|
|
61
101
|
* array/scalar would be persisted as a corrupt map — so it fails loud before
|
|
62
102
|
* any write (0.3.28, V4-08). The legacy `pending.json` merge stays inside the
|
|
63
103
|
* task via `readJson` where relevant.
|
|
104
|
+
* @internal Exported only for this package's own tests — not public API
|
|
105
|
+
* surface (audit v10 S-03); other packages must go through the provider seam.
|
|
64
106
|
*/
|
|
65
107
|
async function jsonTransact(io, root, file, task) {
|
|
66
108
|
await transactIo(io(), join(root, file), async (current) => {
|
|
@@ -72,9 +114,8 @@ async function jsonTransact(io, root, file, task) {
|
|
|
72
114
|
return await quarantine(io, root, file, current, error instanceof Error ? error.message : String(error));
|
|
73
115
|
}
|
|
74
116
|
if (RECORD_MAP_FILES.has(file) && !isPlainRecord(parsed)) return await quarantine(io, root, file, current, `expected a plain JSON object (map of records), got ${Array.isArray(parsed) ? "an array" : parsed === null ? "null" : typeof parsed}`);
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
117
|
+
const malformed = firstNonRecordValue(parsed);
|
|
118
|
+
if (malformed !== null) return await quarantine(io, root, file, current, malformed);
|
|
78
119
|
}
|
|
79
120
|
const next = await task(parsed);
|
|
80
121
|
if (next !== null && RECORD_MAP_FILES.has(file) && !isPlainRecord(next)) throw new Error(`evolution state file "${file}" task returned ${Array.isArray(next) ? "an array" : typeof next} (expected null or a plain JSON object map of records); not written.`);
|
|
@@ -83,8 +124,10 @@ async function jsonTransact(io, root, file, task) {
|
|
|
83
124
|
}
|
|
84
125
|
function apply(ctx, rawConfig) {
|
|
85
126
|
const root = (rawConfig.root ?? "").trim() || evolutionHome();
|
|
127
|
+
if (rawConfig.root !== void 0 && rawConfig.root.trim() !== "" && !isAbsolute(rawConfig.root)) ctx.logger.warn(`evolution-state-json: config.root "${rawConfig.root}" is a relative path and resolves against the process CWD ("${root}") — pass an absolute path to make the store location launch-independent`);
|
|
86
128
|
const io = () => ctx.evolutionIo.provider();
|
|
87
129
|
const pathOf = (file) => join(root, file);
|
|
130
|
+
const recordGateWarned = /* @__PURE__ */ new Set();
|
|
88
131
|
async function readJson(file) {
|
|
89
132
|
const raw = await io().readText(pathOf(file));
|
|
90
133
|
if (raw === null) return null;
|
|
@@ -95,8 +138,24 @@ function apply(ctx, rawConfig) {
|
|
|
95
138
|
return await quarantine(io, root, file, raw, error instanceof Error ? error.message : String(error));
|
|
96
139
|
}
|
|
97
140
|
if (RECORD_MAP_FILES.has(file) && !isPlainRecord(parsed)) return await quarantine(io, root, file, raw, `expected a plain JSON object (map of records), got ${Array.isArray(parsed) ? "an array" : parsed === null ? "null" : typeof parsed}`);
|
|
98
|
-
|
|
99
|
-
|
|
141
|
+
const malformed = firstNonRecordValue(parsed);
|
|
142
|
+
if (malformed !== null) return await quarantine(io, root, file, raw, malformed);
|
|
143
|
+
const gate = RECORD_FIELD_GATES[file];
|
|
144
|
+
if (gate !== void 0 && isPlainRecord(parsed)) {
|
|
145
|
+
const entries = Object.entries(parsed);
|
|
146
|
+
const failing = entries.filter(([, record]) => !isPlainRecord(record) || !gate(record)).map(([id]) => id);
|
|
147
|
+
if (failing.length > 0) {
|
|
148
|
+
const bad = {};
|
|
149
|
+
const good = {};
|
|
150
|
+
for (const [id, record] of entries) if (failing.includes(id)) bad[id] = record;
|
|
151
|
+
else good[id] = record;
|
|
152
|
+
await io().writeText(`${pathOf(file)}.corrupt`, JSON.stringify(bad, null, 2)).catch(() => {});
|
|
153
|
+
if (!recordGateWarned.has(file)) {
|
|
154
|
+
recordGateWarned.add(file);
|
|
155
|
+
ctx.logger.warn(`evolution-state-json: ${failing.length} record(s) in "${file}" failed the record schema gate and were quarantined to "${file}.corrupt": ${failing.join(", ")}`);
|
|
156
|
+
}
|
|
157
|
+
return good;
|
|
158
|
+
}
|
|
100
159
|
}
|
|
101
160
|
return parsed;
|
|
102
161
|
}
|
|
@@ -107,12 +166,12 @@ function apply(ctx, rawConfig) {
|
|
|
107
166
|
if (archivedIdsCache !== null) return archivedIdsCache;
|
|
108
167
|
const ids = /* @__PURE__ */ new Set();
|
|
109
168
|
try {
|
|
110
|
-
const rawArchive = await readJson(
|
|
169
|
+
const rawArchive = await readJson(PENDING_ARCHIVE_FILE);
|
|
111
170
|
if (Array.isArray(rawArchive)) {
|
|
112
171
|
for (const entry of rawArchive) if (entry && typeof entry.id === "string") ids.add(entry.id);
|
|
113
172
|
}
|
|
114
173
|
try {
|
|
115
|
-
const rawBak = await readJson(
|
|
174
|
+
const rawBak = await readJson(PENDING_ARCHIVE_BAK_FILE);
|
|
116
175
|
if (Array.isArray(rawBak)) {
|
|
117
176
|
for (const entry of rawBak) if (entry && typeof entry.id === "string") ids.add(entry.id);
|
|
118
177
|
}
|
|
@@ -134,19 +193,21 @@ function apply(ctx, rawConfig) {
|
|
|
134
193
|
if (legacyMigrated) return {};
|
|
135
194
|
try {
|
|
136
195
|
const retired = filterLegacy(legacy, current, await readArchivedIds());
|
|
137
|
-
await jsonTransact(io, root,
|
|
196
|
+
await jsonTransact(io, root, PENDING_STATE_FILE, (fresh) => ({
|
|
138
197
|
...retired,
|
|
139
198
|
...fresh ?? {}
|
|
140
199
|
}));
|
|
141
|
-
await io().rename(pathOf(
|
|
200
|
+
await io().rename(pathOf(PENDING_LEGACY_FILE), `${pathOf(PENDING_LEGACY_FILE)}.migrated`);
|
|
142
201
|
legacyMigrated = true;
|
|
143
202
|
return retired;
|
|
144
|
-
} catch {
|
|
203
|
+
} catch (error) {
|
|
204
|
+
if (error instanceof Error && error.name === QUARANTINE_ERROR_NAME) throw error;
|
|
205
|
+
ctx.logger.warn(`evolution-state-json: legacy pending retirement deferred: ${error instanceof Error ? error.message : String(error)}`);
|
|
145
206
|
return legacy;
|
|
146
207
|
}
|
|
147
208
|
}
|
|
148
209
|
async function loadPendingMap() {
|
|
149
|
-
const [current, legacy] = await Promise.all([readJson(
|
|
210
|
+
const [current, legacy] = await Promise.all([readJson(PENDING_STATE_FILE), readJson(PENDING_LEGACY_FILE)]);
|
|
150
211
|
if (legacy !== null) return {
|
|
151
212
|
...await retireLegacyOnce(legacy, current),
|
|
152
213
|
...current ?? {}
|
|
@@ -204,7 +265,7 @@ function apply(ctx, rawConfig) {
|
|
|
204
265
|
* bound. */
|
|
205
266
|
async function appendArchive(records) {
|
|
206
267
|
try {
|
|
207
|
-
await transactIo(io(), pathOf(
|
|
268
|
+
await transactIo(io(), pathOf(PENDING_ARCHIVE_FILE), async (current) => {
|
|
208
269
|
let archive = [];
|
|
209
270
|
if (current !== null) try {
|
|
210
271
|
const parsed = JSON.parse(current);
|
|
@@ -224,7 +285,7 @@ function apply(ctx, rawConfig) {
|
|
|
224
285
|
if (fresh.length === 0 && !hadDuplicates) return current;
|
|
225
286
|
const next = [...archive, ...fresh];
|
|
226
287
|
if (next.length > ARCHIVE_RESOLVED_CAP) {
|
|
227
|
-
if (archive.length > 0) await io().writeText(pathOf(
|
|
288
|
+
if (archive.length > 0) await io().writeText(pathOf(PENDING_ARCHIVE_BAK_FILE), JSON.stringify(archive, null, 2)).catch(() => {});
|
|
228
289
|
archivedIdsCache = null;
|
|
229
290
|
return JSON.stringify((fresh.length > 0 ? fresh : next).slice(-5e3), null, 2);
|
|
230
291
|
}
|
|
@@ -234,15 +295,15 @@ function apply(ctx, rawConfig) {
|
|
|
234
295
|
} catch {}
|
|
235
296
|
}
|
|
236
297
|
const provider = {
|
|
237
|
-
name:
|
|
298
|
+
name: PROVIDER_JSON,
|
|
238
299
|
async loadReviewState(sessionId) {
|
|
239
300
|
return await mutate(async () => {
|
|
240
|
-
return (await readJson(
|
|
301
|
+
return (await readJson(REVIEW_STATE_FILE))?.[sessionId] ?? null;
|
|
241
302
|
});
|
|
242
303
|
},
|
|
243
304
|
async saveReviewState(sessionId, record) {
|
|
244
305
|
await mutate(async () => {
|
|
245
|
-
await jsonTransact(io, root,
|
|
306
|
+
await jsonTransact(io, root, REVIEW_STATE_FILE, (current) => ({
|
|
246
307
|
...current ?? {},
|
|
247
308
|
[sessionId]: record
|
|
248
309
|
}));
|
|
@@ -250,25 +311,25 @@ function apply(ctx, rawConfig) {
|
|
|
250
311
|
},
|
|
251
312
|
async loadCuratorState() {
|
|
252
313
|
return await mutate(async () => {
|
|
253
|
-
return (await readJson(
|
|
314
|
+
return (await readJson(CURATOR_STATE_FILE))?.[CURATOR_STATE_KEY] ?? null;
|
|
254
315
|
});
|
|
255
316
|
},
|
|
256
317
|
async saveCuratorState(record) {
|
|
257
318
|
await mutate(async () => {
|
|
258
|
-
await jsonTransact(io, root,
|
|
319
|
+
await jsonTransact(io, root, CURATOR_STATE_FILE, (current) => ({
|
|
259
320
|
...current ?? {},
|
|
260
|
-
|
|
321
|
+
[CURATOR_STATE_KEY]: record
|
|
261
322
|
}));
|
|
262
323
|
});
|
|
263
324
|
},
|
|
264
325
|
async transactCuratorState(task) {
|
|
265
326
|
await mutate(async () => {
|
|
266
|
-
await jsonTransact(io, root,
|
|
267
|
-
const next = task(current?.
|
|
327
|
+
await jsonTransact(io, root, CURATOR_STATE_FILE, (current) => {
|
|
328
|
+
const next = task(current?.[CURATOR_STATE_KEY] ?? null);
|
|
268
329
|
if (next === null) return current;
|
|
269
330
|
return {
|
|
270
331
|
...current ?? {},
|
|
271
|
-
|
|
332
|
+
[CURATOR_STATE_KEY]: next
|
|
272
333
|
};
|
|
273
334
|
});
|
|
274
335
|
});
|
|
@@ -281,9 +342,9 @@ function apply(ctx, rawConfig) {
|
|
|
281
342
|
},
|
|
282
343
|
async savePending(record) {
|
|
283
344
|
await mutate(async () => {
|
|
284
|
-
await jsonTransact(io, root,
|
|
345
|
+
await jsonTransact(io, root, PENDING_STATE_FILE, async (current) => {
|
|
285
346
|
return {
|
|
286
|
-
...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(
|
|
347
|
+
...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(PENDING_LEGACY_FILE), current ?? {}),
|
|
287
348
|
[record.id]: record
|
|
288
349
|
};
|
|
289
350
|
});
|
|
@@ -292,8 +353,8 @@ function apply(ctx, rawConfig) {
|
|
|
292
353
|
async claimPending(id, claimId) {
|
|
293
354
|
return await mutate(async () => {
|
|
294
355
|
const slot = { claimed: null };
|
|
295
|
-
await jsonTransact(io, root,
|
|
296
|
-
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(
|
|
356
|
+
await jsonTransact(io, root, PENDING_STATE_FILE, async (current) => {
|
|
357
|
+
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(PENDING_LEGACY_FILE), current ?? {}) };
|
|
297
358
|
const record = map[id] ?? null;
|
|
298
359
|
if (record === null || !canClaimPending(record.status)) return map;
|
|
299
360
|
const now = Date.now();
|
|
@@ -311,8 +372,8 @@ function apply(ctx, rawConfig) {
|
|
|
311
372
|
},
|
|
312
373
|
async releasePendingClaim(id, claimId) {
|
|
313
374
|
await mutate(async () => {
|
|
314
|
-
await jsonTransact(io, root,
|
|
315
|
-
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(
|
|
375
|
+
await jsonTransact(io, root, PENDING_STATE_FILE, async (current) => {
|
|
376
|
+
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(PENDING_LEGACY_FILE), current ?? {}) };
|
|
316
377
|
const record = map[id];
|
|
317
378
|
if (!record || record.claimedBy !== claimId) return map;
|
|
318
379
|
if (record.status !== "pending" && record.status !== "executing") return map;
|
|
@@ -330,8 +391,8 @@ function apply(ctx, rawConfig) {
|
|
|
330
391
|
applied: false
|
|
331
392
|
};
|
|
332
393
|
let evicted = [];
|
|
333
|
-
await jsonTransact(io, root,
|
|
334
|
-
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(
|
|
394
|
+
await jsonTransact(io, root, PENDING_STATE_FILE, async (current) => {
|
|
395
|
+
const map = { ...await mergedWithFilteredLegacy(legacyMigrated ? null : await readJson(PENDING_LEGACY_FILE), current ?? {}) };
|
|
335
396
|
const record = map[id] ?? null;
|
|
336
397
|
if (record === null || !canResolvePending(record.status)) {
|
|
337
398
|
result = {
|
package/lib/types/index.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export declare const Config: z<Config>;
|
|
|
24
24
|
* array/scalar would be persisted as a corrupt map — so it fails loud before
|
|
25
25
|
* any write (0.3.28, V4-08). The legacy `pending.json` merge stays inside the
|
|
26
26
|
* task via `readJson` where relevant.
|
|
27
|
+
* @internal Exported only for this package's own tests — not public API
|
|
28
|
+
* surface (audit v10 S-03); other packages must go through the provider seam.
|
|
27
29
|
*/
|
|
28
30
|
export declare function jsonTransact<T>(io: () => EvolutionIoLike, root: string, file: string, task: (current: T | null) => T | null | Promise<T | null>): Promise<void>;
|
|
29
31
|
export declare function apply(ctx: Context, rawConfig: Config): void;
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-state-json",
|
|
3
3
|
"description": "JSON-file evolution state provider over the IO seam (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.52",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
10
10
|
"url": "git+https://github.com/lmzhen/dsh-evolution.git",
|
|
11
|
-
"directory": "packages/
|
|
11
|
+
"directory": "packages/evolution-state-json"
|
|
12
12
|
},
|
|
13
13
|
"type": "module",
|
|
14
14
|
"main": "lib/index.js",
|
|
@@ -31,18 +31,18 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
34
|
-
"@lmzhen/dsh-evolution-core": "^0.3.
|
|
34
|
+
"@lmzhen/dsh-evolution-core": "^0.3.52"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
38
38
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
39
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
40
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
39
|
+
"@lmzhen/dsh-evolution-io": "^0.3.52",
|
|
40
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.52"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
44
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
45
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-evolution-io-node": "^0.3.
|
|
44
|
+
"@lmzhen/dsh-evolution-io": "^0.3.52",
|
|
45
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.52",
|
|
46
|
+
"@lmzhen/dsh-evolution-io-node": "^0.3.52"
|
|
47
47
|
}
|
|
48
48
|
}
|