@evomap/evolver 1.78.9 → 1.79.0

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.
Files changed (42) hide show
  1. package/README.ja-JP.md +1 -0
  2. package/README.ko-KR.md +1 -0
  3. package/README.md +1 -0
  4. package/README.zh-CN.md +1 -0
  5. package/index.js +177 -7
  6. package/package.json +1 -1
  7. package/scripts/build_binaries.js +388 -0
  8. package/src/evolve.js +1 -1
  9. package/src/gep/.integrity +0 -0
  10. package/src/gep/a2aProtocol.js +1 -1
  11. package/src/gep/candidateEval.js +1 -1
  12. package/src/gep/candidates.js +1 -1
  13. package/src/gep/contentHash.js +1 -1
  14. package/src/gep/crypto.js +1 -1
  15. package/src/gep/curriculum.js +1 -1
  16. package/src/gep/deviceId.js +1 -1
  17. package/src/gep/envFingerprint.js +1 -1
  18. package/src/gep/explore.js +1 -1
  19. package/src/gep/hubReview.js +1 -1
  20. package/src/gep/hubSearch.js +1 -1
  21. package/src/gep/hubVerify.js +1 -1
  22. package/src/gep/integrityCheck.js +1 -1
  23. package/src/gep/learningSignals.js +1 -1
  24. package/src/gep/memoryGraph.js +1 -1
  25. package/src/gep/memoryGraphAdapter.js +1 -1
  26. package/src/gep/mutation.js +1 -1
  27. package/src/gep/narrativeMemory.js +1 -1
  28. package/src/gep/personality.js +1 -1
  29. package/src/gep/policyCheck.js +1 -1
  30. package/src/gep/prompt.js +1 -1
  31. package/src/gep/reflection.js +1 -1
  32. package/src/gep/selector.js +1 -1
  33. package/src/gep/shield.js +1 -1
  34. package/src/gep/skillDistiller.js +1 -1
  35. package/src/gep/solidify.js +1 -1
  36. package/src/gep/strategy.js +1 -1
  37. package/assets/gep/candidates.jsonl +0 -1
  38. package/assets/gep/capsules.json +0 -4
  39. package/assets/gep/events.jsonl +0 -0
  40. package/assets/gep/failed_capsules.json +0 -4
  41. package/assets/gep/genes.json +0 -201
  42. package/assets/gep/genes.jsonl +0 -0
package/README.ja-JP.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0)
5
5
  [![Node.js >= 18](https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg)](https://nodejs.org/)
6
6
  [![GitHub last commit](https://img.shields.io/github/last-commit/EvoMap/evolver)](https://github.com/EvoMap/evolver/commits/main)
7
+ [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
7
8
  [![GitHub issues](https://img.shields.io/github/issues/EvoMap/evolver)](https://github.com/EvoMap/evolver/issues)
8
9
  [![arXiv](https://img.shields.io/badge/arXiv-2604.15097-b31b1b.svg)](https://arxiv.org/abs/2604.15097)
9
10
 
package/README.ko-KR.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0)
5
5
  [![Node.js >= 18](https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg)](https://nodejs.org/)
6
6
  [![GitHub last commit](https://img.shields.io/github/last-commit/EvoMap/evolver)](https://github.com/EvoMap/evolver/commits/main)
7
+ [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
7
8
  [![GitHub issues](https://img.shields.io/github/issues/EvoMap/evolver)](https://github.com/EvoMap/evolver/issues)
8
9
  [![arXiv](https://img.shields.io/badge/arXiv-2604.15097-b31b1b.svg)](https://arxiv.org/abs/2604.15097)
9
10
 
package/README.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0)
5
5
  [![Node.js >= 18](https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg)](https://nodejs.org/)
6
6
  [![GitHub last commit](https://img.shields.io/github/last-commit/EvoMap/evolver)](https://github.com/EvoMap/evolver/commits/main)
7
+ [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
7
8
  [![GitHub issues](https://img.shields.io/github/issues/EvoMap/evolver)](https://github.com/EvoMap/evolver/issues)
8
9
  [![arXiv](https://img.shields.io/badge/arXiv-2604.15097-b31b1b.svg)](https://arxiv.org/abs/2604.15097)
9
10
 
package/README.zh-CN.md CHANGED
@@ -4,6 +4,7 @@
4
4
  [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0)
5
5
  [![Node.js >= 18](https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg)](https://nodejs.org/)
6
6
  [![GitHub last commit](https://img.shields.io/github/last-commit/EvoMap/evolver)](https://github.com/EvoMap/evolver/commits/main)
7
+ [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
7
8
  [![GitHub issues](https://img.shields.io/github/issues/EvoMap/evolver)](https://github.com/EvoMap/evolver/issues)
8
9
  [![arXiv](https://img.shields.io/badge/arXiv-2604.15097-b31b1b.svg)](https://arxiv.org/abs/2604.15097)
9
10
 
package/index.js CHANGED
@@ -104,6 +104,42 @@ function parseMs(v, fallback) {
104
104
  return fallback;
105
105
  }
106
106
 
107
+ function parseBoolEnv(v, fallback) {
108
+ if (v == null) return fallback;
109
+ const s = String(v).toLowerCase().trim();
110
+ if (s === '' ) return fallback;
111
+ if (s === 'false' || s === '0' || s === 'off' || s === 'no') return false;
112
+ if (s === 'true' || s === '1' || s === 'on' || s === 'yes') return true;
113
+ return fallback;
114
+ }
115
+
116
+ class CycleTimeoutError extends Error {
117
+ constructor(timeoutMs, phase, cycleNum) {
118
+ super('Cycle hard-timeout exceeded after ' + timeoutMs + 'ms (cycle=' + cycleNum + ', phase=' + phase + ')');
119
+ this.name = 'CycleTimeoutError';
120
+ this.code = 'CYCLE_TIMEOUT';
121
+ this.timeoutMs = timeoutMs;
122
+ this.phase = phase;
123
+ this.cycleNum = cycleNum;
124
+ }
125
+ }
126
+
127
+ // Atomic write of the cycle_progress.json file. Wrapper polls this file every
128
+ // 60s; if updated_at goes stale beyond EVOLVE_INNER_STUCK_TIMEOUT_SEC the
129
+ // wrapper treats the inner core as zombie and SIGKILLs it. See Issue #19 (the
130
+ // 22-day stuck-cycle incident) and the cross-repo timeout plan for context.
131
+ function writeCycleProgressAtomic(progressPath, fields) {
132
+ try {
133
+ const data = Object.assign({}, fields, { updated_at: Date.now() });
134
+ const tmp = progressPath + '.tmp.' + process.pid;
135
+ fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
136
+ fs.renameSync(tmp, progressPath);
137
+ return true;
138
+ } catch (e) {
139
+ return false;
140
+ }
141
+ }
142
+
107
143
  function getLastSignals(statePath) {
108
144
  try {
109
145
  const st = readJsonSafe(statePath);
@@ -255,6 +291,7 @@ async function main() {
255
291
 
256
292
  const { getEvolutionDir, getEvolverLogPath } = require('./src/gep/paths');
257
293
  const solidifyStatePath = path.join(getEvolutionDir(), 'evolution_solidify_state.json');
294
+ const cycleProgressPath = path.join(getEvolutionDir(), 'cycle_progress.json');
258
295
 
259
296
  const minSleepMs = parseMs(process.env.EVOLVER_MIN_SLEEP_MS, 2000);
260
297
  const maxSleepMs = parseMs(process.env.EVOLVER_MAX_SLEEP_MS, 300000);
@@ -270,6 +307,15 @@ async function main() {
270
307
  const maxRssMb = parseMs(process.env.EVOLVER_MAX_RSS_MB, 500) || 500;
271
308
  const suicideEnabled = String(process.env.EVOLVER_SUICIDE || '').toLowerCase() !== 'false';
272
309
 
310
+ // Issue #19: hard timeout around evolve.run() to break out of zombie
311
+ // cycles (e.g. unclosed socket / stuck LLM call). On timeout we throw
312
+ // CycleTimeoutError, log diagnostic stderr, and force suicide-respawn
313
+ // so the wrapper sees a fresh PID + cycle. Also write cycle_progress
314
+ // every progressUpdateMs so the wrapper has a true heartbeat to poll.
315
+ const cycleTimeoutEnabled = parseBoolEnv(process.env.EVOLVER_CYCLE_TIMEOUT_ENABLED, true);
316
+ const cycleTimeoutMs = parseMs(process.env.EVOLVER_CYCLE_TIMEOUT_MS, 2700000); // 45 min default
317
+ const progressUpdateMs = parseMs(process.env.EVOLVER_PROGRESS_UPDATE_MS, 60000); // 1 min default
318
+
273
319
  // Start hub heartbeat (keeps node alive independently of evolution cycles)
274
320
  try {
275
321
  if (process.env.EVOMAP_PROXY === '1' || process.env.A2A_TRANSPORT === 'mailbox') {
@@ -388,8 +434,46 @@ async function main() {
388
434
 
389
435
  const t0 = Date.now();
390
436
  let ok = false;
437
+ // Issue #19: write progress at cycle start, refresh it every
438
+ // progressUpdateMs (default 60s) while evolve.run() is active, and
439
+ // wrap evolve.run() with Promise.race(timeout) so a hung internal
440
+ // call cannot freeze the daemon for days.
441
+ writeCycleProgressAtomic(cycleProgressPath, {
442
+ pid: process.pid,
443
+ outer_cycle: cycleCount,
444
+ inner_cycle: cycleCount,
445
+ started_at: t0,
446
+ phase: 'evolve.run',
447
+ });
448
+ let progressTicker = null;
449
+ if (progressUpdateMs > 0) {
450
+ progressTicker = setInterval(function () {
451
+ writeCycleProgressAtomic(cycleProgressPath, {
452
+ pid: process.pid,
453
+ outer_cycle: cycleCount,
454
+ inner_cycle: cycleCount,
455
+ started_at: t0,
456
+ phase: 'evolve.run',
457
+ });
458
+ }, progressUpdateMs);
459
+ if (typeof progressTicker.unref === 'function') progressTicker.unref();
460
+ }
461
+ let cycleTimeoutHandle = null;
462
+ let cycleTimedOut = false;
391
463
  try {
392
- await evolve.run();
464
+ const evolvePromise = evolve.run();
465
+ if (cycleTimeoutEnabled && cycleTimeoutMs > 0) {
466
+ const timeoutPromise = new Promise(function (_, reject) {
467
+ cycleTimeoutHandle = setTimeout(function () {
468
+ cycleTimedOut = true;
469
+ reject(new CycleTimeoutError(cycleTimeoutMs, 'evolve.run', cycleCount));
470
+ }, cycleTimeoutMs);
471
+ if (cycleTimeoutHandle && typeof cycleTimeoutHandle.unref === 'function') cycleTimeoutHandle.unref();
472
+ });
473
+ await Promise.race([evolvePromise, timeoutPromise]);
474
+ } else {
475
+ await evolvePromise;
476
+ }
393
477
  ok = true;
394
478
 
395
479
  if (String(process.env.EVOLVE_BRIDGE || '').toLowerCase() === 'false') {
@@ -403,7 +487,37 @@ async function main() {
403
487
  }
404
488
  } catch (error) {
405
489
  const msg = error && error.message ? String(error.message) : String(error);
490
+ if (error && error.code === 'CYCLE_TIMEOUT') {
491
+ console.error('[Daemon] ' + msg);
492
+ if (progressTicker) { clearInterval(progressTicker); progressTicker = null; }
493
+ if (cycleTimeoutHandle) { clearTimeout(cycleTimeoutHandle); cycleTimeoutHandle = null; }
494
+ writeCycleProgressAtomic(cycleProgressPath, {
495
+ pid: process.pid,
496
+ outer_cycle: cycleCount,
497
+ inner_cycle: cycleCount,
498
+ started_at: t0,
499
+ phase: 'cycle_timeout_respawn',
500
+ });
501
+ try {
502
+ const logFd = fs.openSync(getEvolverLogPath(), 'a');
503
+ const spawnOpts = {
504
+ detached: true,
505
+ stdio: ['ignore', logFd, logFd],
506
+ env: process.env,
507
+ windowsHide: true,
508
+ };
509
+ const child = spawn(process.execPath, [__filename, ...args], spawnOpts);
510
+ child.unref();
511
+ } catch (spawnErr) {
512
+ console.error('[Daemon] Force-restart spawn after cycle timeout failed: ' + (spawnErr && spawnErr.message || spawnErr));
513
+ }
514
+ releaseLock();
515
+ process.exit(1);
516
+ }
406
517
  console.error(`Evolution cycle failed: ${msg}`);
518
+ } finally {
519
+ if (progressTicker) { clearInterval(progressTicker); progressTicker = null; }
520
+ if (cycleTimeoutHandle) { clearTimeout(cycleTimeoutHandle); cycleTimeoutHandle = null; }
407
521
  }
408
522
  const dt = Date.now() - t0;
409
523
 
@@ -496,6 +610,13 @@ async function main() {
496
610
  const signals = getLastSignals(solidifyStatePath).join(',');
497
611
  console.log(`[Verbose] cycle=${cycleCount} ok=${ok} dt=${dt}ms sleep=${totalSleepMs}ms (base=${currentSleepMs} jitter=${jitter} sat=${saturationMultiplier}x) rss=${memMb}MB signals=[${signals}]`);
498
612
  }
613
+ writeCycleProgressAtomic(cycleProgressPath, {
614
+ pid: process.pid,
615
+ outer_cycle: cycleCount,
616
+ inner_cycle: cycleCount,
617
+ started_at: t0,
618
+ phase: 'sleep',
619
+ });
499
620
  await sleepMs(totalSleepMs);
500
621
 
501
622
  } catch (loopErr) {
@@ -1097,6 +1218,7 @@ async function main() {
1097
1218
  })();
1098
1219
  const dryRun = args.includes('--dry-run');
1099
1220
  const listUnpublished = !args.includes('--no-unpublished-list');
1221
+ const force = args.includes('--force');
1100
1222
  const limitPerPage = 100;
1101
1223
 
1102
1224
  const validScopes = new Set(['all', 'purchased', 'published']);
@@ -1173,11 +1295,25 @@ async function main() {
1173
1295
 
1174
1296
  const existingGenes = loadGenes();
1175
1297
  const existingCapsules = loadCapsules();
1298
+ // Dedup by Hub asset_id is the only safe key. Local-facing `id` (e.g.
1299
+ // `gene_gep_repair_from_errors`) collides between bundled default seed
1300
+ // genes and identically-named assets that the user later published, so
1301
+ // dedup-by-id silently skips legitimate Hub copies on first sync. Track
1302
+ // hub_asset_id (set by previous syncs / publishes) and only skip when
1303
+ // we've already seen the same Hub-side identity.
1304
+ const localHubAssetIds = new Set();
1305
+ for (const g of existingGenes) {
1306
+ if (g && g.hub_asset_id) localHubAssetIds.add(String(g.hub_asset_id));
1307
+ }
1308
+ for (const c of existingCapsules) {
1309
+ if (c && c.hub_asset_id) localHubAssetIds.add(String(c.hub_asset_id));
1310
+ }
1176
1311
  const localGeneIds = new Set(existingGenes.filter(function (g) { return g && g.id; }).map(function (g) { return g.id; }));
1177
1312
  const localCapsuleIds = new Set(existingCapsules.filter(function (c) { return c && c.id; }).map(function (c) { return c.id; }));
1178
1313
 
1179
1314
  let synced = 0;
1180
- let skipped = 0;
1315
+ let skippedAlreadySynced = 0;
1316
+ let skippedIdCollision = 0;
1181
1317
  let fetchErrors = 0;
1182
1318
 
1183
1319
  for (const asset of allAssets) {
@@ -1186,14 +1322,37 @@ async function main() {
1186
1322
  const localId = asset.local_id || assetId;
1187
1323
 
1188
1324
  if (assetType !== 'Gene' && assetType !== 'Capsule') {
1189
- skipped++;
1325
+ skippedAlreadySynced++;
1326
+ continue;
1327
+ }
1328
+
1329
+ // Already-synced check: same Hub asset_id is already in our local
1330
+ // store. Idempotent skip; safe to no-op even with --force because
1331
+ // re-fetching the same payload would only rewrite identical bytes.
1332
+ if (!force && localHubAssetIds.has(String(assetId))) {
1333
+ skippedAlreadySynced++;
1190
1334
  continue;
1191
1335
  }
1192
- if (assetType === 'Gene' && localGeneIds.has(localId)) { skipped++; continue; }
1193
- if (assetType === 'Capsule' && localCapsuleIds.has(localId)) { skipped++; continue; }
1336
+
1337
+ // Local-id collision: a local entry with the same user-facing id
1338
+ // already exists but has no hub_asset_id (e.g. bundled default seed
1339
+ // gene, or a hand-edited entry). Without --force we keep the
1340
+ // user-owned entry and warn so the user can decide.
1341
+ if (!force) {
1342
+ if (assetType === 'Gene' && localGeneIds.has(localId)) {
1343
+ if (isVerbose) console.warn(' [sync] Skipping ' + localId + ' (local id collision; pass --force to overwrite with Hub copy)');
1344
+ skippedIdCollision++;
1345
+ continue;
1346
+ }
1347
+ if (assetType === 'Capsule' && localCapsuleIds.has(localId)) {
1348
+ if (isVerbose) console.warn(' [sync] Skipping ' + localId + ' (local id collision; pass --force to overwrite with Hub copy)');
1349
+ skippedIdCollision++;
1350
+ continue;
1351
+ }
1352
+ }
1194
1353
 
1195
1354
  if (dryRun) {
1196
- console.log(' [dry-run] Would sync: ' + assetType + ' ' + assetId);
1355
+ console.log(' [dry-run] Would sync: ' + assetType + ' ' + assetId + (force ? ' (force)' : ''));
1197
1356
  synced++;
1198
1357
  continue;
1199
1358
  }
@@ -1230,6 +1389,7 @@ async function main() {
1230
1389
  };
1231
1390
  upsertGene(geneObj);
1232
1391
  localGeneIds.add(geneObj.id);
1392
+ localHubAssetIds.add(String(assetId));
1233
1393
  } else {
1234
1394
  const capsuleObj = {
1235
1395
  type: 'Capsule',
@@ -1244,6 +1404,7 @@ async function main() {
1244
1404
  };
1245
1405
  upsertCapsule(capsuleObj);
1246
1406
  localCapsuleIds.add(capsuleObj.id);
1407
+ localHubAssetIds.add(String(assetId));
1247
1408
  }
1248
1409
  synced++;
1249
1410
  } catch (fetchErr) {
@@ -1252,7 +1413,12 @@ async function main() {
1252
1413
  }
1253
1414
  }
1254
1415
 
1255
- console.log('[sync] Done. scope=' + scopeArg + ' synced=' + synced + ' skipped=' + skipped + ' errors=' + fetchErrors);
1416
+ const skippedTotal = skippedAlreadySynced + skippedIdCollision;
1417
+ console.log('[sync] Done. scope=' + scopeArg + ' synced=' + synced + ' skipped=' + skippedTotal + ' (already_synced=' + skippedAlreadySynced + ', id_collision=' + skippedIdCollision + ') errors=' + fetchErrors);
1418
+ if (skippedIdCollision > 0 && !force) {
1419
+ console.log('[sync] ' + skippedIdCollision + ' Hub asset(s) share a local id with an existing local entry that has no hub_asset_id.');
1420
+ console.log('[sync] Re-run with --force to overwrite those local entries with the Hub copies.');
1421
+ }
1256
1422
  if (dryRun) console.log('[sync] (dry-run mode: no files were modified)');
1257
1423
 
1258
1424
  if (listUnpublished && doPublished) {
@@ -1467,6 +1633,7 @@ async function main() {
1467
1633
  - --status=draft,promoted,all (only for published scope; default promoted+draft)
1468
1634
  - --export=<path.gepx> (also bundle local assets into a .gepx archive)
1469
1635
  - --no-unpublished-list (suppress local-only asset list)
1636
+ - --force (overwrite local entries that share an id with a Hub asset; bypasses default-seed dedup)
1470
1637
  - --dry-run (preview without writing to local store)
1471
1638
  - solidify flags:
1472
1639
  - --dry-run
@@ -1535,4 +1702,7 @@ module.exports = {
1535
1702
  readJsonSafe,
1536
1703
  rejectPendingRun,
1537
1704
  isPendingSolidify,
1705
+ parseBoolEnv,
1706
+ CycleTimeoutError,
1707
+ writeCycleProgressAtomic,
1538
1708
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver",
3
- "version": "1.78.9",
3
+ "version": "1.79.0",
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": {