@evomap/evolver 1.31.0 → 1.32.2
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/index.js +19 -12
- package/package.json +1 -1
- package/src/evolve.js +87 -7
- package/src/gep/a2aProtocol.js +20 -5
- package/src/gep/assetStore.js +28 -7
- package/src/gep/candidates.js +66 -4
- package/src/gep/learningSignals.js +89 -0
- package/src/gep/prompt.js +11 -0
- package/src/gep/selector.js +49 -2
- package/src/gep/skillDistiller.js +286 -110
- package/src/gep/solidify.js +137 -2
package/index.js
CHANGED
|
@@ -282,7 +282,7 @@ async function main() {
|
|
|
282
282
|
|
|
283
283
|
if (res && res.ok && !dryRun) {
|
|
284
284
|
try {
|
|
285
|
-
const { shouldDistill, prepareDistillation } = require('./src/gep/skillDistiller');
|
|
285
|
+
const { shouldDistill, prepareDistillation, autoDistill } = require('./src/gep/skillDistiller');
|
|
286
286
|
const { readStateForSolidify } = require('./src/gep/solidify');
|
|
287
287
|
const solidifyState = readStateForSolidify();
|
|
288
288
|
const count = solidifyState.solidify_count || 0;
|
|
@@ -290,16 +290,21 @@ async function main() {
|
|
|
290
290
|
const autoTrigger = count > 0 && count % autoDistillInterval === 0;
|
|
291
291
|
|
|
292
292
|
if (autoTrigger || shouldDistill()) {
|
|
293
|
-
const
|
|
294
|
-
if (
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
293
|
+
const auto = autoDistill();
|
|
294
|
+
if (auto && auto.ok && auto.gene) {
|
|
295
|
+
console.log('[Distiller] Auto-distilled gene: ' + auto.gene.id);
|
|
296
|
+
} else {
|
|
297
|
+
const dr = prepareDistillation();
|
|
298
|
+
if (dr && dr.ok && dr.promptPath) {
|
|
299
|
+
const trigger = autoTrigger ? `auto (every ${autoDistillInterval} solidifies, count=${count})` : 'threshold';
|
|
300
|
+
console.log('\n[DISTILL_REQUEST]');
|
|
301
|
+
console.log(`Distillation triggered: ${trigger}`);
|
|
302
|
+
console.log('Read the prompt file, process it with your LLM,');
|
|
303
|
+
console.log('save the LLM response to a file, then run:');
|
|
304
|
+
console.log(' node index.js distill --response-file=<path_to_llm_response>');
|
|
305
|
+
console.log('Prompt file: ' + dr.promptPath);
|
|
306
|
+
console.log('[/DISTILL_REQUEST]');
|
|
307
|
+
}
|
|
303
308
|
}
|
|
304
309
|
}
|
|
305
310
|
} catch (e) {
|
|
@@ -454,7 +459,9 @@ async function main() {
|
|
|
454
459
|
const s = readJsonSafe(sp);
|
|
455
460
|
if (s && s.last_run) {
|
|
456
461
|
s.last_solidify = { run_id: s.last_run.run_id, rejected: true, timestamp: new Date().toISOString() };
|
|
457
|
-
|
|
462
|
+
const tmpReject = `${sp}.tmp`;
|
|
463
|
+
fs.writeFileSync(tmpReject, JSON.stringify(s, null, 2) + '\n', 'utf8');
|
|
464
|
+
fs.renameSync(tmpReject, sp);
|
|
458
465
|
}
|
|
459
466
|
}
|
|
460
467
|
console.log('[Review] Changes rolled back.');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.32.2",
|
|
4
4
|
"description": "A GEP-powered self-evolution engine for AI agents. Features automated log analysis and Genome Evolution Protocol (GEP) for auditable, reusable evolution assets.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
package/src/evolve.js
CHANGED
|
@@ -39,6 +39,8 @@ const { getEvolutionDir } = require('./gep/paths');
|
|
|
39
39
|
const { shouldReflect, buildReflectionContext, recordReflection } = require('./gep/reflection');
|
|
40
40
|
const { loadNarrativeSummary } = require('./gep/narrativeMemory');
|
|
41
41
|
const { maybeReportIssue } = require('./gep/issueReporter');
|
|
42
|
+
const { resolveStrategy } = require('./gep/strategy');
|
|
43
|
+
const { expandSignals } = require('./gep/learningSignals');
|
|
42
44
|
|
|
43
45
|
const REPO_ROOT = getRepoRoot();
|
|
44
46
|
|
|
@@ -352,6 +354,73 @@ function readRecentLog(filePath, size = 10000) {
|
|
|
352
354
|
}
|
|
353
355
|
}
|
|
354
356
|
|
|
357
|
+
function computeAdaptiveStrategyPolicy(opts) {
|
|
358
|
+
const recentEvents = Array.isArray(opts && opts.recentEvents) ? opts.recentEvents : [];
|
|
359
|
+
const selectedGene = opts && opts.selectedGene ? opts.selectedGene : null;
|
|
360
|
+
const signals = Array.isArray(opts && opts.signals) ? opts.signals : [];
|
|
361
|
+
const baseStrategy = resolveStrategy({ signals: signals });
|
|
362
|
+
|
|
363
|
+
const tail = recentEvents.slice(-8);
|
|
364
|
+
let repairStreak = 0;
|
|
365
|
+
for (let i = tail.length - 1; i >= 0; i--) {
|
|
366
|
+
if (tail[i] && tail[i].intent === 'repair') repairStreak++;
|
|
367
|
+
else break;
|
|
368
|
+
}
|
|
369
|
+
let failureStreak = 0;
|
|
370
|
+
for (let i = tail.length - 1; i >= 0; i--) {
|
|
371
|
+
if (tail[i] && tail[i].outcome && tail[i].outcome.status === 'failed') failureStreak++;
|
|
372
|
+
else break;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const antiPatterns = selectedGene && Array.isArray(selectedGene.anti_patterns) ? selectedGene.anti_patterns.slice(-5) : [];
|
|
376
|
+
const learningHistory = selectedGene && Array.isArray(selectedGene.learning_history) ? selectedGene.learning_history.slice(-6) : [];
|
|
377
|
+
const signalTags = new Set(expandSignals(signals, ''));
|
|
378
|
+
const overlappingAntiPatterns = antiPatterns.filter(function (ap) {
|
|
379
|
+
return ap && Array.isArray(ap.learning_signals) && ap.learning_signals.some(function (tag) {
|
|
380
|
+
return signalTags.has(String(tag));
|
|
381
|
+
});
|
|
382
|
+
});
|
|
383
|
+
const hardFailures = overlappingAntiPatterns.filter(function (ap) { return ap && ap.mode === 'hard'; }).length;
|
|
384
|
+
const softFailures = overlappingAntiPatterns.filter(function (ap) { return ap && ap.mode !== 'hard'; }).length;
|
|
385
|
+
const recentSuccesses = learningHistory.filter(function (x) { return x && x.outcome === 'success'; }).length;
|
|
386
|
+
|
|
387
|
+
const stagnation = signals.includes('stable_success_plateau') ||
|
|
388
|
+
signals.includes('evolution_saturation') ||
|
|
389
|
+
signals.includes('empty_cycle_loop_detected') ||
|
|
390
|
+
failureStreak >= 3 ||
|
|
391
|
+
repairStreak >= 3;
|
|
392
|
+
|
|
393
|
+
const forceInnovate = stagnation && !signals.includes('log_error');
|
|
394
|
+
const highRiskGene = hardFailures >= 1 || (softFailures >= 2 && recentSuccesses === 0);
|
|
395
|
+
const cautiousExecution = highRiskGene || failureStreak >= 2;
|
|
396
|
+
|
|
397
|
+
let blastRadiusMaxFiles = selectedGene && selectedGene.constraints && Number.isFinite(Number(selectedGene.constraints.max_files))
|
|
398
|
+
? Number(selectedGene.constraints.max_files)
|
|
399
|
+
: 12;
|
|
400
|
+
if (cautiousExecution) blastRadiusMaxFiles = Math.max(2, Math.min(blastRadiusMaxFiles, 6));
|
|
401
|
+
else if (forceInnovate) blastRadiusMaxFiles = Math.max(3, Math.min(blastRadiusMaxFiles, 10));
|
|
402
|
+
|
|
403
|
+
const directives = [];
|
|
404
|
+
directives.push('Base strategy: ' + baseStrategy.label + ' (' + baseStrategy.description + ')');
|
|
405
|
+
if (forceInnovate) directives.push('Force strategy shift: prefer innovate over repeating repair/optimize.');
|
|
406
|
+
if (highRiskGene) directives.push('Selected gene is high risk for current signals; keep blast radius narrow and prefer smallest viable change.');
|
|
407
|
+
if (failureStreak >= 2) directives.push('Recent failure streak detected; avoid repeating recent failed approach.');
|
|
408
|
+
directives.push('Target max files for this cycle: ' + blastRadiusMaxFiles + '.');
|
|
409
|
+
|
|
410
|
+
return {
|
|
411
|
+
name: baseStrategy.name,
|
|
412
|
+
label: baseStrategy.label,
|
|
413
|
+
description: baseStrategy.description,
|
|
414
|
+
forceInnovate: forceInnovate,
|
|
415
|
+
cautiousExecution: cautiousExecution,
|
|
416
|
+
highRiskGene: highRiskGene,
|
|
417
|
+
repairStreak: repairStreak,
|
|
418
|
+
failureStreak: failureStreak,
|
|
419
|
+
blastRadiusMaxFiles: blastRadiusMaxFiles,
|
|
420
|
+
directives: directives,
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
|
|
355
424
|
function checkSystemHealth() {
|
|
356
425
|
const report = [];
|
|
357
426
|
try {
|
|
@@ -1279,6 +1348,7 @@ async function run() {
|
|
|
1279
1348
|
const newCandidates = extractCapabilityCandidates({
|
|
1280
1349
|
recentSessionTranscript: recentMasterLog,
|
|
1281
1350
|
signals,
|
|
1351
|
+
recentFailedCapsules: readRecentFailedCapsules(50),
|
|
1282
1352
|
});
|
|
1283
1353
|
for (const c of newCandidates) {
|
|
1284
1354
|
try {
|
|
@@ -1433,6 +1503,11 @@ async function run() {
|
|
|
1433
1503
|
? capsuleCandidates.map(c => (c && c.id ? String(c.id) : null)).filter(Boolean)
|
|
1434
1504
|
: [];
|
|
1435
1505
|
const selectedCapsuleId = capsulesUsed.length ? capsulesUsed[0] : null;
|
|
1506
|
+
const strategyPolicy = computeAdaptiveStrategyPolicy({
|
|
1507
|
+
recentEvents,
|
|
1508
|
+
selectedGene,
|
|
1509
|
+
signals,
|
|
1510
|
+
});
|
|
1436
1511
|
|
|
1437
1512
|
// Personality selection (natural selection + small mutation when triggered).
|
|
1438
1513
|
// This state is persisted in MEMORY_DIR and is treated as an evolution control surface (not role-play).
|
|
@@ -1463,9 +1538,9 @@ async function run() {
|
|
|
1463
1538
|
tailAvgScore >= 0.7;
|
|
1464
1539
|
const forceInnovation =
|
|
1465
1540
|
String(process.env.FORCE_INNOVATION || process.env.EVOLVE_FORCE_INNOVATION || '').toLowerCase() === 'true';
|
|
1466
|
-
const mutationInnovateMode = !!IS_RANDOM_DRIFT || !!innovationPressure || !!forceInnovation;
|
|
1541
|
+
const mutationInnovateMode = !!IS_RANDOM_DRIFT || !!innovationPressure || !!forceInnovation || !!strategyPolicy.forceInnovate;
|
|
1467
1542
|
const mutationSignals = innovationPressure ? [...(Array.isArray(signals) ? signals : []), 'stable_success_plateau'] : signals;
|
|
1468
|
-
const mutationSignalsEffective = forceInnovation
|
|
1543
|
+
const mutationSignalsEffective = (forceInnovation || strategyPolicy.forceInnovate)
|
|
1469
1544
|
? [...(Array.isArray(mutationSignals) ? mutationSignals : []), 'force_innovation']
|
|
1470
1545
|
: mutationSignals;
|
|
1471
1546
|
|
|
@@ -1565,10 +1640,13 @@ async function run() {
|
|
|
1565
1640
|
console.warn('[SolidifyState] Failed to read git HEAD:', e && e.message || e);
|
|
1566
1641
|
}
|
|
1567
1642
|
|
|
1568
|
-
const maxFiles =
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1643
|
+
const maxFiles = strategyPolicy && Number.isFinite(Number(strategyPolicy.blastRadiusMaxFiles))
|
|
1644
|
+
? Number(strategyPolicy.blastRadiusMaxFiles)
|
|
1645
|
+
: (
|
|
1646
|
+
selectedGene && selectedGene.constraints && Number.isFinite(Number(selectedGene.constraints.max_files))
|
|
1647
|
+
? Number(selectedGene.constraints.max_files)
|
|
1648
|
+
: 12
|
|
1649
|
+
);
|
|
1572
1650
|
const blastRadiusEstimate = {
|
|
1573
1651
|
files: Number.isFinite(maxFiles) && maxFiles > 0 ? maxFiles : 0,
|
|
1574
1652
|
lines: Number.isFinite(maxFiles) && maxFiles > 0 ? Math.round(maxFiles * 80) : 0,
|
|
@@ -1602,6 +1680,7 @@ async function run() {
|
|
|
1602
1680
|
baseline_untracked: baselineUntracked,
|
|
1603
1681
|
baseline_git_head: baselineHead,
|
|
1604
1682
|
blast_radius_estimate: blastRadiusEstimate,
|
|
1683
|
+
strategy_policy: strategyPolicy,
|
|
1605
1684
|
active_task_id: activeTask ? (activeTask.id || activeTask.task_id || null) : null,
|
|
1606
1685
|
active_task_title: activeTask ? (activeTask.title || null) : null,
|
|
1607
1686
|
worker_assignment_id: activeTask ? (activeTask._worker_assignment_id || null) : null,
|
|
@@ -1739,6 +1818,7 @@ ${mutationDirective}
|
|
|
1739
1818
|
capabilityCandidatesPreview,
|
|
1740
1819
|
externalCandidatesPreview,
|
|
1741
1820
|
hubMatchedBlock,
|
|
1821
|
+
strategyPolicy,
|
|
1742
1822
|
failedCapsules: recentFailedCapsules,
|
|
1743
1823
|
hubLessons,
|
|
1744
1824
|
});
|
|
@@ -1827,5 +1907,5 @@ ${mutationDirective}
|
|
|
1827
1907
|
}
|
|
1828
1908
|
}
|
|
1829
1909
|
|
|
1830
|
-
module.exports = { run };
|
|
1910
|
+
module.exports = { run, computeAdaptiveStrategyPolicy };
|
|
1831
1911
|
|
package/src/gep/a2aProtocol.js
CHANGED
|
@@ -305,7 +305,9 @@ function unwrapAssetFromMessage(input) {
|
|
|
305
305
|
function ensureDir(dir) {
|
|
306
306
|
try {
|
|
307
307
|
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
308
|
-
} catch (e) {
|
|
308
|
+
} catch (e) {
|
|
309
|
+
console.warn('[a2aProtocol] ensureDir failed:', dir, e && e.message || e);
|
|
310
|
+
}
|
|
309
311
|
}
|
|
310
312
|
|
|
311
313
|
function defaultA2ADir() {
|
|
@@ -337,7 +339,9 @@ function fileTransportReceive(opts) {
|
|
|
337
339
|
if (msg && msg.protocol === PROTOCOL_NAME) messages.push(msg);
|
|
338
340
|
} catch (e) {}
|
|
339
341
|
}
|
|
340
|
-
} catch (e) {
|
|
342
|
+
} catch (e) {
|
|
343
|
+
console.warn('[a2aProtocol] Failed to read inbox file:', files[fi], e && e.message || e);
|
|
344
|
+
}
|
|
341
345
|
}
|
|
342
346
|
return messages;
|
|
343
347
|
}
|
|
@@ -407,6 +411,8 @@ var _latestNoveltyHint = null;
|
|
|
407
411
|
var _latestCapabilityGaps = [];
|
|
408
412
|
var _pendingCommitmentUpdates = [];
|
|
409
413
|
var _cachedHubNodeSecret = null;
|
|
414
|
+
var _cachedHubNodeSecretAt = 0;
|
|
415
|
+
var _SECRET_CACHE_TTL_MS = 60000;
|
|
410
416
|
var _heartbeatIntervalMs = 0;
|
|
411
417
|
var _heartbeatRunning = false;
|
|
412
418
|
|
|
@@ -428,7 +434,9 @@ function _persistNodeSecret(secret) {
|
|
|
428
434
|
fs.mkdirSync(NODE_ID_DIR, { recursive: true, mode: 0o700 });
|
|
429
435
|
}
|
|
430
436
|
fs.writeFileSync(NODE_SECRET_FILE, secret, { encoding: 'utf8', mode: 0o600 });
|
|
431
|
-
} catch {
|
|
437
|
+
} catch (e) {
|
|
438
|
+
console.warn('[a2aProtocol] Failed to persist node secret:', e && e.message || e);
|
|
439
|
+
}
|
|
432
440
|
}
|
|
433
441
|
|
|
434
442
|
function getHubUrl() {
|
|
@@ -464,6 +472,7 @@ function sendHelloToHub() {
|
|
|
464
472
|
|| null;
|
|
465
473
|
if (secret && /^[a-f0-9]{64}$/i.test(secret)) {
|
|
466
474
|
_cachedHubNodeSecret = secret;
|
|
475
|
+
_cachedHubNodeSecretAt = Date.now();
|
|
467
476
|
_persistNodeSecret(secret);
|
|
468
477
|
}
|
|
469
478
|
return { ok: true, response: data };
|
|
@@ -473,10 +482,14 @@ function sendHelloToHub() {
|
|
|
473
482
|
|
|
474
483
|
function getHubNodeSecret() {
|
|
475
484
|
if (process.env.A2A_NODE_SECRET) return process.env.A2A_NODE_SECRET;
|
|
476
|
-
|
|
485
|
+
var now = Date.now();
|
|
486
|
+
if (_cachedHubNodeSecret && (now - _cachedHubNodeSecretAt) < _SECRET_CACHE_TTL_MS) {
|
|
487
|
+
return _cachedHubNodeSecret;
|
|
488
|
+
}
|
|
477
489
|
var persisted = _loadPersistedNodeSecret();
|
|
478
490
|
if (persisted) {
|
|
479
491
|
_cachedHubNodeSecret = persisted;
|
|
492
|
+
_cachedHubNodeSecretAt = now;
|
|
480
493
|
return persisted;
|
|
481
494
|
}
|
|
482
495
|
if (process.env.A2A_HUB_TOKEN) return process.env.A2A_HUB_TOKEN;
|
|
@@ -529,7 +542,9 @@ function sendHeartbeat() {
|
|
|
529
542
|
meta.env_fingerprint = fp;
|
|
530
543
|
_heartbeatFpSent = true;
|
|
531
544
|
}
|
|
532
|
-
} catch {
|
|
545
|
+
} catch (e) {
|
|
546
|
+
console.warn('[a2aProtocol] Failed to capture env fingerprint:', e && e.message || e);
|
|
547
|
+
}
|
|
533
548
|
}
|
|
534
549
|
|
|
535
550
|
if (Object.keys(meta).length > 0) {
|
package/src/gep/assetStore.js
CHANGED
|
@@ -100,7 +100,9 @@ function loadGenes() {
|
|
|
100
100
|
}
|
|
101
101
|
});
|
|
102
102
|
}
|
|
103
|
-
} catch(e) {
|
|
103
|
+
} catch(e) {
|
|
104
|
+
console.warn('[AssetStore] Failed to read genes.jsonl:', e && e.message || e);
|
|
105
|
+
}
|
|
104
106
|
|
|
105
107
|
// Combine and deduplicate by ID (JSONL takes precedence if newer, but here we just merge)
|
|
106
108
|
const combined = [...jsonGenes, ...jsonlGenes];
|
|
@@ -124,7 +126,9 @@ function loadCapsules() {
|
|
|
124
126
|
}
|
|
125
127
|
});
|
|
126
128
|
}
|
|
127
|
-
} catch(e) {
|
|
129
|
+
} catch(e) {
|
|
130
|
+
console.warn('[AssetStore] Failed to read capsules.jsonl:', e && e.message || e);
|
|
131
|
+
}
|
|
128
132
|
|
|
129
133
|
// Combine and deduplicate by ID
|
|
130
134
|
const combined = [...legacy, ...jsonlCapsules];
|
|
@@ -144,7 +148,10 @@ function getLastEventId() {
|
|
|
144
148
|
if (lines.length === 0) return null;
|
|
145
149
|
const last = JSON.parse(lines[lines.length - 1]);
|
|
146
150
|
return last && typeof last.id === 'string' ? last.id : null;
|
|
147
|
-
} catch {
|
|
151
|
+
} catch (e) {
|
|
152
|
+
console.warn('[AssetStore] Failed to read last event ID:', e && e.message || e);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
148
155
|
}
|
|
149
156
|
|
|
150
157
|
function readAllEvents() {
|
|
@@ -155,7 +162,10 @@ function readAllEvents() {
|
|
|
155
162
|
return raw.split('\n').map(l => l.trim()).filter(Boolean).map(l => {
|
|
156
163
|
try { return JSON.parse(l); } catch { return null; }
|
|
157
164
|
}).filter(Boolean);
|
|
158
|
-
} catch {
|
|
165
|
+
} catch (e) {
|
|
166
|
+
console.warn('[AssetStore] Failed to read events.jsonl:', e && e.message || e);
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
function appendEventJsonl(eventObj) {
|
|
@@ -198,7 +208,10 @@ function readRecentCandidates(limit = 20) {
|
|
|
198
208
|
} finally {
|
|
199
209
|
fs.closeSync(fd);
|
|
200
210
|
}
|
|
201
|
-
} catch {
|
|
211
|
+
} catch (e) {
|
|
212
|
+
console.warn('[AssetStore] Failed to read candidates.jsonl:', e && e.message || e);
|
|
213
|
+
return [];
|
|
214
|
+
}
|
|
202
215
|
}
|
|
203
216
|
|
|
204
217
|
function readRecentExternalCandidates(limit = 50) {
|
|
@@ -225,14 +238,21 @@ function readRecentExternalCandidates(limit = 50) {
|
|
|
225
238
|
} finally {
|
|
226
239
|
fs.closeSync(fd);
|
|
227
240
|
}
|
|
228
|
-
} catch {
|
|
241
|
+
} catch (e) {
|
|
242
|
+
console.warn('[AssetStore] Failed to read external_candidates.jsonl:', e && e.message || e);
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
229
245
|
}
|
|
230
246
|
|
|
231
247
|
// Safety net: ensure schema_version and asset_id are present before writing.
|
|
232
248
|
function ensureSchemaFields(obj) {
|
|
233
249
|
if (!obj || typeof obj !== 'object') return obj;
|
|
234
250
|
if (!obj.schema_version) obj.schema_version = SCHEMA_VERSION;
|
|
235
|
-
if (!obj.asset_id) {
|
|
251
|
+
if (!obj.asset_id) {
|
|
252
|
+
try { obj.asset_id = computeAssetId(obj); } catch (e) {
|
|
253
|
+
console.warn('[AssetStore] Failed to compute asset ID:', e && e.message || e);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
236
256
|
return obj;
|
|
237
257
|
}
|
|
238
258
|
|
|
@@ -287,6 +307,7 @@ function readRecentFailedCapsules(limit) {
|
|
|
287
307
|
var list = Array.isArray(current.failed_capsules) ? current.failed_capsules : [];
|
|
288
308
|
return list.slice(Math.max(0, list.length - n));
|
|
289
309
|
} catch (e) {
|
|
310
|
+
console.warn('[AssetStore] Failed to read failed_capsules.json:', e && e.message || e);
|
|
290
311
|
return [];
|
|
291
312
|
}
|
|
292
313
|
}
|
package/src/gep/candidates.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
const { expandSignals } = require('./learningSignals');
|
|
2
|
+
|
|
1
3
|
function stableHash(input) {
|
|
2
4
|
// Deterministic lightweight hash (not cryptographic).
|
|
3
5
|
const s = String(input || '');
|
|
@@ -60,13 +62,15 @@ function buildFiveQuestionsShape({ title, signals, evidence }) {
|
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
|
|
63
|
-
function extractCapabilityCandidates({ recentSessionTranscript, signals }) {
|
|
65
|
+
function extractCapabilityCandidates({ recentSessionTranscript, signals, recentFailedCapsules }) {
|
|
64
66
|
const candidates = [];
|
|
67
|
+
const signalList = Array.isArray(signals) ? signals : [];
|
|
68
|
+
const expandedTags = expandSignals(signalList, recentSessionTranscript);
|
|
65
69
|
const toolCalls = extractToolCalls(recentSessionTranscript);
|
|
66
70
|
const freq = countFreq(toolCalls);
|
|
67
71
|
|
|
68
72
|
for (const [tool, count] of freq.entries()) {
|
|
69
|
-
if (count <
|
|
73
|
+
if (count < 3) continue;
|
|
70
74
|
const title = `Repeated tool usage: ${tool}`;
|
|
71
75
|
const evidence = `Observed ${count} occurrences of tool call marker for ${tool}.`;
|
|
72
76
|
const shape = buildFiveQuestionsShape({ title, signals, evidence });
|
|
@@ -76,13 +80,13 @@ function extractCapabilityCandidates({ recentSessionTranscript, signals }) {
|
|
|
76
80
|
title,
|
|
77
81
|
source: 'transcript',
|
|
78
82
|
created_at: new Date().toISOString(),
|
|
79
|
-
signals:
|
|
83
|
+
signals: signalList,
|
|
84
|
+
tags: expandedTags,
|
|
80
85
|
shape,
|
|
81
86
|
});
|
|
82
87
|
}
|
|
83
88
|
|
|
84
89
|
// Signals-as-candidates: capture recurring pain points as reusable capability shapes.
|
|
85
|
-
const signalList = Array.isArray(signals) ? signals : [];
|
|
86
90
|
const signalCandidates = [
|
|
87
91
|
// Defensive signals
|
|
88
92
|
{ signal: 'log_error', title: 'Repair recurring runtime errors' },
|
|
@@ -109,10 +113,67 @@ function extractCapabilityCandidates({ recentSessionTranscript, signals }) {
|
|
|
109
113
|
source: 'signals',
|
|
110
114
|
created_at: new Date().toISOString(),
|
|
111
115
|
signals: signalList,
|
|
116
|
+
tags: expandedTags,
|
|
112
117
|
shape,
|
|
113
118
|
});
|
|
114
119
|
}
|
|
115
120
|
|
|
121
|
+
var failedCapsules = Array.isArray(recentFailedCapsules) ? recentFailedCapsules : [];
|
|
122
|
+
var groups = {};
|
|
123
|
+
var problemPriority = [
|
|
124
|
+
'problem:performance',
|
|
125
|
+
'problem:protocol',
|
|
126
|
+
'problem:reliability',
|
|
127
|
+
'problem:stagnation',
|
|
128
|
+
'problem:capability',
|
|
129
|
+
];
|
|
130
|
+
for (var i = 0; i < failedCapsules.length; i++) {
|
|
131
|
+
var fc = failedCapsules[i];
|
|
132
|
+
if (!fc || fc.outcome && fc.outcome.status === 'success') continue;
|
|
133
|
+
var reason = String(fc.failure_reason || '').trim();
|
|
134
|
+
var failureTags = expandSignals((fc.trigger || []).concat(signalList), reason).filter(function (t) {
|
|
135
|
+
return t.indexOf('problem:') === 0 || t.indexOf('risk:') === 0 || t.indexOf('area:') === 0 || t.indexOf('action:') === 0;
|
|
136
|
+
});
|
|
137
|
+
if (failureTags.length === 0) continue;
|
|
138
|
+
var dominantProblem = null;
|
|
139
|
+
for (var p = 0; p < problemPriority.length; p++) {
|
|
140
|
+
if (failureTags.indexOf(problemPriority[p]) !== -1) {
|
|
141
|
+
dominantProblem = problemPriority[p];
|
|
142
|
+
break;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
var groupingTags = dominantProblem
|
|
146
|
+
? [dominantProblem]
|
|
147
|
+
: failureTags.filter(function (tag) { return tag.indexOf('area:') === 0 || tag.indexOf('risk:') === 0; }).slice(0, 1);
|
|
148
|
+
var key = groupingTags.join('|');
|
|
149
|
+
if (!groups[key]) groups[key] = { count: 0, tags: failureTags, reasons: [], gene: fc.gene || null };
|
|
150
|
+
groups[key].count += 1;
|
|
151
|
+
if (reason) groups[key].reasons.push(reason);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
Object.keys(groups).forEach(function (key) {
|
|
155
|
+
var group = groups[key];
|
|
156
|
+
if (!group || group.count < 2) return;
|
|
157
|
+
var title = 'Learn from recurring failed evolution paths';
|
|
158
|
+
if (group.tags.indexOf('problem:performance') !== -1) title = 'Resolve recurring performance regressions';
|
|
159
|
+
else if (group.tags.indexOf('problem:protocol') !== -1) title = 'Prevent recurring protocol and validation regressions';
|
|
160
|
+
else if (group.tags.indexOf('problem:reliability') !== -1) title = 'Repair recurring reliability failures';
|
|
161
|
+
else if (group.tags.indexOf('problem:stagnation') !== -1) title = 'Break repeated stagnation loops with a new strategy';
|
|
162
|
+
else if (group.tags.indexOf('area:orchestration') !== -1) title = 'Stabilize task and orchestration behavior';
|
|
163
|
+
var evidence = 'Observed ' + group.count + ' recent failed evolutions with similar learning tags. ' +
|
|
164
|
+
(group.reasons[0] ? 'Latest reason: ' + clip(group.reasons[0], 180) : '');
|
|
165
|
+
candidates.push({
|
|
166
|
+
type: 'CapabilityCandidate',
|
|
167
|
+
id: 'cand_' + stableHash('failed:' + key),
|
|
168
|
+
title: title,
|
|
169
|
+
source: 'failed_capsules',
|
|
170
|
+
created_at: new Date().toISOString(),
|
|
171
|
+
signals: signalList,
|
|
172
|
+
tags: group.tags,
|
|
173
|
+
shape: buildFiveQuestionsShape({ title: title, signals: signalList, evidence: evidence }),
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
116
177
|
// Dedup by id
|
|
117
178
|
const seen = new Set();
|
|
118
179
|
return candidates.filter(c => {
|
|
@@ -142,5 +203,6 @@ function renderCandidatesPreview(candidates, maxChars = 1400) {
|
|
|
142
203
|
module.exports = {
|
|
143
204
|
extractCapabilityCandidates,
|
|
144
205
|
renderCandidatesPreview,
|
|
206
|
+
expandSignals,
|
|
145
207
|
};
|
|
146
208
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// Structured learning signal expansion: raw signals -> categorized tags for gene selection and evolution feedback.
|
|
2
|
+
function unique(items) {
|
|
3
|
+
return Array.from(new Set((Array.isArray(items) ? items : []).filter(Boolean).map(function (x) {
|
|
4
|
+
return String(x).trim();
|
|
5
|
+
}).filter(Boolean)));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function add(tags, value) {
|
|
9
|
+
if (!value) return;
|
|
10
|
+
tags.push(String(value).trim());
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function expandSignals(signals, extraText) {
|
|
14
|
+
var raw = Array.isArray(signals) ? signals.map(function (s) { return String(s); }) : [];
|
|
15
|
+
var tags = [];
|
|
16
|
+
|
|
17
|
+
for (var i = 0; i < raw.length; i++) {
|
|
18
|
+
var signal = raw[i];
|
|
19
|
+
add(tags, signal);
|
|
20
|
+
var base = signal.split(':')[0];
|
|
21
|
+
if (base && base !== signal) add(tags, base);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
var text = (raw.join(' ') + ' ' + String(extraText || '')).toLowerCase();
|
|
25
|
+
|
|
26
|
+
if (/(error|exception|failed|unstable|log_error|runtime|429)/.test(text)) {
|
|
27
|
+
add(tags, 'problem:reliability');
|
|
28
|
+
add(tags, 'action:repair');
|
|
29
|
+
}
|
|
30
|
+
if (/(protocol|prompt|audit|gep|schema|drift)/.test(text)) {
|
|
31
|
+
add(tags, 'problem:protocol');
|
|
32
|
+
add(tags, 'action:optimize');
|
|
33
|
+
add(tags, 'area:prompt');
|
|
34
|
+
}
|
|
35
|
+
if (/(perf|performance|bottleneck|latency|slow|throughput)/.test(text)) {
|
|
36
|
+
add(tags, 'problem:performance');
|
|
37
|
+
add(tags, 'action:optimize');
|
|
38
|
+
}
|
|
39
|
+
if (/(feature|capability_gap|user_feature_request|external_opportunity|stagnation recommendation)/.test(text)) {
|
|
40
|
+
add(tags, 'problem:capability');
|
|
41
|
+
add(tags, 'action:innovate');
|
|
42
|
+
}
|
|
43
|
+
if (/(stagnation|plateau|steady_state|saturation|empty_cycle_loop|loop_detected|recurring)/.test(text)) {
|
|
44
|
+
add(tags, 'problem:stagnation');
|
|
45
|
+
add(tags, 'action:innovate');
|
|
46
|
+
}
|
|
47
|
+
if (/(task|worker|heartbeat|hub|commitment|assignment|orchestration)/.test(text)) {
|
|
48
|
+
add(tags, 'area:orchestration');
|
|
49
|
+
}
|
|
50
|
+
if (/(memory|narrative|reflection)/.test(text)) {
|
|
51
|
+
add(tags, 'area:memory');
|
|
52
|
+
}
|
|
53
|
+
if (/(skill|dashboard)/.test(text)) {
|
|
54
|
+
add(tags, 'area:skills');
|
|
55
|
+
}
|
|
56
|
+
if (/(validation|canary|rollback|constraint|blast radius|destructive)/.test(text)) {
|
|
57
|
+
add(tags, 'risk:validation');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
return unique(tags);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function geneTags(gene) {
|
|
64
|
+
if (!gene || typeof gene !== 'object') return [];
|
|
65
|
+
var inputs = [];
|
|
66
|
+
if (gene.category) inputs.push('action:' + String(gene.category).toLowerCase());
|
|
67
|
+
if (Array.isArray(gene.signals_match)) inputs = inputs.concat(gene.signals_match);
|
|
68
|
+
if (typeof gene.id === 'string') inputs.push(gene.id);
|
|
69
|
+
if (typeof gene.summary === 'string') inputs.push(gene.summary);
|
|
70
|
+
return expandSignals(inputs, '');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function scoreTagOverlap(gene, signals) {
|
|
74
|
+
var signalTags = expandSignals(signals, '');
|
|
75
|
+
var geneTagList = geneTags(gene);
|
|
76
|
+
if (signalTags.length === 0 || geneTagList.length === 0) return 0;
|
|
77
|
+
var signalSet = new Set(signalTags);
|
|
78
|
+
var hits = 0;
|
|
79
|
+
for (var i = 0; i < geneTagList.length; i++) {
|
|
80
|
+
if (signalSet.has(geneTagList[i])) hits++;
|
|
81
|
+
}
|
|
82
|
+
return hits;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
module.exports = {
|
|
86
|
+
expandSignals: expandSignals,
|
|
87
|
+
geneTags: geneTags,
|
|
88
|
+
scoreTagOverlap: scoreTagOverlap,
|
|
89
|
+
};
|
package/src/gep/prompt.js
CHANGED
|
@@ -266,6 +266,7 @@ function buildGepPrompt({
|
|
|
266
266
|
recentHistory,
|
|
267
267
|
failedCapsules,
|
|
268
268
|
hubLessons,
|
|
269
|
+
strategyPolicy,
|
|
269
270
|
}) {
|
|
270
271
|
const parentValue = parentEventId ? `"${parentEventId}"` : 'null';
|
|
271
272
|
const selectedGeneId = selectedGene && selectedGene.id ? selectedGene.id : 'gene_<name>';
|
|
@@ -289,6 +290,15 @@ ACTIVE STRATEGY (Generic):
|
|
|
289
290
|
3. Apply minimal, safe changes.
|
|
290
291
|
4. Validate changes strictly.
|
|
291
292
|
5. Solidify knowledge.
|
|
293
|
+
`.trim();
|
|
294
|
+
}
|
|
295
|
+
let strategyPolicyBlock = '';
|
|
296
|
+
if (strategyPolicy && Array.isArray(strategyPolicy.directives) && strategyPolicy.directives.length > 0) {
|
|
297
|
+
strategyPolicyBlock = `
|
|
298
|
+
ADAPTIVE STRATEGY POLICY:
|
|
299
|
+
${strategyPolicy.directives.map((s, i) => `${i + 1}. ${s}`).join('\n')}
|
|
300
|
+
${strategyPolicy.forceInnovate ? 'You MUST prefer INNOVATE unless a critical blocking error is present.' : ''}
|
|
301
|
+
${strategyPolicy.cautiousExecution ? 'You MUST reduce blast radius and avoid broad refactors in this cycle.' : ''}
|
|
292
302
|
`.trim();
|
|
293
303
|
}
|
|
294
304
|
|
|
@@ -384,6 +394,7 @@ II. Directives & Logic
|
|
|
384
394
|
|
|
385
395
|
2. Selection: Selected Gene "${selectedGeneId}".
|
|
386
396
|
${strategyBlock}
|
|
397
|
+
${strategyPolicyBlock ? '\n' + strategyPolicyBlock : ''}
|
|
387
398
|
|
|
388
399
|
3. Execution: Apply changes (tool calls). Repair/Optimize: small/reversible. Innovate: new skills in \`skills/<name>/\`.
|
|
389
400
|
4. Validation: Run gene's validation steps. Fail = ROLLBACK.
|