@lmzhen/dsh-evolution-feedback 0.3.66 → 0.3.68
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 +33 -7
- package/lib/types/index.d.ts +7 -2
- package/package.json +7 -7
package/lib/index.js
CHANGED
|
@@ -48,11 +48,16 @@ var EvolutionFeedback = class EvolutionFeedback {
|
|
|
48
48
|
durableNote = /* @__PURE__ */ new Map();
|
|
49
49
|
/** P2-32 (v11): process-level bound — every feedbacked session would
|
|
50
50
|
* otherwise keep one entry for the whole process lifetime (a name + note
|
|
51
|
-
* string per session); cap
|
|
51
|
+
* string per session); cap this map and drop the earliest-INSERTED entry on
|
|
52
|
+
* overflow (Map iteration order is insertion order, and re-setting an
|
|
53
|
+
* existing key does not refresh its position, so the eviction is FIFO rather
|
|
54
|
+
* than least-recently-used). `warnedMessages` carries the same bound through
|
|
55
|
+
* `WARNED_CAP` below. */
|
|
52
56
|
static NOTE_CAP = 512;
|
|
53
57
|
/** V5-32 (0.3.31): fire-and-forget append failures are warn-once per unique
|
|
54
58
|
* message — a persistent refusal (e.g. a future-version log) must not spam
|
|
55
|
-
* the log on every user feedback entry (family posture: process-level once).
|
|
59
|
+
* the log on every user feedback entry (family posture: process-level once).
|
|
60
|
+
* Bounded like `durableNote`, with the same FIFO eviction. */
|
|
56
61
|
warnedMessages = /* @__PURE__ */ new Set();
|
|
57
62
|
WARNED_CAP = 512;
|
|
58
63
|
/** V5-29 (0.3.31): invoked after a FAILED append rolled back, so a caller
|
|
@@ -86,7 +91,7 @@ var EvolutionFeedback = class EvolutionFeedback {
|
|
|
86
91
|
const rawEvents = await io.readText(eventsPath);
|
|
87
92
|
const archiveNames = await listEventArchives(io, eventsPath);
|
|
88
93
|
if ((rawEvents === null || rawEvents.trim() === "") && archiveNames.length === 0) {
|
|
89
|
-
const aggregate = parseAggregate(await io.readText(path));
|
|
94
|
+
const aggregate = parseAggregate(await io.readText(path), this.warn);
|
|
90
95
|
if (aggregate) try {
|
|
91
96
|
await migrateFeedbackEvents(io, eventsPath, aggregate);
|
|
92
97
|
} catch {}
|
|
@@ -293,13 +298,34 @@ async function migrateFeedbackEvents(io, eventsPath, aggregate) {
|
|
|
293
298
|
}, null, 2));
|
|
294
299
|
});
|
|
295
300
|
}
|
|
296
|
-
/** Parse a legacy aggregate (v1) or a v2 cache into a plain aggregate state.
|
|
297
|
-
|
|
301
|
+
/** Parse a legacy aggregate (v1) or a v2 cache into a plain aggregate state.
|
|
302
|
+
* V24-05 (v24): every record passes the shared per-record sanitizer (same
|
|
303
|
+
* validation as `parseCache` — S6.4), and migrated counts are clamped so one
|
|
304
|
+
* corrupted field cannot expand into an unbounded event array at boot. */
|
|
305
|
+
const MAX_MIGRATED_EVENTS_PER_RECORD = 1e4;
|
|
306
|
+
function parseAggregate(raw, warn = () => {}) {
|
|
298
307
|
if (raw === null) return null;
|
|
299
308
|
try {
|
|
300
309
|
const parsed = JSON.parse(raw);
|
|
301
|
-
const
|
|
302
|
-
|
|
310
|
+
const clamp = (count, target, field) => {
|
|
311
|
+
if (count > MAX_MIGRATED_EVENTS_PER_RECORD) {
|
|
312
|
+
warn(`evolution-feedback: migration clamps ${field}=${count} for "${target}" to ${MAX_MIGRATED_EVENTS_PER_RECORD} — the on-disk aggregate is not trustworthy at that scale`);
|
|
313
|
+
return MAX_MIGRATED_EVENTS_PER_RECORD;
|
|
314
|
+
}
|
|
315
|
+
return count;
|
|
316
|
+
};
|
|
317
|
+
const sanitizeSection = (section, kind) => {
|
|
318
|
+
if (!isRecord(section)) return void 0;
|
|
319
|
+
const cleaned = sanitizeCacheRecords(section, kind, warn);
|
|
320
|
+
for (const [target, record] of Object.entries(cleaned)) if (record.positive > MAX_MIGRATED_EVENTS_PER_RECORD || record.negative > MAX_MIGRATED_EVENTS_PER_RECORD) cleaned[target] = {
|
|
321
|
+
...record,
|
|
322
|
+
positive: clamp(record.positive, target, "positive"),
|
|
323
|
+
negative: clamp(record.negative, target, "negative")
|
|
324
|
+
};
|
|
325
|
+
return cleaned;
|
|
326
|
+
};
|
|
327
|
+
const skills = sanitizeSection(parsed.skills, "skill");
|
|
328
|
+
const sessions = sanitizeSection(parsed.sessions, "session");
|
|
303
329
|
if (!skills && !sessions) return null;
|
|
304
330
|
return {
|
|
305
331
|
skills: skills ?? {},
|
package/lib/types/index.d.ts
CHANGED
|
@@ -52,11 +52,16 @@ export declare class EvolutionFeedback {
|
|
|
52
52
|
private readonly durableNote;
|
|
53
53
|
/** P2-32 (v11): process-level bound — every feedbacked session would
|
|
54
54
|
* otherwise keep one entry for the whole process lifetime (a name + note
|
|
55
|
-
* string per session); cap
|
|
55
|
+
* string per session); cap this map and drop the earliest-INSERTED entry on
|
|
56
|
+
* overflow (Map iteration order is insertion order, and re-setting an
|
|
57
|
+
* existing key does not refresh its position, so the eviction is FIFO rather
|
|
58
|
+
* than least-recently-used). `warnedMessages` carries the same bound through
|
|
59
|
+
* `WARNED_CAP` below. */
|
|
56
60
|
private static readonly NOTE_CAP;
|
|
57
61
|
/** V5-32 (0.3.31): fire-and-forget append failures are warn-once per unique
|
|
58
62
|
* message — a persistent refusal (e.g. a future-version log) must not spam
|
|
59
|
-
* the log on every user feedback entry (family posture: process-level once).
|
|
63
|
+
* the log on every user feedback entry (family posture: process-level once).
|
|
64
|
+
* Bounded like `durableNote`, with the same FIFO eviction. */
|
|
60
65
|
private readonly warnedMessages;
|
|
61
66
|
private readonly WARNED_CAP;
|
|
62
67
|
/** V5-29 (0.3.31): invoked after a FAILED append rolled back, so a caller
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-feedback",
|
|
3
3
|
"description": "Feedback-to-quality scoring for self-evolution (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.68",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -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.68"
|
|
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-skill-usage": "^0.3.
|
|
39
|
+
"@lmzhen/dsh-evolution-io": "^0.3.68",
|
|
40
|
+
"@lmzhen/dsh-skill-usage": "^0.3.68"
|
|
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-io-node": "^0.3.
|
|
46
|
-
"@lmzhen/dsh-skill-usage": "^0.3.
|
|
44
|
+
"@lmzhen/dsh-evolution-io": "^0.3.68",
|
|
45
|
+
"@lmzhen/dsh-evolution-io-node": "^0.3.68",
|
|
46
|
+
"@lmzhen/dsh-skill-usage": "^0.3.68"
|
|
47
47
|
}
|
|
48
48
|
}
|