@lmzhen/dsh-evolution-feedback 0.1.0-rc.66 → 0.1.0-rc.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 +248 -53
- package/lib/types/index.d.ts +12 -2
- package/package.json +6 -6
package/lib/index.js
CHANGED
|
@@ -41,6 +41,76 @@ function evolutionIoAdapter(provider) {
|
|
|
41
41
|
}
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
|
+
function eventsFile(home) {
|
|
45
|
+
return join(home, "evolution", "events.json");
|
|
46
|
+
}
|
|
47
|
+
function isEventRecord(event) {
|
|
48
|
+
return typeof event === "object" && event !== null && typeof event.seq === "number";
|
|
49
|
+
}
|
|
50
|
+
function parseEventList(raw) {
|
|
51
|
+
if (raw === null) return [];
|
|
52
|
+
try {
|
|
53
|
+
const parsed = JSON.parse(raw);
|
|
54
|
+
if (!Array.isArray(parsed.events)) return [];
|
|
55
|
+
return parsed.events.filter(isEventRecord);
|
|
56
|
+
} catch {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Append one event under the write lock (rc.68): `seq` = current max + 1
|
|
62
|
+
* computed inside the transact, so two processes appending concurrently never
|
|
63
|
+
* collide. A malformed log is refused (bytes preserved) and the append fails.
|
|
64
|
+
* Returns the assigned seq.
|
|
65
|
+
*/
|
|
66
|
+
async function appendEvolutionEvent(io, path, event) {
|
|
67
|
+
let assigned = 0;
|
|
68
|
+
await transactIo(io, path, (current) => {
|
|
69
|
+
if (current !== null) try {
|
|
70
|
+
JSON.parse(current);
|
|
71
|
+
} catch {
|
|
72
|
+
return Promise.resolve(current);
|
|
73
|
+
}
|
|
74
|
+
const events = parseEventList(current);
|
|
75
|
+
const maxSeq = events.reduce((max, entry) => Math.max(max, entry.seq), 0);
|
|
76
|
+
const record = {
|
|
77
|
+
...event,
|
|
78
|
+
seq: maxSeq + 1,
|
|
79
|
+
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
80
|
+
};
|
|
81
|
+
assigned = record.seq;
|
|
82
|
+
return Promise.resolve(JSON.stringify({
|
|
83
|
+
version: 1,
|
|
84
|
+
events: [...events, record]
|
|
85
|
+
}, null, 2));
|
|
86
|
+
});
|
|
87
|
+
if (assigned === 0) throw new Error(`evolution event log is malformed and was not touched: ${path}`);
|
|
88
|
+
return assigned;
|
|
89
|
+
}
|
|
90
|
+
/** Read the event log; a missing file reads as empty, malformed is flagged. */
|
|
91
|
+
async function readEvolutionEvents(io, path) {
|
|
92
|
+
const raw = await io.readText(path);
|
|
93
|
+
if (raw === null) return {
|
|
94
|
+
events: [],
|
|
95
|
+
malformed: false
|
|
96
|
+
};
|
|
97
|
+
try {
|
|
98
|
+
const parsed = JSON.parse(raw);
|
|
99
|
+
if (!Array.isArray(parsed.events)) return {
|
|
100
|
+
events: [],
|
|
101
|
+
malformed: true
|
|
102
|
+
};
|
|
103
|
+
return {
|
|
104
|
+
events: parsed.events.filter(isEventRecord),
|
|
105
|
+
malformed: false
|
|
106
|
+
};
|
|
107
|
+
} catch {
|
|
108
|
+
return {
|
|
109
|
+
events: [],
|
|
110
|
+
malformed: true
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
44
114
|
//#endregion
|
|
45
115
|
//#region ../evolution-core/src/prompts.ts
|
|
46
116
|
/**
|
|
@@ -65,7 +135,7 @@ function evolutionIoAdapter(provider) {
|
|
|
65
135
|
* changes semantically: the bundle digest is the fail-closed signal for
|
|
66
136
|
* review workers, so a stale id across deployments must be distinguishable.
|
|
67
137
|
*/
|
|
68
|
-
const PROMPT_BUNDLE_ID = "dsh-evolution@
|
|
138
|
+
const PROMPT_BUNDLE_ID = "dsh-evolution@7";
|
|
69
139
|
const MEMORY_REVIEW_PROMPT = `[Auto-review — Memory]
|
|
70
140
|
Review the conversation above and consider saving to memory if appropriate.
|
|
71
141
|
|
|
@@ -86,6 +156,8 @@ Signals to look for (any one of these warrants action):
|
|
|
86
156
|
• Non-trivial technique, fix, workaround, debugging path, or tool-usage pattern emerged that a future session would benefit from. Capture it.
|
|
87
157
|
• A skill that got loaded or consulted this session turned out to be wrong, missing a step, or outdated. Patch it NOW.
|
|
88
158
|
|
|
159
|
+
Read-before-write (enforced by this channel): update, patch, delete, or write support files ONLY into skills you loaded or read in THIS session — ops on unread skills are dropped; CREATE of a brand-new umbrella is the only exception.
|
|
160
|
+
|
|
89
161
|
Preference order — prefer the earliest action that fits, but do pick one when a signal above fired:
|
|
90
162
|
1. UPDATE A CURRENTLY-LOADED SKILL. Look back through the conversation for skills the user loaded or you read. If any of them covers the territory of the new learning, PATCH that one first. It is the skill that was in play, so it's the right one to extend.
|
|
91
163
|
2. UPDATE AN EXISTING UMBRELLA. If no loaded skill fits but an existing class-level skill does, patch it. Add a subsection, a pitfall, or broaden a trigger.
|
|
@@ -133,6 +205,8 @@ Signals that warrant a skill update (any one is enough):
|
|
|
133
205
|
• Non-trivial technique, fix, workaround, or debugging path emerged.
|
|
134
206
|
• A skill that was loaded or consulted turned out wrong, missing, or outdated — patch it now.
|
|
135
207
|
|
|
208
|
+
Read-before-write (enforced by this channel): update, patch, delete, or write support files ONLY into skills you loaded or read in THIS session — ops on unread skills are dropped; CREATE of a brand-new umbrella is the only exception.
|
|
209
|
+
|
|
136
210
|
Preference order for skills — pick the earliest that fits:
|
|
137
211
|
1. UPDATE A CURRENTLY-LOADED SKILL. Check what skills were loaded or read in the conversation. If one of them covers the learning, PATCH it first. It was in play; it's the right place.
|
|
138
212
|
2. UPDATE AN EXISTING UMBRELLA. Patch it.
|
|
@@ -240,12 +314,12 @@ function sha256(text) {
|
|
|
240
314
|
function createPromptBundle(prompts) {
|
|
241
315
|
const canonical = JSON.stringify({
|
|
242
316
|
id: PROMPT_BUNDLE_ID,
|
|
243
|
-
version:
|
|
317
|
+
version: 7,
|
|
244
318
|
prompts: Object.fromEntries(Object.entries(prompts).sort())
|
|
245
319
|
});
|
|
246
320
|
return Object.freeze({
|
|
247
321
|
id: PROMPT_BUNDLE_ID,
|
|
248
|
-
version:
|
|
322
|
+
version: 7,
|
|
249
323
|
prompts: Object.freeze({ ...prompts }),
|
|
250
324
|
sha256: sha256(canonical)
|
|
251
325
|
});
|
|
@@ -272,8 +346,15 @@ new RegExp(String.raw`ignore\s+${FILLER}(?:previous|above|prior|all)\s+${FILLER}
|
|
|
272
346
|
* Feedback is durable through `ctx.evolutionIo` (when mounted) and skill
|
|
273
347
|
* feedback feeds `quality_score` / `quality_warn` on the usage record, so
|
|
274
348
|
* curator decisions can consume it deterministically.
|
|
349
|
+
*
|
|
350
|
+
* Persistence (rc.68): the EVENTS LOG (`evolution/events.json`, via
|
|
351
|
+
* `evolution-core/evolution-events.ts`) is the single source of truth —
|
|
352
|
+
* every increment appends one event under the write lock. `feedback.json` is
|
|
353
|
+
* a rebuildable BOOT CACHE (`{ version: 2, lastSeq, skills, sessions }`),
|
|
354
|
+
* never the truth; the in-memory state is the optimistic aggregate.
|
|
275
355
|
* @module @lmzhen/dsh-evolution-feedback
|
|
276
356
|
*/
|
|
357
|
+
const CACHE_VERSION = 2;
|
|
277
358
|
var EvolutionFeedback = class {
|
|
278
359
|
state = {
|
|
279
360
|
skills: {},
|
|
@@ -281,9 +362,13 @@ var EvolutionFeedback = class {
|
|
|
281
362
|
};
|
|
282
363
|
chain = Promise.resolve();
|
|
283
364
|
path;
|
|
365
|
+
eventsPath;
|
|
284
366
|
io;
|
|
285
367
|
constructor(io, home = process.env.DSH_HOME ?? join(homedir(), ".dsh"), pathOverride) {
|
|
286
|
-
if (io)
|
|
368
|
+
if (io) {
|
|
369
|
+
this.path = pathOverride ?? join(home, "evolution", "feedback.json");
|
|
370
|
+
this.eventsPath = eventsFile(home);
|
|
371
|
+
}
|
|
287
372
|
this.io = io;
|
|
288
373
|
}
|
|
289
374
|
mutate(task) {
|
|
@@ -293,26 +378,45 @@ var EvolutionFeedback = class {
|
|
|
293
378
|
}
|
|
294
379
|
async restore(io) {
|
|
295
380
|
const path = this.path;
|
|
296
|
-
|
|
381
|
+
const eventsPath = this.eventsPath;
|
|
382
|
+
if (!path || !eventsPath) return;
|
|
297
383
|
await this.mutate(async () => {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
384
|
+
if (await io.readText(eventsPath) === null) {
|
|
385
|
+
const aggregate = parseAggregate(await io.readText(path));
|
|
386
|
+
if (aggregate) try {
|
|
387
|
+
await transactIo(io, eventsPath, (current) => {
|
|
388
|
+
if (current !== null) return Promise.resolve(current);
|
|
389
|
+
return Promise.resolve(JSON.stringify({
|
|
390
|
+
version: 1,
|
|
391
|
+
events: synthesizeFeedbackEvents(aggregate)
|
|
392
|
+
}, null, 2));
|
|
393
|
+
});
|
|
394
|
+
} catch {}
|
|
395
|
+
}
|
|
396
|
+
const { events } = await readEvolutionEvents(io, eventsPath);
|
|
397
|
+
const cache = parseCache(await io.readText(path));
|
|
398
|
+
const maxSeq = events.reduce((max, event) => Math.max(max, event.seq), 0);
|
|
399
|
+
const truth = cache ? foldWithDelta(cache, events) : foldFeedbackState(events);
|
|
400
|
+
this.state = {
|
|
401
|
+
skills: {
|
|
402
|
+
...truth.skills,
|
|
403
|
+
...this.state.skills
|
|
404
|
+
},
|
|
405
|
+
sessions: {
|
|
406
|
+
...truth.sessions,
|
|
407
|
+
...this.state.sessions
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
if (maxSeq > 0 && (!cache || cache.lastSeq < maxSeq)) try {
|
|
411
|
+
await io.writeText(path, JSON.stringify({
|
|
412
|
+
version: CACHE_VERSION,
|
|
413
|
+
lastSeq: maxSeq,
|
|
414
|
+
...truth
|
|
415
|
+
}, null, 2));
|
|
312
416
|
} catch {}
|
|
313
417
|
});
|
|
314
418
|
}
|
|
315
|
-
record(target, rating, note, kind = "session"
|
|
419
|
+
record(target, rating, note, kind = "session") {
|
|
316
420
|
const mode = kind === "skill" ? "skills" : "sessions";
|
|
317
421
|
const table = this.state[mode];
|
|
318
422
|
const current = table[target] ?? {
|
|
@@ -322,28 +426,17 @@ var EvolutionFeedback = class {
|
|
|
322
426
|
current[rating] += 1;
|
|
323
427
|
if (note !== void 0) current.lastNote = note;
|
|
324
428
|
table[target] = current;
|
|
325
|
-
const recordIo =
|
|
326
|
-
const
|
|
327
|
-
if (!recordIo || !
|
|
429
|
+
const recordIo = this.io;
|
|
430
|
+
const eventsPath = this.eventsPath;
|
|
431
|
+
if (!recordIo || !eventsPath) return;
|
|
328
432
|
this.mutate(async () => {
|
|
329
433
|
try {
|
|
330
|
-
await
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const diskState = parseState(raw);
|
|
337
|
-
const diskTable = diskState[mode];
|
|
338
|
-
const diskRec = diskTable[target] ?? {
|
|
339
|
-
positive: 0,
|
|
340
|
-
negative: 0
|
|
341
|
-
};
|
|
342
|
-
diskRec[rating] += 1;
|
|
343
|
-
if (note !== void 0) diskRec.lastNote = note;
|
|
344
|
-
diskTable[target] = diskRec;
|
|
345
|
-
this.state = diskState;
|
|
346
|
-
return Promise.resolve(JSON.stringify(diskState, null, 2));
|
|
434
|
+
await appendEvolutionEvent(recordIo, eventsPath, {
|
|
435
|
+
type: "feedback",
|
|
436
|
+
target,
|
|
437
|
+
kind,
|
|
438
|
+
rating,
|
|
439
|
+
note
|
|
347
440
|
});
|
|
348
441
|
} catch (error) {}
|
|
349
442
|
});
|
|
@@ -365,26 +458,128 @@ var EvolutionFeedback = class {
|
|
|
365
458
|
waitIdle() {
|
|
366
459
|
return this.chain;
|
|
367
460
|
}
|
|
461
|
+
/** Rebuild the boot cache from the log truth (rc.68); best-effort. */
|
|
462
|
+
persistCache() {
|
|
463
|
+
const path = this.path;
|
|
464
|
+
const eventsPath = this.eventsPath;
|
|
465
|
+
const recordIo = this.io;
|
|
466
|
+
if (!path || !eventsPath || !recordIo) return Promise.resolve();
|
|
467
|
+
return this.mutate(async () => {
|
|
468
|
+
try {
|
|
469
|
+
const { events } = await readEvolutionEvents(recordIo, eventsPath);
|
|
470
|
+
const maxSeq = events.reduce((max, event) => Math.max(max, event.seq), 0);
|
|
471
|
+
if (maxSeq === 0) return;
|
|
472
|
+
await recordIo.writeText(path, JSON.stringify({
|
|
473
|
+
version: CACHE_VERSION,
|
|
474
|
+
lastSeq: maxSeq,
|
|
475
|
+
...foldFeedbackState(events)
|
|
476
|
+
}, null, 2));
|
|
477
|
+
} catch {}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
368
480
|
};
|
|
369
|
-
/** Parse a
|
|
370
|
-
function
|
|
371
|
-
if (raw === null) return
|
|
372
|
-
skills: {},
|
|
373
|
-
sessions: {}
|
|
374
|
-
};
|
|
481
|
+
/** Parse a legacy aggregate (v1) or a v2 cache into a plain aggregate state. */
|
|
482
|
+
function parseAggregate(raw) {
|
|
483
|
+
if (raw === null) return null;
|
|
375
484
|
try {
|
|
376
485
|
const parsed = JSON.parse(raw);
|
|
486
|
+
const skills = isRecord(parsed.skills) ? parsed.skills : void 0;
|
|
487
|
+
const sessions = isRecord(parsed.sessions) ? parsed.sessions : void 0;
|
|
488
|
+
if (!skills && !sessions) return null;
|
|
377
489
|
return {
|
|
378
|
-
skills:
|
|
379
|
-
sessions:
|
|
490
|
+
skills: skills ?? {},
|
|
491
|
+
sessions: sessions ?? {}
|
|
380
492
|
};
|
|
381
493
|
} catch {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function parseCache(raw) {
|
|
498
|
+
if (raw === null) return null;
|
|
499
|
+
try {
|
|
500
|
+
const parsed = JSON.parse(raw);
|
|
501
|
+
if (parsed.version !== CACHE_VERSION || typeof parsed.lastSeq !== "number") return null;
|
|
502
|
+
if (!isRecord(parsed.skills) || !isRecord(parsed.sessions)) return null;
|
|
382
503
|
return {
|
|
383
|
-
|
|
384
|
-
|
|
504
|
+
lastSeq: parsed.lastSeq,
|
|
505
|
+
state: {
|
|
506
|
+
skills: parsed.skills,
|
|
507
|
+
sessions: parsed.sessions
|
|
508
|
+
}
|
|
385
509
|
};
|
|
510
|
+
} catch {
|
|
511
|
+
return null;
|
|
386
512
|
}
|
|
387
513
|
}
|
|
514
|
+
function isRecord(value) {
|
|
515
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
516
|
+
}
|
|
517
|
+
/** Fold all feedback events from zero (the truth view). */
|
|
518
|
+
function foldFeedbackState(events) {
|
|
519
|
+
const state = {
|
|
520
|
+
skills: {},
|
|
521
|
+
sessions: {}
|
|
522
|
+
};
|
|
523
|
+
for (const event of events) applyFeedbackEvent(state, event);
|
|
524
|
+
return state;
|
|
525
|
+
}
|
|
526
|
+
/** Fold events after the cache's lastSeq onto the cached aggregates. */
|
|
527
|
+
function foldWithDelta(cache, events) {
|
|
528
|
+
const state = {
|
|
529
|
+
skills: { ...cache.state.skills },
|
|
530
|
+
sessions: { ...cache.state.sessions }
|
|
531
|
+
};
|
|
532
|
+
for (const event of events) if (event.seq > cache.lastSeq) applyFeedbackEvent(state, event);
|
|
533
|
+
return state;
|
|
534
|
+
}
|
|
535
|
+
function applyFeedbackEvent(state, event) {
|
|
536
|
+
if (event.type !== "feedback") return;
|
|
537
|
+
const target = event.target;
|
|
538
|
+
if (target === void 0 || event.rating === void 0) return;
|
|
539
|
+
const table = event.kind === "skill" ? state.skills : state.sessions;
|
|
540
|
+
const record = table[target] ?? {
|
|
541
|
+
positive: 0,
|
|
542
|
+
negative: 0
|
|
543
|
+
};
|
|
544
|
+
record[event.rating] += 1;
|
|
545
|
+
if (event.note !== void 0) record.lastNote = event.note;
|
|
546
|
+
table[target] = record;
|
|
547
|
+
}
|
|
548
|
+
/** Synthesize one event per aggregate count unit, lastNote on the final event (migration). */
|
|
549
|
+
function synthesizeFeedbackEvents(aggregate) {
|
|
550
|
+
const events = [];
|
|
551
|
+
const at = (/* @__PURE__ */ new Date()).toISOString();
|
|
552
|
+
const emitTarget = (kind, target, record) => {
|
|
553
|
+
const first = events.length + 1;
|
|
554
|
+
for (let index = 0; index < record.positive; index += 1) events.push({
|
|
555
|
+
seq: events.length + 1,
|
|
556
|
+
at,
|
|
557
|
+
type: "feedback",
|
|
558
|
+
kind,
|
|
559
|
+
target,
|
|
560
|
+
rating: "positive"
|
|
561
|
+
});
|
|
562
|
+
for (let index = 0; index < record.negative; index += 1) events.push({
|
|
563
|
+
seq: events.length + 1,
|
|
564
|
+
at,
|
|
565
|
+
type: "feedback",
|
|
566
|
+
kind,
|
|
567
|
+
target,
|
|
568
|
+
rating: "negative"
|
|
569
|
+
});
|
|
570
|
+
if (record.lastNote !== void 0 && events.length >= first) {
|
|
571
|
+
const last = events.length - 1;
|
|
572
|
+
const final = events[last];
|
|
573
|
+
if (final) events[last] = {
|
|
574
|
+
...final,
|
|
575
|
+
note: record.lastNote
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
for (const [target, record] of Object.entries(aggregate.skills)) emitTarget("skill", target, record);
|
|
580
|
+
for (const [target, record] of Object.entries(aggregate.sessions)) emitTarget("session", target, record);
|
|
581
|
+
return events;
|
|
582
|
+
}
|
|
388
583
|
const name = "evolution-feedback";
|
|
389
584
|
const Config = z.object({
|
|
390
585
|
qualityWarnThreshold: z.number().default(-.25),
|
|
@@ -401,8 +596,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
401
596
|
const skillUsage = ctx.get("skillUsage");
|
|
402
597
|
if (skillUsage) {
|
|
403
598
|
const original = feedback.record.bind(feedback);
|
|
404
|
-
feedback.record = (target, rating, note, kind
|
|
405
|
-
original(target, rating, note, kind ?? "session"
|
|
599
|
+
feedback.record = (target, rating, note, kind) => {
|
|
600
|
+
original(target, rating, note, kind ?? "session");
|
|
406
601
|
if (kind === "skill") {
|
|
407
602
|
const score = feedback.score(target, "skill");
|
|
408
603
|
const warn = score < (rawConfig.qualityWarnThreshold ?? -.25);
|
|
@@ -413,7 +608,7 @@ function apply(ctx, rawConfig = {}) {
|
|
|
413
608
|
};
|
|
414
609
|
}
|
|
415
610
|
ctx.effect(() => () => {
|
|
416
|
-
return feedback.waitIdle();
|
|
611
|
+
return Promise.all([feedback.persistCache(), feedback.waitIdle()]);
|
|
417
612
|
}, "evolution-feedback.records");
|
|
418
613
|
}
|
|
419
614
|
//#endregion
|
package/lib/types/index.d.ts
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
* Feedback is durable through `ctx.evolutionIo` (when mounted) and skill
|
|
5
5
|
* feedback feeds `quality_score` / `quality_warn` on the usage record, so
|
|
6
6
|
* curator decisions can consume it deterministically.
|
|
7
|
+
*
|
|
8
|
+
* Persistence (rc.68): the EVENTS LOG (`evolution/events.json`, via
|
|
9
|
+
* `evolution-core/evolution-events.ts`) is the single source of truth —
|
|
10
|
+
* every increment appends one event under the write lock. `feedback.json` is
|
|
11
|
+
* a rebuildable BOOT CACHE (`{ version: 2, lastSeq, skills, sessions }`),
|
|
12
|
+
* never the truth; the in-memory state is the optimistic aggregate.
|
|
7
13
|
* @module @deepseek-ai/dsh-evolution-feedback
|
|
8
14
|
*/
|
|
9
15
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -28,21 +34,25 @@ export declare class EvolutionFeedback {
|
|
|
28
34
|
private state;
|
|
29
35
|
private chain;
|
|
30
36
|
private readonly path?;
|
|
37
|
+
private readonly eventsPath?;
|
|
31
38
|
private readonly io;
|
|
32
39
|
constructor(io?: IoLike, home?: string, pathOverride?: string);
|
|
33
40
|
private mutate;
|
|
34
41
|
restore(io: IoLike): Promise<void>;
|
|
35
|
-
record(target: string, rating: 'positive' | 'negative', note?: string, kind?: 'skill' | 'session'
|
|
42
|
+
record(target: string, rating: 'positive' | 'negative', note?: string, kind?: 'skill' | 'session'): void;
|
|
36
43
|
score(target: string, kind?: 'skill' | 'session'): number;
|
|
37
44
|
snapshot(): FeedbackState;
|
|
38
45
|
/** Await the pending record-task chain (unload safety; rc.66). */
|
|
39
46
|
waitIdle(): Promise<unknown>;
|
|
47
|
+
/** Rebuild the boot cache from the log truth (rc.68); best-effort. */
|
|
48
|
+
persistCache(): Promise<void>;
|
|
40
49
|
}
|
|
41
50
|
export declare const name = "evolution-feedback";
|
|
42
51
|
export interface Config {
|
|
43
52
|
/** Score below which curator receives quality_warn for a skill. */
|
|
44
53
|
qualityWarnThreshold?: number;
|
|
45
|
-
/** Explicit
|
|
54
|
+
/** Explicit boot-cache file path; empty derives $DSH_HOME/evolution/feedback.json
|
|
55
|
+
* (the event log is its sibling `events.json`). */
|
|
46
56
|
path?: string;
|
|
47
57
|
}
|
|
48
58
|
export declare const Config: z<Config>;
|
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.1.0-rc.
|
|
4
|
+
"version": "0.1.0-rc.68",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -37,13 +37,13 @@
|
|
|
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.1.0-rc.
|
|
41
|
-
"@lmzhen/dsh-skill-usage": "^0.1.0-rc.
|
|
40
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.68",
|
|
41
|
+
"@lmzhen/dsh-skill-usage": "^0.1.0-rc.68"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
45
|
-
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.
|
|
46
|
-
"@lmzhen/dsh-evolution-io-node": "^0.1.0-rc.
|
|
47
|
-
"@lmzhen/dsh-skill-usage": "^0.1.0-rc.
|
|
45
|
+
"@lmzhen/dsh-evolution-io": "^0.1.0-rc.68",
|
|
46
|
+
"@lmzhen/dsh-evolution-io-node": "^0.1.0-rc.68",
|
|
47
|
+
"@lmzhen/dsh-skill-usage": "^0.1.0-rc.68"
|
|
48
48
|
}
|
|
49
49
|
}
|