@evomap/evolver 1.89.14 → 1.89.17

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 (72) hide show
  1. package/index.js +219 -29
  2. package/package.json +1 -1
  3. package/src/adapters/claudeCode.js +2 -2
  4. package/src/adapters/codex.js +2 -2
  5. package/src/adapters/hookAdapter.js +14 -2
  6. package/src/adapters/scripts/evolver-session-start.js +182 -2
  7. package/src/config.js +11 -3
  8. package/src/evolve/guards.js +1 -1
  9. package/src/evolve/pipeline/collect.js +1 -1
  10. package/src/evolve/pipeline/dispatch.js +1 -1
  11. package/src/evolve/pipeline/enrich.js +1 -1
  12. package/src/evolve/pipeline/hub.js +1 -1
  13. package/src/evolve/pipeline/select.js +1 -1
  14. package/src/evolve/pipeline/signals.js +1 -1
  15. package/src/evolve/utils.js +1 -1
  16. package/src/evolve.js +1 -1
  17. package/src/forceUpdate.js +499 -119
  18. package/src/gep/a2aProtocol.js +1 -1
  19. package/src/gep/antiAbuseTelemetry.js +1 -1
  20. package/src/gep/autoDistillConv.js +1 -1
  21. package/src/gep/autoDistillLlm.js +1 -1
  22. package/src/gep/candidateEval.js +1 -1
  23. package/src/gep/candidates.js +1 -1
  24. package/src/gep/cliContracts.js +1154 -0
  25. package/src/gep/contentHash.js +1 -1
  26. package/src/gep/conversationDistiller.js +1 -1
  27. package/src/gep/conversationSniffer.js +1 -1
  28. package/src/gep/crypto.js +1 -1
  29. package/src/gep/curriculum.js +1 -1
  30. package/src/gep/deviceId.js +1 -1
  31. package/src/gep/envFingerprint.js +1 -1
  32. package/src/gep/epigenetics.js +1 -1
  33. package/src/gep/execBridge.js +1 -1
  34. package/src/gep/explore.js +1 -1
  35. package/src/gep/hash.js +1 -1
  36. package/src/gep/hostErrorClassifier.js +34 -0
  37. package/src/gep/hubFetch.js +1 -1
  38. package/src/gep/hubReview.js +1 -1
  39. package/src/gep/hubSearch.js +1 -1
  40. package/src/gep/hubVerify.js +1 -1
  41. package/src/gep/issueReporter.js +87 -0
  42. package/src/gep/learningSignals.js +1 -1
  43. package/src/gep/memoryGraph.js +1 -1
  44. package/src/gep/memoryGraphAdapter.js +1 -1
  45. package/src/gep/mutation.js +1 -1
  46. package/src/gep/narrativeMemory.js +1 -1
  47. package/src/gep/openPRRegistry.js +1 -1
  48. package/src/gep/paths.js +20 -0
  49. package/src/gep/personality.js +1 -1
  50. package/src/gep/policyCheck.js +1 -1
  51. package/src/gep/prompt.js +1 -1
  52. package/src/gep/recallInject.js +1 -1
  53. package/src/gep/recallVerifier.js +1 -1
  54. package/src/gep/reflection.js +1 -1
  55. package/src/gep/sanitize.js +20 -4
  56. package/src/gep/savingsCore.js +1 -1
  57. package/src/gep/selector.js +1 -1
  58. package/src/gep/signals.js +70 -24
  59. package/src/gep/skillDistiller.js +1 -1
  60. package/src/gep/solidify.js +1 -1
  61. package/src/gep/strategy.js +1 -1
  62. package/src/gep/tokenSavings.js +1 -1
  63. package/src/gep/workspaceKeychain.js +1 -1
  64. package/src/ops/lifecycle.js +501 -31
  65. package/src/proxy/extensions/traceControl.js +1 -1
  66. package/src/proxy/index.js +4 -4
  67. package/src/proxy/inject.js +1 -1
  68. package/src/proxy/lifecycle/manager.js +233 -33
  69. package/src/proxy/sync/inbound.js +5 -4
  70. package/src/proxy/sync/outbound.js +3 -2
  71. package/src/proxy/trace/extractor.js +1 -1
  72. package/src/proxy/trace/usage.js +1 -1
@@ -1,3 +1,5 @@
1
+ var { isHostClientError } = require('./hostErrorClassifier');
2
+
1
3
  // Opportunity signal names (shared with mutation.js and personality.js).
2
4
  var OPPORTUNITY_SIGNALS = [
3
5
  'user_feature_request',
@@ -32,6 +34,22 @@ function hasOpportunitySignal(signals) {
32
34
  return false;
33
35
  }
34
36
 
37
+ // A cycle that produced no file/line changes is a no-op ("empty cycle"), not a
38
+ // genuine failure -- even when its outcome is recorded as `failed`. The most
39
+ // common way a `failed` event ends up with zero blast radius is a claimed-
40
+ // success cycle rejected by the anti-forgery guard (`zero_blast_radius_with_
41
+ // success`): the sub-agent declared success but touched nothing -- e.g. an
42
+ // `innovate` gene that only produces metadata/strategy and never edits code.
43
+ // Empty cycles are tracked separately (consecutiveEmptyCycles) and drive
44
+ // graceful steady-state degradation, NOT the failure-loop ban. Single source of
45
+ // truth so the empty/failure split stays consistent across every counter below.
46
+ function _isEmptyCycle(evt) {
47
+ if (!evt) return false;
48
+ if (evt.meta && evt.meta.empty_cycle) return true;
49
+ var br = evt.blast_radius;
50
+ return !!(br && br.files === 0 && br.lines === 0);
51
+ }
52
+
35
53
  // Build a de-duplication set from recent evolution events.
36
54
  // Returns an object: { suppressedSignals: Set<string>, recentIntents: string[], consecutiveRepairCount: number }
37
55
  function analyzeRecentHistory(recentEvents) {
@@ -85,13 +103,11 @@ function analyzeRecentHistory(recentEvents) {
85
103
 
86
104
  var recentIntents = recent.map(function(e) { return e.intent || 'unknown'; });
87
105
 
88
- // Count empty cycles (blast_radius.files === 0) in last 8 events.
106
+ // Count empty cycles (no file/line changes) in last 8 events.
89
107
  // High ratio indicates the evolver is spinning without producing real changes.
90
108
  var emptyCycleCount = 0;
91
109
  for (var ec = 0; ec < tail.length; ec++) {
92
- var br = tail[ec].blast_radius;
93
- var em = tail[ec].meta && tail[ec].meta.empty_cycle;
94
- if (em || (br && br.files === 0 && br.lines === 0)) {
110
+ if (_isEmptyCycle(tail[ec])) {
95
111
  emptyCycleCount++;
96
112
  }
97
113
  }
@@ -101,19 +117,25 @@ function analyzeRecentHistory(recentEvents) {
101
117
  // zero-change cycles. Used to trigger graceful degradation to steady-state mode.
102
118
  var consecutiveEmptyCycles = 0;
103
119
  for (var se = recent.length - 1; se >= 0; se--) {
104
- var seBr = recent[se].blast_radius;
105
- var seEm = recent[se].meta && recent[se].meta.empty_cycle;
106
- if (seEm || (seBr && seBr.files === 0 && seBr.lines === 0)) {
120
+ if (_isEmptyCycle(recent[se])) {
107
121
  consecutiveEmptyCycles++;
108
122
  } else {
109
123
  break;
110
124
  }
111
125
  }
112
126
 
113
- // Count consecutive failures at the tail of recent events.
127
+ // Count consecutive *productive* failures at the tail of recent events.
114
128
  // This tells the evolver "you have been failing N times in a row -- slow down."
129
+ //
130
+ // A no-op (zero-blast) cycle breaks the streak instead of extending it: it is
131
+ // already counted as an empty cycle above, which drives graceful steady-state
132
+ // degradation. Without this guard a gene that keeps producing empty cycles --
133
+ // or a sub-agent whose claimed-success cycles are anti-forgery-rejected to
134
+ // `failed` with zero blast radius -- would double-trigger failure_loop_detected
135
+ // + ban_gene and file a spurious recurring-failure GitHub issue (see #577).
115
136
  var consecutiveFailureCount = 0;
116
137
  for (var cf = recent.length - 1; cf >= 0; cf--) {
138
+ if (_isEmptyCycle(recent[cf])) break;
117
139
  var outcome = recent[cf].outcome;
118
140
  if (outcome && outcome.status === 'failed') {
119
141
  consecutiveFailureCount++;
@@ -122,9 +144,12 @@ function analyzeRecentHistory(recentEvents) {
122
144
  }
123
145
  }
124
146
 
125
- // Count total failures in last 8 events (failure ratio).
147
+ // Count total *productive* failures in last 8 events (failure ratio).
148
+ // Empty (zero-blast) cycles are excluded for the same reason as the
149
+ // consecutive-failure streak above -- a no-op is saturation, not a failure.
126
150
  var recentFailureCount = 0;
127
151
  for (var rf = 0; rf < tail.length; rf++) {
152
+ if (_isEmptyCycle(tail[rf])) continue;
128
153
  var rfOut = tail[rf].outcome;
129
154
  if (rfOut && rfOut.status === 'failed') recentFailureCount++;
130
155
  }
@@ -595,6 +620,12 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
595
620
 
596
621
  var history = analyzeRecentHistory(recentEvents || []);
597
622
 
623
+ // Is the recent failure corpus dominated by an unrecoverable host/LLM client
624
+ // error (4xx: malformed request / auth / quota)? Such failures are the host
625
+ // Agent's provider config, not the Gene -- they must not feed the
626
+ // failure-loop streak / ban_gene path below (#571, follow-up to #534).
627
+ var hostClientError = isHostClientError(corpus);
628
+
598
629
  var errorHit = /\[error\]|error:|exception:|iserror":true|"status":\s*"error"|"status":\s*"failed"|错误\s*[::]|异常\s*[::]|报错\s*[::]|失败\s*[::]/.test(lower);
599
630
 
600
631
  // Layer 1: Regex (deterministic, 0ms)
@@ -673,28 +704,42 @@ function extractSignals({ recentSessionTranscript, todayLog, memorySnippet, user
673
704
 
674
705
  // Failure streak awareness
675
706
  if (history.consecutiveFailureCount >= 3) {
676
- signals.push('consecutive_failure_streak_' + history.consecutiveFailureCount);
677
- if (history.consecutiveFailureCount >= 5) {
678
- signals.push('failure_loop_detected');
679
- var topGene = null;
680
- var topGeneCount = 0;
681
- var gfEntries = Object.entries(history.geneFreq);
682
- for (var gfi = 0; gfi < gfEntries.length; gfi++) {
683
- if (gfEntries[gfi][1] > topGeneCount) {
684
- topGeneCount = gfEntries[gfi][1];
685
- topGene = gfEntries[gfi][0];
707
+ if (hostClientError) {
708
+ // Non-Gene-attributable: an unrecoverable host/LLM 4xx is driving the
709
+ // streak. Surface an actionable signal pointing at the host LLM config
710
+ // and skip the streak/ban path so we never ban an innocent Gene or trip
711
+ // failure_loop_detected (#571).
712
+ if (!signals.includes('host_llm_client_error')) signals.push('host_llm_client_error');
713
+ } else {
714
+ signals.push('consecutive_failure_streak_' + history.consecutiveFailureCount);
715
+ if (history.consecutiveFailureCount >= 5) {
716
+ signals.push('failure_loop_detected');
717
+ var topGene = null;
718
+ var topGeneCount = 0;
719
+ var gfEntries = Object.entries(history.geneFreq);
720
+ for (var gfi = 0; gfi < gfEntries.length; gfi++) {
721
+ if (gfEntries[gfi][1] > topGeneCount) {
722
+ topGeneCount = gfEntries[gfi][1];
723
+ topGene = gfEntries[gfi][0];
724
+ }
725
+ }
726
+ if (topGene) {
727
+ signals.push('ban_gene:' + topGene);
686
728
  }
687
- }
688
- if (topGene) {
689
- signals.push('ban_gene:' + topGene);
690
729
  }
691
730
  }
692
731
  }
693
732
 
694
733
  // High failure ratio in recent history (>= 75% failed in last 8 cycles)
695
734
  if (history.recentFailureRatio >= 0.75) {
696
- signals.push('high_failure_ratio');
697
- signals.push('force_innovation_after_repair_loop');
735
+ if (hostClientError) {
736
+ // Same rationale as the streak block: a host/LLM 4xx is not the Gene's
737
+ // fault, so do not force innovation away from it (#571).
738
+ if (!signals.includes('host_llm_client_error')) signals.push('host_llm_client_error');
739
+ } else {
740
+ signals.push('high_failure_ratio');
741
+ signals.push('force_innovation_after_repair_loop');
742
+ }
698
743
  }
699
744
 
700
745
  // Plateau detection: recent scores trending down or stagnant.
@@ -727,4 +772,5 @@ module.exports = {
727
772
  OPPORTUNITY_SIGNALS, SIGNAL_PROFILES,
728
773
  _extractRegex, _extractKeywordScore, _extractLLM, _mergeSignals,
729
774
  _curlHubIpFamilyArgs, _curlHubIpFamilyAttempts, _curlAuthConfig,
775
+ _isEmptyCycle,
730
776
  };