@evomap/evolver 1.89.15 → 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 (64) hide show
  1. package/package.json +1 -1
  2. package/src/adapters/claudeCode.js +2 -2
  3. package/src/adapters/codex.js +2 -2
  4. package/src/adapters/hookAdapter.js +14 -2
  5. package/src/adapters/scripts/evolver-session-start.js +182 -2
  6. package/src/config.js +11 -3
  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 -1
  17. package/src/gep/antiAbuseTelemetry.js +1 -1
  18. package/src/gep/autoDistillConv.js +1 -1
  19. package/src/gep/autoDistillLlm.js +1 -1
  20. package/src/gep/candidateEval.js +1 -1
  21. package/src/gep/candidates.js +1 -1
  22. package/src/gep/contentHash.js +1 -1
  23. package/src/gep/conversationDistiller.js +1 -1
  24. package/src/gep/conversationSniffer.js +1 -1
  25. package/src/gep/crypto.js +1 -1
  26. package/src/gep/curriculum.js +1 -1
  27. package/src/gep/deviceId.js +1 -1
  28. package/src/gep/envFingerprint.js +1 -1
  29. package/src/gep/epigenetics.js +1 -1
  30. package/src/gep/execBridge.js +1 -1
  31. package/src/gep/explore.js +1 -1
  32. package/src/gep/hash.js +1 -1
  33. package/src/gep/hostErrorClassifier.js +34 -0
  34. package/src/gep/hubFetch.js +1 -1
  35. package/src/gep/hubReview.js +1 -1
  36. package/src/gep/hubSearch.js +1 -1
  37. package/src/gep/hubVerify.js +1 -1
  38. package/src/gep/issueReporter.js +5 -4
  39. package/src/gep/learningSignals.js +1 -1
  40. package/src/gep/memoryGraph.js +1 -1
  41. package/src/gep/memoryGraphAdapter.js +1 -1
  42. package/src/gep/mutation.js +1 -1
  43. package/src/gep/narrativeMemory.js +1 -1
  44. package/src/gep/openPRRegistry.js +1 -1
  45. package/src/gep/paths.js +20 -0
  46. package/src/gep/personality.js +1 -1
  47. package/src/gep/policyCheck.js +1 -1
  48. package/src/gep/prompt.js +1 -1
  49. package/src/gep/recallInject.js +1 -1
  50. package/src/gep/recallVerifier.js +1 -1
  51. package/src/gep/reflection.js +1 -1
  52. package/src/gep/savingsCore.js +1 -1
  53. package/src/gep/selector.js +1 -1
  54. package/src/gep/signals.js +37 -15
  55. package/src/gep/skillDistiller.js +1 -1
  56. package/src/gep/solidify.js +1 -1
  57. package/src/gep/strategy.js +1 -1
  58. package/src/gep/tokenSavings.js +1 -1
  59. package/src/gep/workspaceKeychain.js +1 -1
  60. package/src/ops/lifecycle.js +501 -31
  61. package/src/proxy/extensions/traceControl.js +1 -1
  62. package/src/proxy/inject.js +1 -1
  63. package/src/proxy/trace/extractor.js +1 -1
  64. package/src/proxy/trace/usage.js +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver",
3
- "version": "1.89.15",
3
+ "version": "1.89.17",
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": {
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { mergeJsonFile, copyHookScripts, appendSectionToFile, removeHookScripts, removeMarkedSection, assertSafeConfigDir } = require('./hookAdapter');
3
+ const { mergeJsonFile, copyHookScripts, appendSectionToFile, removeHookScripts, removeMarkedSection, assertSafeConfigDir, isEvolverHookCommand } = require('./hookAdapter');
4
4
 
5
5
  const HOOK_SCRIPTS_DIR_NAME = 'hooks';
6
6
  const EVOLVER_MARKER = '<!-- evolver-evolution-memory -->';
@@ -146,7 +146,7 @@ function uninstall({ configRoot }) {
146
146
  const innerBefore = matcher.hooks.length;
147
147
  const filtered = matcher.hooks.filter(h => {
148
148
  const cmd = (h && h.command) || '';
149
- return !cmd.includes('evolver-session') && !cmd.includes('evolver-signal') && !cmd.includes('evolver-task-recall');
149
+ return !isEvolverHookCommand(cmd);
150
150
  });
151
151
  // A matcher containing both evolver and user hooks shrinks
152
152
  // its inner array without changing the outer matcher count.
@@ -1,6 +1,6 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
- const { mergeJsonFile, copyHookScripts, appendSectionToFile, removeHookScripts, removeMarkedSection, assertSafeConfigDir } = require('./hookAdapter');
3
+ const { mergeJsonFile, copyHookScripts, appendSectionToFile, removeHookScripts, removeMarkedSection, assertSafeConfigDir, isEvolverHookCommand } = require('./hookAdapter');
4
4
 
5
5
  const HOOK_SCRIPTS_DIR_NAME = 'hooks';
6
6
  const EVOLVER_MARKER = '<!-- evolver-evolution-memory -->';
@@ -166,7 +166,7 @@ function uninstall({ configRoot }) {
166
166
  const before = data.hooks[event].length;
167
167
  data.hooks[event] = data.hooks[event].filter(h => {
168
168
  const cmd = (h && h.command) || '';
169
- return !cmd.includes('evolver-session') && !cmd.includes('evolver-signal');
169
+ return !isEvolverHookCommand(cmd);
170
170
  });
171
171
  if (data.hooks[event].length !== before) touched = true;
172
172
  if (data.hooks[event].length === 0) delete data.hooks[event];
@@ -76,7 +76,7 @@ function mergeWithHooksUnion(target, source) {
76
76
  if (tArr && sArr) {
77
77
  const isEvolverOwned = (entry) => {
78
78
  const cmds = collectCommands(entry);
79
- return cmds.some(c => c.includes('evolver-session') || c.includes('evolver-signal') || c.includes('evolver-task-recall'));
79
+ return cmds.some(isEvolverHookCommand);
80
80
  };
81
81
  const userEntries = tArr.filter(e => !isEvolverOwned(e));
82
82
  result.hooks[event] = [...userEntries, ...sArr];
@@ -101,6 +101,17 @@ function collectCommands(entry) {
101
101
  return out;
102
102
  }
103
103
 
104
+ function isEvolverHookCommand(command) {
105
+ if (typeof command !== 'string') return false;
106
+ return command.includes('evolver-session') ||
107
+ command.includes('evolver-signal') ||
108
+ command.includes('evolver-task-recall') ||
109
+ // Legacy installs briefly shipped this companion daemon hook. Treat it
110
+ // as evolver-owned so reinstall/merge can remove it instead of preserving
111
+ // a stale supervisor that may point clients at a dead proxy.
112
+ command.includes('evolver-daemon-start');
113
+ }
114
+
104
115
  function deepMerge(target, source) {
105
116
  const result = { ...target };
106
117
  for (const key of Object.keys(source)) {
@@ -226,7 +237,7 @@ function removeEvolverHooks(filePath, { markerKey = '_evolver_managed' } = {}) {
226
237
  const before = data.hooks[event].length;
227
238
  data.hooks[event] = data.hooks[event].filter(h => {
228
239
  const cmd = h.command || '';
229
- return !cmd.includes('evolver-session') && !cmd.includes('evolver-signal') && !cmd.includes('evolver-task-recall');
240
+ return !isEvolverHookCommand(cmd);
230
241
  });
231
242
  if (data.hooks[event].length !== before) changed = true;
232
243
  if (data.hooks[event].length === 0) delete data.hooks[event];
@@ -358,6 +369,7 @@ module.exports = {
358
369
  deepMerge,
359
370
  mergeWithHooksUnion,
360
371
  collectCommands,
372
+ isEvolverHookCommand,
361
373
  copyHookScripts,
362
374
  appendSectionToFile,
363
375
  assertSafeConfigDir,
@@ -14,6 +14,180 @@ const { filterRelevantOutcomes } = require('./_memoryFiltering');
14
14
  // _maybeRestartDaemon's catch-all and silently disable daemon auto-restart.
15
15
  const lockPaths = require('./_lockPaths');
16
16
 
17
+ function _readJson(file) {
18
+ try {
19
+ if (!file || !fs.existsSync(file)) return null;
20
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
21
+ } catch (_) {
22
+ return null;
23
+ }
24
+ }
25
+
26
+ function _isLoopbackProxyUrl(value) {
27
+ const raw = String(value || '').trim();
28
+ if (!raw) return false;
29
+ try {
30
+ const u = new URL(raw);
31
+ const host = u.hostname.toLowerCase();
32
+ return u.protocol === 'http:' && (host === '127.0.0.1' || host === 'localhost' || host === '::1' || host === '[::1]');
33
+ } catch (_) {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ function _stripTomlComment(line) {
39
+ let out = '';
40
+ let quote = null;
41
+ let escaped = false;
42
+ for (const ch of String(line || '')) {
43
+ if (escaped) {
44
+ out += ch;
45
+ escaped = false;
46
+ continue;
47
+ }
48
+ if (ch === '\\' && quote === '"') {
49
+ out += ch;
50
+ escaped = true;
51
+ continue;
52
+ }
53
+ if ((ch === '"' || ch === "'") && !quote) {
54
+ quote = ch;
55
+ out += ch;
56
+ continue;
57
+ }
58
+ if (ch === quote) {
59
+ quote = null;
60
+ out += ch;
61
+ continue;
62
+ }
63
+ if (ch === '#' && !quote) break;
64
+ out += ch;
65
+ }
66
+ return out.trim();
67
+ }
68
+
69
+ function _tomlStringValue(value) {
70
+ const raw = _stripTomlComment(value);
71
+ const match = raw.match(/^(['"])([\s\S]*)\1$/);
72
+ return match ? match[2] : raw.trim();
73
+ }
74
+
75
+ function _codexConfigPath() {
76
+ if (process.env.CODEX_CONFIG_FILE || process.env.EVOMAP_CODEX_CONFIG_FILE) {
77
+ return process.env.CODEX_CONFIG_FILE || process.env.EVOMAP_CODEX_CONFIG_FILE;
78
+ }
79
+ const home = process.env.HOME || os.homedir();
80
+ return home ? path.join(home, '.codex', 'config.toml') : null;
81
+ }
82
+
83
+ function _codexConfigExpectsProxy() {
84
+ const file = _codexConfigPath();
85
+ if (!file || !fs.existsSync(file)) return false;
86
+ let selectedProvider = null;
87
+ let section = '';
88
+ const providerUrls = {};
89
+ try {
90
+ const content = fs.readFileSync(file, 'utf8');
91
+ for (const line of content.split(/\r?\n/)) {
92
+ const clean = _stripTomlComment(line);
93
+ if (!clean) continue;
94
+ const sectionMatch = clean.match(/^\[([^\]]+)\]$/);
95
+ if (sectionMatch) {
96
+ section = sectionMatch[1].trim();
97
+ continue;
98
+ }
99
+ const kv = clean.match(/^([A-Za-z0-9_.-]+)\s*=\s*([\s\S]+)$/);
100
+ if (!kv) continue;
101
+ const key = kv[1].trim();
102
+ const value = _tomlStringValue(kv[2]);
103
+ if (!section && key === 'model_provider') {
104
+ selectedProvider = value;
105
+ continue;
106
+ }
107
+ const providerMatch = section.match(/^model_providers\.([A-Za-z0-9_.-]+)$/);
108
+ if (providerMatch && key === 'base_url') {
109
+ providerUrls[providerMatch[1]] = value;
110
+ continue;
111
+ }
112
+ if (!section && key === 'base_url' && _isLoopbackProxyUrl(value)) return true;
113
+ }
114
+ } catch {
115
+ return false;
116
+ }
117
+ if (selectedProvider && _isLoopbackProxyUrl(providerUrls[selectedProvider])) return true;
118
+ return Object.keys(providerUrls).some(name =>
119
+ /(?:evomap|proxy)/i.test(name) && _isLoopbackProxyUrl(providerUrls[name])
120
+ );
121
+ }
122
+
123
+ function _proxyExpected() {
124
+ if (String(process.env.EVOMAP_PROXY || '').trim() === '1') return true;
125
+ if (String(process.env.A2A_TRANSPORT || '').trim().toLowerCase() === 'mailbox') return true;
126
+ if (_isLoopbackProxyUrl(process.env.EVOMAP_PROXY_URL) || _isLoopbackProxyUrl(process.env.ANTHROPIC_BASE_URL)) return true;
127
+ if (_codexConfigExpectsProxy()) return true;
128
+
129
+ const home = process.env.HOME || os.homedir();
130
+ const settingsFile = process.env.CLAUDE_SETTINGS_FILE || process.env.EVOMAP_CLAUDE_SETTINGS_FILE ||
131
+ (home ? path.join(home, '.claude', 'settings.json') : null);
132
+ const settings = _readJson(settingsFile);
133
+ const cfg = settings && settings.env;
134
+ return !!(cfg && (
135
+ _isLoopbackProxyUrl(cfg.EVOMAP_PROXY_URL) ||
136
+ (String(cfg.EVOMAP_PROXY_AUTO_INJECTED || '') === '1' && _isLoopbackProxyUrl(cfg.ANTHROPIC_BASE_URL))
137
+ ));
138
+ }
139
+
140
+ function _proxyReachable(url, token) {
141
+ if (!_isLoopbackProxyUrl(url) || !token) return false;
142
+ try {
143
+ const { execFileSync } = require('child_process');
144
+ execFileSync(process.execPath, ['-e', `
145
+ const fs = require('fs');
146
+ const http = require('http');
147
+ const url = process.argv[1].replace(/\\/+$/, '') + '/proxy/status';
148
+ const token = fs.readFileSync(0, 'utf8').trim();
149
+ if (!token) process.exit(1);
150
+ const req = http.get(url, { headers: { Authorization: 'Bearer ' + token } }, (res) => {
151
+ let body = '';
152
+ res.setEncoding('utf8');
153
+ res.on('data', (chunk) => {
154
+ body += chunk;
155
+ if (body.length > 1024 * 1024) req.destroy(new Error('response too large'));
156
+ });
157
+ res.on('end', () => {
158
+ if (res.statusCode < 200 || res.statusCode >= 300) process.exit(1);
159
+ let parsed;
160
+ try { parsed = JSON.parse(body); } catch (_) { process.exit(1); }
161
+ if (parsed && parsed.status === 'running' && (parsed.proxy_protocol_version || parsed.schema_version || parsed.node_id != null)) {
162
+ process.exit(0);
163
+ }
164
+ process.exit(1);
165
+ });
166
+ });
167
+ req.setTimeout(700, () => req.destroy(new Error('timeout')));
168
+ req.on('error', () => process.exit(1));
169
+ `, url], { input: String(token), stdio: ['pipe', 'ignore', 'ignore'], timeout: 1200, windowsHide: true });
170
+ return true;
171
+ } catch (_) {
172
+ return false;
173
+ }
174
+ }
175
+
176
+ function _proxyHealthyIfExpected() {
177
+ if (!_proxyExpected()) return true;
178
+ const dir = process.env.EVOLVER_SETTINGS_DIR || path.join(os.homedir(), '.evolver');
179
+ const settings = _readJson(path.join(dir, 'settings.json'));
180
+ const proxy = settings && settings.proxy;
181
+ if (!proxy || !proxy.url) return false;
182
+ if (proxy.pid) {
183
+ try { process.kill(proxy.pid, 0); } catch (e) {
184
+ if (!(e && e.code === 'EPERM')) return false;
185
+ }
186
+ }
187
+ if (!proxy.token) return false;
188
+ return _proxyReachable(proxy.url, proxy.token);
189
+ }
190
+
17
191
  // Auto-restart guard: if the evolver daemon is not running when a new agent
18
192
  // session starts, attempt a background restart. This covers the "idle-death"
19
193
  // scenario: the user closed the machine (macOS sleep), the process died due to
@@ -69,7 +243,7 @@ function _maybeRestartDaemon(evolverRoot) {
69
243
  }
70
244
  } catch (_) { /* lock file unreadable or absent: assume not running */ }
71
245
 
72
- if (daemonRunning) return; // already alive, nothing to do
246
+ if (daemonRunning && _proxyHealthyIfExpected()) return; // already alive, nothing to do
73
247
 
74
248
  // Daemon appears dead. Spawn lifecycle.js start in the background so
75
249
  // this session-start script exits immediately (< 50 ms) and does not
@@ -305,5 +479,11 @@ function main() {
305
479
  if (require.main === module) {
306
480
  main();
307
481
  } else {
308
- module.exports = { belongsToWorkspace };
482
+ module.exports = {
483
+ belongsToWorkspace,
484
+ _isLoopbackProxyUrl,
485
+ _proxyExpected,
486
+ _proxyReachable,
487
+ _proxyHealthyIfExpected,
488
+ };
309
489
  }
package/src/config.js CHANGED
@@ -85,9 +85,17 @@ const HUB_SEARCH_TIMEOUT_MS = envPositiveInt('EVOLVER_HUB_SEARCH_TIMEOUT_MS', 80
85
85
  const PUBLIC_DEFAULT_HUB_URL = 'https://evomap.ai';
86
86
 
87
87
  function resolveHubUrl() {
88
- const raw = process.env.A2A_HUB_URL
89
- || process.env.EVOMAP_HUB_URL
90
- || process.env.EVOLVER_DEFAULT_HUB_URL
88
+ // Trim each candidate and fall through on empty/whitespace-only values.
89
+ // A trailing space in a `.env` value (common with quoted entries like
90
+ // A2A_HUB_URL="https://evomap.ai ") otherwise survives into endpoint
91
+ // construction: `hubUrl.replace(/\/+$/, '')` strips trailing slashes but
92
+ // NOT whitespace, producing "https://evomap.ai /a2a/events/poll" which
93
+ // fails URL validation (#580 Bug 1). `||` alone would also pick a
94
+ // whitespace-only override as "truthy", so trim-then-fall-through here.
95
+ const pick = (v) => { const t = (v == null ? '' : String(v)).trim(); return t || null; };
96
+ const raw = pick(process.env.A2A_HUB_URL)
97
+ || pick(process.env.EVOMAP_HUB_URL)
98
+ || pick(process.env.EVOLVER_DEFAULT_HUB_URL)
91
99
  || PUBLIC_DEFAULT_HUB_URL;
92
100
 
93
101
  if (process.env.EVOMAP_HUB_ALLOW_INSECURE !== '1') {