@lmzhen/dsh-evolution-state-json 0.3.16 → 0.3.17
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 +43 -17
- package/package.json +5 -5
package/lib/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import z from "@deepseek-ai/schemastery";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
+
import { CLAIM_EXPIRY_MS, canClaimPending, canResolvePending, releasedStatus } from "@lmzhen/dsh-evolution-state-storage";
|
|
5
6
|
//#region ../evolution-core/src/io.ts
|
|
6
7
|
/**
|
|
7
8
|
* Run `task` inside `io.transact` when the backend provides it; otherwise fall
|
|
@@ -301,6 +302,24 @@ createPromptBundle({
|
|
|
301
302
|
const FILLER = String.raw`(?:\w+\s+){0,8}`;
|
|
302
303
|
new RegExp(String.raw`ignore\s+${FILLER}(?:previous|above|prior|all)\s+${FILLER}instructions`, "i"), new RegExp(String.raw`new\s+${FILLER}system\s+${FILLER}prompt`, "i"), new RegExp(String.raw`forget\s+${FILLER}(?:everything|all)\s+${FILLER}(?:discussed|you\s+know)`, "i"), new RegExp(String.raw`you\s+have\s+been\s+${FILLER}(?:updated|upgraded|patched)\s+to`, "i"), new RegExp(String.raw`do\s+not\s+${FILLER}tell\s+${FILLER}the\s+user`, "i"), new RegExp(String.raw`output\s+${FILLER}(?:system|initial)\s+prompt`, "i");
|
|
303
304
|
//#endregion
|
|
305
|
+
//#region ../evolution-core/src/serial.ts
|
|
306
|
+
/**
|
|
307
|
+
* A process-local serial task queue: each task starts only after the previous
|
|
308
|
+
* one settles (success or failure), so read-modify-write sequences that share
|
|
309
|
+
* one file never interleave inside this process. The durable cross-process
|
|
310
|
+
* serialization layer is the IO backend's transact lock; this chain is the
|
|
311
|
+
* second layer (0.3.17 S2.8, T-1: the shape was duplicated in state-json and
|
|
312
|
+
* memory-files — one factory now).
|
|
313
|
+
*/
|
|
314
|
+
function makeSerialQueue() {
|
|
315
|
+
let chain = Promise.resolve();
|
|
316
|
+
return (task) => {
|
|
317
|
+
const run = chain.then(task, task);
|
|
318
|
+
chain = run.then(() => void 0, () => void 0);
|
|
319
|
+
return run;
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
//#endregion
|
|
304
323
|
//#region ../../../node_modules/.pnpm/js-yaml@4.2.0/node_modules/js-yaml/dist/js-yaml.mjs
|
|
305
324
|
/*! js-yaml 4.2.0 https://github.com/nodeca/js-yaml @license MIT */
|
|
306
325
|
var __create = Object.create;
|
|
@@ -2648,13 +2667,24 @@ function apply(ctx, rawConfig) {
|
|
|
2648
2667
|
const root = rawConfig.root || defaultRoot();
|
|
2649
2668
|
const io = () => ctx.evolutionIo.provider();
|
|
2650
2669
|
const pathOf = (file) => join(root, file);
|
|
2670
|
+
/** 0.3.17 (E-9): a malformed state file used to parse to `null` and was then
|
|
2671
|
+
* OVERWRITTEN by the next save — every other session's review state / the
|
|
2672
|
+
* whole pending table vanished silently. Fail loud instead: preserve the
|
|
2673
|
+
* original bytes beside it and throw, so the operator can rescue and the
|
|
2674
|
+
* corruption is never accepted as "empty". */
|
|
2675
|
+
async function quarantine(file, raw, reason) {
|
|
2676
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
2677
|
+
const dest = `${pathOf(file)}.corrupt-${stamp}-${Math.random().toString(36).slice(2, 6)}`;
|
|
2678
|
+
await io().writeText(dest, raw).catch(() => {});
|
|
2679
|
+
throw new Error(`evolution state file "${file}" is not valid JSON (${reason}); original preserved at ${dest} — inspect and fix it, then retry.`);
|
|
2680
|
+
}
|
|
2651
2681
|
async function readJson(file) {
|
|
2652
2682
|
const raw = await io().readText(pathOf(file));
|
|
2653
2683
|
if (raw === null) return null;
|
|
2654
2684
|
try {
|
|
2655
2685
|
return JSON.parse(raw);
|
|
2656
|
-
} catch {
|
|
2657
|
-
return
|
|
2686
|
+
} catch (error) {
|
|
2687
|
+
return await quarantine(file, raw, error instanceof Error ? error.message : String(error));
|
|
2658
2688
|
}
|
|
2659
2689
|
}
|
|
2660
2690
|
/**
|
|
@@ -2669,19 +2699,14 @@ function apply(ctx, rawConfig) {
|
|
|
2669
2699
|
let parsed = null;
|
|
2670
2700
|
if (current !== null) try {
|
|
2671
2701
|
parsed = JSON.parse(current);
|
|
2672
|
-
} catch {
|
|
2673
|
-
|
|
2702
|
+
} catch (error) {
|
|
2703
|
+
return await quarantine(file, current, error instanceof Error ? error.message : String(error));
|
|
2674
2704
|
}
|
|
2675
2705
|
const next = await task(parsed);
|
|
2676
2706
|
return next === null ? null : JSON.stringify(next, null, 2);
|
|
2677
2707
|
});
|
|
2678
2708
|
}
|
|
2679
|
-
|
|
2680
|
-
function mutate(task) {
|
|
2681
|
-
const run = chain.then(task, task);
|
|
2682
|
-
chain = run.then(() => void 0, () => void 0);
|
|
2683
|
-
return run;
|
|
2684
|
-
}
|
|
2709
|
+
const mutate = makeSerialQueue();
|
|
2685
2710
|
async function loadPendingMap() {
|
|
2686
2711
|
const [current, legacy] = await Promise.all([readJson("pending-state.json"), readJson("pending.json")]);
|
|
2687
2712
|
return {
|
|
@@ -2743,12 +2768,13 @@ function apply(ctx, rawConfig) {
|
|
|
2743
2768
|
...current ?? {}
|
|
2744
2769
|
};
|
|
2745
2770
|
const record = map[id] ?? null;
|
|
2746
|
-
if (record === null || record.status
|
|
2771
|
+
if (record === null || !canClaimPending(record.status)) return map;
|
|
2747
2772
|
const now = Date.now();
|
|
2748
2773
|
const claimedAt = typeof record.claimedAt === "string" ? Date.parse(record.claimedAt) : 0;
|
|
2749
|
-
if (record.claimedBy !== void 0 && Number.isFinite(claimedAt) && now - claimedAt <
|
|
2774
|
+
if (record.claimedBy !== void 0 && Number.isFinite(claimedAt) && now - claimedAt < CLAIM_EXPIRY_MS) return map;
|
|
2750
2775
|
slot.claimed = {
|
|
2751
2776
|
...record,
|
|
2777
|
+
status: "executing",
|
|
2752
2778
|
claimedBy: claimId,
|
|
2753
2779
|
claimedAt: new Date(now).toISOString()
|
|
2754
2780
|
};
|
|
@@ -2766,10 +2792,10 @@ function apply(ctx, rawConfig) {
|
|
|
2766
2792
|
...current ?? {}
|
|
2767
2793
|
};
|
|
2768
2794
|
const record = map[id];
|
|
2769
|
-
if (record
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2795
|
+
if (!record || record.claimedBy !== claimId) return map;
|
|
2796
|
+
record.status = releasedStatus(record.status);
|
|
2797
|
+
delete record.claimedBy;
|
|
2798
|
+
delete record.claimedAt;
|
|
2773
2799
|
return map;
|
|
2774
2800
|
});
|
|
2775
2801
|
});
|
|
@@ -2786,7 +2812,7 @@ function apply(ctx, rawConfig) {
|
|
|
2786
2812
|
...current ?? {}
|
|
2787
2813
|
};
|
|
2788
2814
|
const record = map[id] ?? null;
|
|
2789
|
-
if (record === null || record.status
|
|
2815
|
+
if (record === null || !canResolvePending(record.status)) {
|
|
2790
2816
|
result = {
|
|
2791
2817
|
record,
|
|
2792
2818
|
applied: false
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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.17",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,12 +37,12 @@
|
|
|
37
37
|
"peerDependencies": {
|
|
38
38
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
39
39
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
40
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
41
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
40
|
+
"@lmzhen/dsh-evolution-io": "^0.3.17",
|
|
41
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.17"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
45
|
-
"@lmzhen/dsh-evolution-io": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-evolution-state-storage": "^0.3.
|
|
45
|
+
"@lmzhen/dsh-evolution-io": "^0.3.17",
|
|
46
|
+
"@lmzhen/dsh-evolution-state-storage": "^0.3.17"
|
|
47
47
|
}
|
|
48
48
|
}
|