@evomap/evolver 1.91.2 β†’ 1.92.1

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 (66) hide show
  1. package/README.md +13 -7
  2. package/index.js +150 -71
  3. package/package.json +2 -1
  4. package/src/adapters/scripts/_runtimePaths.js +10 -1
  5. package/src/adapters/scripts/evolver-session-end.js +10 -1
  6. package/src/canonicalIdentityLock.js +455 -0
  7. package/src/evolve/guards.js +1 -1
  8. package/src/evolve/pipeline/collect.js +1 -1
  9. package/src/evolve/pipeline/dispatch.js +1 -1
  10. package/src/evolve/pipeline/enrich.js +1 -1
  11. package/src/evolve/pipeline/hub.js +1 -1
  12. package/src/evolve/pipeline/select.js +1 -1
  13. package/src/evolve/pipeline/signals.js +1 -1
  14. package/src/evolve/utils.js +1 -1
  15. package/src/evolve.js +1 -1
  16. package/src/gep/a2aProtocol.js +1 -5175
  17. package/src/gep/antiAbuseTelemetry.js +1 -233
  18. package/src/gep/assetStore.js +56 -12
  19. package/src/gep/autoDistillConv.js +1 -205
  20. package/src/gep/autoDistillLlm.js +1 -315
  21. package/src/gep/candidateEval.js +1 -92
  22. package/src/gep/candidates.js +1 -1
  23. package/src/gep/contentHash.js +1 -30
  24. package/src/gep/conversationDistiller.js +1 -270
  25. package/src/gep/conversationSniffer.js +1 -266
  26. package/src/gep/crypto.js +1 -93
  27. package/src/gep/curriculum.js +1 -1
  28. package/src/gep/deviceId.js +1 -218
  29. package/src/gep/envFingerprint.js +1 -118
  30. package/src/gep/epigenetics.js +1 -31
  31. package/src/gep/execBridge.js +1 -712
  32. package/src/gep/explore.js +1 -289
  33. package/src/gep/hash.js +1 -15
  34. package/src/gep/hubFetch.js +1 -672
  35. package/src/gep/hubReview.js +1 -212
  36. package/src/gep/hubSearch.js +1 -544
  37. package/src/gep/hubVerify.js +1 -306
  38. package/src/gep/learningSignals.js +1 -1
  39. package/src/gep/memoryGraph.js +1 -1449
  40. package/src/gep/memoryGraphAdapter.js +1 -203
  41. package/src/gep/mutation.js +1 -1
  42. package/src/gep/narrativeMemory.js +1 -1
  43. package/src/gep/openPRRegistry.js +1 -205
  44. package/src/gep/personality.js +1 -1
  45. package/src/gep/policyCheck.js +1 -599
  46. package/src/gep/prompt.js +1 -1
  47. package/src/gep/recallInject.js +1 -409
  48. package/src/gep/recallVerifier.js +1 -318
  49. package/src/gep/reflection.js +1 -1
  50. package/src/gep/savingsCore.js +1 -1
  51. package/src/gep/schemas/gene.js +2 -0
  52. package/src/gep/selector.js +1 -1
  53. package/src/gep/skillDistiller.js +1 -1294
  54. package/src/gep/solidify.js +1 -1
  55. package/src/gep/strategy.js +1 -136
  56. package/src/gep/syncAsset.js +1 -0
  57. package/src/gep/tokenSavings.js +1 -1
  58. package/src/gep/trajectoryExport.js +1 -3841
  59. package/src/gep/workspaceKeychain.js +1 -174
  60. package/src/proxy/extensions/traceControl.js +1 -123
  61. package/src/proxy/index.js +136 -8
  62. package/src/proxy/inject.js +1 -52
  63. package/src/proxy/lifecycle/manager.js +279 -18
  64. package/src/proxy/mailbox/state.js +60 -29
  65. package/src/proxy/trace/extractor.js +1 -2355
  66. package/src/proxy/trace/usage.js +1 -105
package/README.md CHANGED
@@ -1,12 +1,18 @@
1
- # 🧬 Evolver
1
+ ![Evolver Cover](assets/cover.png)
2
2
 
3
- [![GitHub stars](https://img.shields.io/badge/Stars-8.9k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
4
- [![License: GPL-3.0](https://img.shields.io/badge/License-GPL--3.0-blue.svg)](https://opensource.org/licenses/GPL-3.0)
5
- [![Node.js >= 18](https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg)](https://nodejs.org/)
6
- [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
7
- [![arXiv](https://img.shields.io/badge/arXiv-2604.15097-b31b1b.svg)](https://arxiv.org/abs/2604.15097)
3
+ <h1 align="center">🧬 Evolver</h1>
8
4
 
9
- ![Evolver Cover](assets/cover.png)
5
+ <p align="center">
6
+ <a href="https://trendshift.io/repositories/26015?utm_source=repository-badge&amp;utm_medium=badge&amp;utm_campaign=badge-repository-26015" target="_blank" rel="noopener noreferrer"><img src="https://trendshift.io/api/badge/repositories/26015" alt="EvoMap%2Fevolver | Trendshift" width="250" height="55"/></a>
7
+ </p>
8
+
9
+ <p align="center">
10
+ <a href="https://github.com/EvoMap/evolver/stargazers"><img src="https://img.shields.io/badge/Stars-8.9k-2b3137?logo=github&amp;logoColor=white" alt="GitHub stars"/></a>
11
+ <a href="https://opensource.org/licenses/GPL-3.0"><img src="https://img.shields.io/badge/License-GPL--3.0-blue.svg" alt="License: GPL-3.0"/></a>
12
+ <a href="https://nodejs.org/"><img src="https://img.shields.io/badge/Node.js-%3E%3D%2018-green.svg" alt="Node.js &gt;= 18"/></a>
13
+ <a href="https://www.npmjs.com/package/@evomap/evolver"><img src="https://img.shields.io/npm/dm/@evomap/evolver.svg" alt="npm downloads"/></a>
14
+ <a href="https://arxiv.org/abs/2604.15097"><img src="https://img.shields.io/badge/arXiv-2604.15097-b31b1b.svg" alt="arXiv"/></a>
15
+ </p>
10
16
 
11
17
  **[evomap.ai](https://evomap.ai)** | [Documentation](https://evomap.ai/wiki) | [Chinese / δΈ­ζ–‡ζ–‡ζ‘£](README.zh-CN.md) | [Japanese / ζ—₯本θͺžγƒ‰γ‚­γƒ₯γƒ‘γƒ³γƒˆ](README.ja-JP.md) | [Korean / ν•œκ΅­μ–΄ λ¬Έμ„œ](README.ko-KR.md) | [GitHub](https://github.com/EvoMap/evolver) | [Releases](https://github.com/EvoMap/evolver/releases)
12
18
 
package/index.js CHANGED
@@ -1111,11 +1111,14 @@ async function main() {
1111
1111
  if (_keepAliveTimer) { clearInterval(_keepAliveTimer); _keepAliveTimer = null; }
1112
1112
  stopLockRefresh();
1113
1113
  releaseLock();
1114
+ // Stop event delivery independently because proxy mode owns its own
1115
+ // lifecycle heartbeat and never starts the a2a heartbeat. This clears
1116
+ // both managed SSE and the self-driving poll on every daemon path.
1117
+ try { require('./src/gep/a2aProtocol').stopEventDelivery(); } catch (e) {}
1114
1118
  // stopHeartbeat() clears the drift detector interval and the heartbeat
1115
1119
  // timer, preventing "ghost tick" log noise after exit and ensuring a
1116
1120
  // clean state if the process is somehow continued (test harness, etc.).
1117
1121
  try { require('./src/gep/a2aProtocol').stopHeartbeat(); } catch (e) {}
1118
- try { require('./src/gep/a2aProtocol').stopEventStream(); } catch (e) {}
1119
1122
  }
1120
1123
  process.on('exit', shutdown);
1121
1124
  process.on('SIGINT', () => { shutdown(); process.exit(); });
@@ -1450,6 +1453,29 @@ async function main() {
1450
1453
  clientSettings: {},
1451
1454
  });
1452
1455
  console.log('[Proxy] Started on ' + proxyInfo.url);
1456
+ const a2a = require('./src/gep/a2aProtocol');
1457
+ try {
1458
+ const lifecycle = proxyInfo && proxyInfo.proxy && proxyInfo.proxy.lifecycle;
1459
+ a2a.startEventDelivery({
1460
+ hubUrl: proxyInfo && proxyInfo.proxy && proxyInfo.proxy.hubUrl,
1461
+ nodeId: proxyInfo && proxyInfo.nodeId,
1462
+ // Proxy delivery uses SSE by default for real-time events. Operators
1463
+ // can explicitly disable the additional authenticated connection.
1464
+ enableSse: parseBoolEnv(process.env.EVOLVER_PROXY_SSE_ENABLED, true),
1465
+ identityProvider: lifecycle ? {
1466
+ getNodeId: function () { return lifecycle.nodeId; },
1467
+ getHeaders: function () { return lifecycle._buildHeaders(); },
1468
+ subscribe: function (listener) { return lifecycle.onDeliveryIdentityChange(listener); },
1469
+ } : null,
1470
+ onEventsAccepted: proxyInfo && proxyInfo.proxy
1471
+ ? function (events) { proxyInfo.proxy.acceptHubEvents(events); }
1472
+ : null,
1473
+ });
1474
+ }
1475
+ catch (deliveryErr) {
1476
+ console.warn('[Events] startEventDelivery failed: ' +
1477
+ (deliveryErr && deliveryErr.message || deliveryErr));
1478
+ }
1453
1479
  try {
1454
1480
  const { injectProxyEnv } = require('./src/proxy/inject');
1455
1481
  const injected = injectProxyEnv(proxyInfo);
@@ -1465,7 +1491,6 @@ async function main() {
1465
1491
  registerMailboxTransport();
1466
1492
  process.env.A2A_TRANSPORT = 'mailbox';
1467
1493
  try {
1468
- const a2a = require('./src/gep/a2aProtocol');
1469
1494
  a2a.startSystemdNotifyWatchdog(function () {
1470
1495
  try {
1471
1496
  const proxy = proxyInfo && proxyInfo.proxy;
@@ -2552,10 +2577,27 @@ async function main() {
2552
2577
  }
2553
2578
 
2554
2579
  } else if (command === 'sync') {
2555
- const { getHubUrl, getNodeId, buildHubHeaders, sendHelloToHub, getHubNodeSecret } = require('./src/gep/a2aProtocol');
2580
+ const {
2581
+ getHubUrl,
2582
+ getNodeId,
2583
+ buildHubHeaders,
2584
+ buildHubHeadersReadOnly,
2585
+ sendHelloToHub,
2586
+ getHubNodeSecret,
2587
+ getHubIdentityTupleReadOnly,
2588
+ } = require('./src/gep/a2aProtocol');
2556
2589
  const { hubFetch } = require('./src/gep/hubFetch');
2557
- const { upsertGene, upsertCapsule, loadGenes, loadCapsules } = require('./src/gep/assetStore');
2590
+ const {
2591
+ upsertSyncedGene,
2592
+ upsertCapsule,
2593
+ loadGenes,
2594
+ loadGenesReadOnly,
2595
+ loadCapsules,
2596
+ loadCapsulesReadOnly,
2597
+ } = require('./src/gep/assetStore');
2598
+ const { prepareSyncAsset } = require('./src/gep/syncAsset');
2558
2599
  const { getGepAssetsDir, getMemoryDir } = require('./src/gep/paths');
2600
+ const dryRun = args.includes('--dry-run');
2559
2601
 
2560
2602
  const hubUrl = getHubUrl();
2561
2603
  if (!hubUrl) {
@@ -2564,7 +2606,10 @@ async function main() {
2564
2606
  }
2565
2607
 
2566
2608
  try {
2567
- if (!getHubNodeSecret()) {
2609
+ const readOnlyIdentity = dryRun ? getHubIdentityTupleReadOnly() : null;
2610
+ const nodeSecret = dryRun ? readOnlyIdentity.secret : getHubNodeSecret();
2611
+ const nodeSecretVersion = dryRun ? readOnlyIdentity.version : null;
2612
+ if (!nodeSecret && !dryRun) {
2568
2613
  // Round-7 (Β§20.7): refuse a fresh hello if a live daemon owns
2569
2614
  // the lock; the daemon's secret will appear shortly.
2570
2615
  refuseHelloIfDaemonRunning('sync');
@@ -2577,7 +2622,21 @@ async function main() {
2577
2622
  console.log('[sync] Registered as ' + getNodeId());
2578
2623
  }
2579
2624
 
2580
- const nodeId = getNodeId();
2625
+ const hubHeaders = dryRun
2626
+ ? function () {
2627
+ const headers = buildHubHeadersReadOnly(nodeSecret, nodeSecretVersion);
2628
+ if (!headers.Authorization) {
2629
+ throw new Error('Dry-run requires an existing node_secret or a valid OAuth access token; refusing an unauthenticated Hub request.');
2630
+ }
2631
+ return headers;
2632
+ }
2633
+ : buildHubHeaders;
2634
+ if (dryRun) hubHeaders();
2635
+ const nodeId = dryRun ? readOnlyIdentity.nodeId : getNodeId();
2636
+ if (dryRun && !nodeId) {
2637
+ console.error('[sync] Dry-run requires an existing node_id; refusing to generate identity state.');
2638
+ process.exit(1);
2639
+ }
2581
2640
  const baseUrl = hubUrl.replace(/\/+$/, '');
2582
2641
  const typeFilter = (function () {
2583
2642
  const f = args.find(function (a) { return typeof a === 'string' && a.startsWith('--type='); });
@@ -2595,7 +2654,6 @@ async function main() {
2595
2654
  const f = args.find(function (a) { return typeof a === 'string' && a.startsWith('--export='); });
2596
2655
  return f ? f.slice('--export='.length) : null;
2597
2656
  })();
2598
- const dryRun = args.includes('--dry-run');
2599
2657
  const listUnpublished = !args.includes('--no-unpublished-list');
2600
2658
  const force = args.includes('--force');
2601
2659
  const limitPerPage = 100;
@@ -2624,7 +2682,7 @@ async function main() {
2624
2682
  }
2625
2683
  const resp = await hubFetch(url, {
2626
2684
  method: 'GET',
2627
- headers: buildHubHeaders(),
2685
+ headers: hubHeaders(),
2628
2686
  signal: AbortSignal.timeout(30000),
2629
2687
  });
2630
2688
  if (!resp.ok) {
@@ -2672,8 +2730,8 @@ async function main() {
2672
2730
  }
2673
2731
  }
2674
2732
 
2675
- const existingGenes = dryRun ? loadGenes({ seed: false }) : loadGenes();
2676
- const existingCapsules = loadCapsules();
2733
+ const existingGenes = dryRun ? loadGenesReadOnly() : loadGenes();
2734
+ const existingCapsules = dryRun ? loadCapsulesReadOnly() : loadCapsules();
2677
2735
  // Dedup by Hub asset_id is the only safe key. Local-facing `id` (e.g.
2678
2736
  // `gene_gep_repair_from_errors`) collides between bundled default seed
2679
2737
  // genes and identically-named assets that the user later published, so
@@ -2689,6 +2747,8 @@ async function main() {
2689
2747
  }
2690
2748
  const localGeneIds = new Set(existingGenes.filter(function (g) { return g && g.id; }).map(function (g) { return g.id; }));
2691
2749
  const localCapsuleIds = new Set(existingCapsules.filter(function (c) { return c && c.id; }).map(function (c) { return c.id; }));
2750
+ const batchGeneIds = new Map();
2751
+ const batchCapsuleIds = new Map();
2692
2752
 
2693
2753
  let synced = 0;
2694
2754
  let skippedAlreadySynced = 0;
@@ -2713,81 +2773,99 @@ async function main() {
2713
2773
  continue;
2714
2774
  }
2715
2775
 
2716
- // Local-id collision: a local entry with the same user-facing id
2717
- // already exists but has no hub_asset_id (e.g. bundled default seed
2718
- // gene, or a hand-edited entry). Without --force we keep the
2719
- // user-owned entry and warn so the user can decide.
2720
- if (!force) {
2721
- if (assetType === 'Gene' && localGeneIds.has(localId)) {
2722
- if (isVerbose) console.warn(' [sync] Skipping ' + localId + ' (local id collision; pass --force to overwrite with Hub copy)');
2723
- skippedIdCollision++;
2724
- continue;
2725
- }
2726
- if (assetType === 'Capsule' && localCapsuleIds.has(localId)) {
2727
- if (isVerbose) console.warn(' [sync] Skipping ' + localId + ' (local id collision; pass --force to overwrite with Hub copy)');
2728
- skippedIdCollision++;
2729
- continue;
2776
+ const localIds = assetType === 'Gene' ? localGeneIds : localCapsuleIds;
2777
+ // The list response is the only identity hint available until detail
2778
+ // fetch and payload validation succeed. If it already names a local
2779
+ // entry, fail closed on those preliminary failures: preserve the local
2780
+ // copy and let --force make the overwrite intent explicit.
2781
+ const hasListIdCollision = !force && localIds.has(localId);
2782
+ function skipUnverifiedListIdCollision(stage) {
2783
+ if (isVerbose) {
2784
+ console.warn(
2785
+ ' [sync] Skipping ' + JSON.stringify(String(localId)) +
2786
+ ' (local id collision; ' + stage +
2787
+ '; pass --force to overwrite with Hub copy)'
2788
+ );
2730
2789
  }
2790
+ skippedIdCollision++;
2731
2791
  }
2732
2792
 
2733
- if (dryRun) {
2734
- console.log(' [dry-run] Would sync: ' + assetType + ' ' + assetId + (force ? ' (force)' : ''));
2735
- synced++;
2736
- continue;
2737
- }
2738
-
2739
- try {
2740
- let payload = asset.payload;
2741
- if (!payload) {
2793
+ let payload = asset.payload;
2794
+ if (!payload) {
2795
+ try {
2742
2796
  const detailResp = await hubFetch(baseUrl + '/a2a/assets/' + encodeURIComponent(assetId) + '?detailed=true', {
2743
2797
  method: 'GET',
2744
- headers: buildHubHeaders(),
2798
+ headers: hubHeaders(),
2745
2799
  signal: AbortSignal.timeout(15000),
2746
2800
  });
2747
2801
  if (!detailResp.ok) {
2748
- if (isVerbose) console.warn(' [sync] Failed to fetch detail for ' + assetId + ' (HTTP ' + detailResp.status + ')');
2749
- fetchErrors++;
2750
- continue;
2802
+ throw new Error('detail HTTP ' + detailResp.status);
2751
2803
  }
2752
2804
  const detail = await detailResp.json();
2753
- payload = detail.payload || {};
2805
+ payload = detail && detail.payload;
2806
+ } catch (detailErr) {
2807
+ if (hasListIdCollision) {
2808
+ skipUnverifiedListIdCollision('could not fetch Hub detail');
2809
+ } else {
2810
+ if (isVerbose) console.warn(' [sync] Error fetching ' + assetId + ': ' + (detailErr && detailErr.message || detailErr));
2811
+ fetchErrors++;
2812
+ }
2813
+ continue;
2814
+ }
2815
+ }
2816
+
2817
+ let syncAsset;
2818
+ try {
2819
+ const syncedAt = new Date().toISOString();
2820
+ syncAsset = prepareSyncAsset({
2821
+ assetType,
2822
+ payload,
2823
+ assetId,
2824
+ localId,
2825
+ summary: asset.summary || '',
2826
+ syncedAt,
2827
+ });
2828
+ } catch (prepareErr) {
2829
+ if (hasListIdCollision) {
2830
+ skipUnverifiedListIdCollision('could not validate Hub payload');
2831
+ } else {
2832
+ if (isVerbose) console.warn(' [sync] Error fetching ' + assetId + ': ' + (prepareErr && prepareErr.message || prepareErr));
2833
+ fetchErrors++;
2834
+ }
2835
+ continue;
2836
+ }
2837
+
2838
+ try {
2839
+ const effectiveLocalId = syncAsset.id;
2840
+ const batchIds = assetType === 'Gene' ? batchGeneIds : batchCapsuleIds;
2841
+ const batchAssetId = batchIds.get(effectiveLocalId);
2842
+
2843
+ if (batchAssetId && batchAssetId !== String(assetId)) {
2844
+ throw new Error('duplicate local id ' + effectiveLocalId + ' in Hub batch (' + batchAssetId + ', ' + assetId + ')');
2845
+ }
2846
+ batchIds.set(effectiveLocalId, String(assetId));
2847
+
2848
+ // Local-id collision: a local entry with the same user-facing id
2849
+ // already exists but has no matching hub_asset_id. Without --force
2850
+ // we keep the local entry and report the collision.
2851
+ if (!force && localIds.has(effectiveLocalId)) {
2852
+ if (isVerbose) console.warn(' [sync] Skipping ' + effectiveLocalId + ' (local id collision; pass --force to overwrite with Hub copy)');
2853
+ skippedIdCollision++;
2854
+ continue;
2754
2855
  }
2755
2856
 
2756
- if (assetType === 'Gene') {
2757
- const geneObj = {
2758
- type: 'Gene',
2759
- id: payload.id || localId,
2760
- category: payload.category || 'unknown',
2761
- signals: Array.isArray(payload.signals) ? payload.signals : [],
2762
- strategy: Array.isArray(payload.strategy) ? payload.strategy : [],
2763
- avoid: Array.isArray(payload.avoid) ? payload.avoid : [],
2764
- validation: payload.validation || {},
2765
- summary: payload.summary || asset.summary || '',
2766
- hub_asset_id: assetId,
2767
- synced_at: new Date().toISOString(),
2768
- };
2769
- upsertGene(geneObj);
2770
- localGeneIds.add(geneObj.id);
2771
- localHubAssetIds.add(String(assetId));
2857
+ if (dryRun) {
2858
+ console.log(' [dry-run] Would sync: ' + assetType + ' ' + assetId + (force ? ' (force)' : ''));
2859
+ } else if (assetType === 'Gene') {
2860
+ upsertSyncedGene(syncAsset);
2772
2861
  } else {
2773
- const capsuleObj = {
2774
- type: 'Capsule',
2775
- id: payload.id || localId,
2776
- gene: payload.gene || null,
2777
- genes_used: Array.isArray(payload.genes_used) ? payload.genes_used : [],
2778
- outcome: payload.outcome || {},
2779
- execution_trace: payload.execution_trace || {},
2780
- summary: payload.summary || asset.summary || '',
2781
- hub_asset_id: assetId,
2782
- synced_at: new Date().toISOString(),
2783
- };
2784
- upsertCapsule(capsuleObj);
2785
- localCapsuleIds.add(capsuleObj.id);
2786
- localHubAssetIds.add(String(assetId));
2862
+ upsertCapsule(syncAsset);
2787
2863
  }
2864
+ localIds.add(effectiveLocalId);
2865
+ localHubAssetIds.add(String(assetId));
2788
2866
  synced++;
2789
- } catch (fetchErr) {
2790
- if (isVerbose) console.warn(' [sync] Error fetching ' + assetId + ': ' + (fetchErr && fetchErr.message || fetchErr));
2867
+ } catch (syncErr) {
2868
+ if (isVerbose) console.warn(' [sync] Error syncing ' + assetId + ': ' + (syncErr && syncErr.message || syncErr));
2791
2869
  fetchErrors++;
2792
2870
  }
2793
2871
  }
@@ -2798,7 +2876,7 @@ async function main() {
2798
2876
  console.log('[sync] ' + skippedIdCollision + ' Hub asset(s) share a local id with an existing local entry that has no hub_asset_id.');
2799
2877
  console.log('[sync] Re-run with --force to overwrite those local entries with the Hub copies.');
2800
2878
  }
2801
- if (dryRun) console.log('[sync] (dry-run mode: no files were modified)');
2879
+ if (dryRun) console.log('[sync] (dry-run mode: no asset-store or export files were modified)');
2802
2880
 
2803
2881
  if (listUnpublished && doPublished) {
2804
2882
  const hubGeneIds = new Set();
@@ -2849,6 +2927,7 @@ async function main() {
2849
2927
  }
2850
2928
  }
2851
2929
  }
2930
+ if (fetchErrors > 0) process.exitCode = 1;
2852
2931
  } catch (error) {
2853
2932
  if (error && error.name === 'TimeoutError') {
2854
2933
  console.error('[sync] Request timed out. Check your network and A2A_HUB_URL.');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver",
3
- "version": "1.91.2",
3
+ "version": "1.92.1",
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": {
@@ -41,6 +41,7 @@
41
41
  "@evomap/atp-sdk": "^0.1.0",
42
42
  "@evomap/gep-sdk": "^1.5.0",
43
43
  "dotenv": "^16.4.7",
44
+ "eventsource": "^4.1.0",
44
45
  "undici": "^7.28.0"
45
46
  },
46
47
  "devDependencies": {
@@ -402,6 +402,15 @@ function findMemoryGraph(evolverRoot) {
402
402
  return path.join(userDir, 'memory_graph.jsonl');
403
403
  }
404
404
 
405
+ function gitExecutable() {
406
+ if (process.platform !== 'win32') {
407
+ const xcodeGit = '/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git';
408
+ if (fs.existsSync(xcodeGit)) return xcodeGit;
409
+ if (fs.existsSync('/usr/bin/git')) return '/usr/bin/git';
410
+ }
411
+ return 'git';
412
+ }
413
+
405
414
  // Is `dir` inside a git work tree? Cheap, no-shell `git rev-parse`. Returns
406
415
  // false on any error (git missing, not a repo, timeout) and never throws.
407
416
  // The session-start hook uses this only to decide whether to surface a
@@ -409,7 +418,7 @@ function findMemoryGraph(evolverRoot) {
409
418
  // suppresses the notice rather than breaking anything.
410
419
  function isGitWorkspace(dir) {
411
420
  try {
412
- const res = spawnSync('git', ['rev-parse', '--is-inside-work-tree'], {
421
+ const res = spawnSync(gitExecutable(), ['rev-parse', '--is-inside-work-tree'], {
413
422
  cwd: dir,
414
423
  encoding: 'utf8',
415
424
  timeout: 5000,
@@ -24,12 +24,21 @@ const { findEvolverRoot, findMemoryGraph, resolveProjectDir, resolveWorkspaceId
24
24
  // consistent with the review-time reader in src/evolve/pipeline/collect.js
25
25
  // (Bugbot PR #109 round-1 MEDIUM; reader/writer drift flagged on PR #555).
26
26
 
27
+ function gitExecutable() {
28
+ if (process.platform !== 'win32') {
29
+ const xcodeGit = '/Applications/Xcode.app/Contents/Developer/usr/libexec/git-core/git';
30
+ if (fs.existsSync(xcodeGit)) return xcodeGit;
31
+ if (fs.existsSync('/usr/bin/git')) return '/usr/bin/git';
32
+ }
33
+ return 'git';
34
+ }
35
+
27
36
  function runGit(args, cwd) {
28
37
  // Argv-array form, no shell. Avoids POSIX `2>/dev/null` redirects that
29
38
  // break on Windows cmd.exe (#537). Failures (e.g. no HEAD~1 in a fresh
30
39
  // repo) are surfaced as a non-zero status; callers distinguish them
31
40
  // from successful empty output via the `ok` flag (PR #94 round-6 LOW).
32
- const res = spawnSync('git', args, {
41
+ const res = spawnSync(gitExecutable(), args, {
33
42
  cwd,
34
43
  encoding: 'utf8',
35
44
  timeout: 5000,