@evomap/evolver 1.89.15 → 1.89.18

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 (90) hide show
  1. package/README.ja-JP.md +1 -1
  2. package/README.ko-KR.md +1 -1
  3. package/README.md +1 -1
  4. package/README.zh-CN.md +1 -1
  5. package/index.js +89 -17
  6. package/package.json +1 -1
  7. package/src/adapters/claudeCode.js +38 -9
  8. package/src/adapters/codex.js +2 -2
  9. package/src/adapters/hookAdapter.js +38 -2
  10. package/src/adapters/scripts/evolver-session-start.js +182 -2
  11. package/src/atp/atpExecute.js +3 -1
  12. package/src/atp/hubClient.js +5 -3
  13. package/src/atp/serviceHelper.js +3 -2
  14. package/src/config.js +11 -3
  15. package/src/evolve/guards.js +1 -1
  16. package/src/evolve/pipeline/collect.js +1 -1
  17. package/src/evolve/pipeline/dispatch.js +1 -1
  18. package/src/evolve/pipeline/enrich.js +1 -1
  19. package/src/evolve/pipeline/hub.js +1 -1
  20. package/src/evolve/pipeline/select.js +1 -1
  21. package/src/evolve/pipeline/signals.js +1 -1
  22. package/src/evolve/utils.js +1 -1
  23. package/src/evolve.js +1 -1
  24. package/src/gep/a2aProtocol.js +1 -1
  25. package/src/gep/antiAbuseTelemetry.js +1 -1
  26. package/src/gep/autoDistillConv.js +1 -1
  27. package/src/gep/autoDistillLlm.js +1 -1
  28. package/src/gep/candidateEval.js +1 -1
  29. package/src/gep/candidates.js +1 -1
  30. package/src/gep/cliContracts.js +37 -1
  31. package/src/gep/contentHash.js +1 -1
  32. package/src/gep/conversationDistiller.js +1 -1
  33. package/src/gep/conversationSniffer.js +1 -1
  34. package/src/gep/crypto.js +1 -1
  35. package/src/gep/curriculum.js +1 -1
  36. package/src/gep/deviceId.js +1 -1
  37. package/src/gep/directoryClient.js +7 -3
  38. package/src/gep/envFingerprint.js +1 -1
  39. package/src/gep/epigenetics.js +1 -1
  40. package/src/gep/execBridge.js +1 -1
  41. package/src/gep/explore.js +1 -1
  42. package/src/gep/hash.js +1 -1
  43. package/src/gep/hostErrorClassifier.js +34 -0
  44. package/src/gep/hubFetch.js +1 -1
  45. package/src/gep/hubReview.js +1 -1
  46. package/src/gep/hubSearch.js +1 -1
  47. package/src/gep/hubVerify.js +1 -1
  48. package/src/gep/issueReporter.js +5 -4
  49. package/src/gep/learningSignals.js +1 -1
  50. package/src/gep/memoryGraph.js +1 -1
  51. package/src/gep/memoryGraphAdapter.js +1 -1
  52. package/src/gep/mutation.js +1 -1
  53. package/src/gep/narrativeMemory.js +1 -1
  54. package/src/gep/oauthLogin.js +3 -5
  55. package/src/gep/openPRRegistry.js +1 -1
  56. package/src/gep/paths.js +20 -0
  57. package/src/gep/personality.js +1 -1
  58. package/src/gep/policyCheck.js +1 -1
  59. package/src/gep/privacyClient.js +28 -10
  60. package/src/gep/prompt.js +1 -1
  61. package/src/gep/recallInject.js +1 -1
  62. package/src/gep/recallVerifier.js +1 -1
  63. package/src/gep/reflection.js +1 -1
  64. package/src/gep/savingsCore.js +1 -1
  65. package/src/gep/selector.js +1 -1
  66. package/src/gep/signals.js +37 -15
  67. package/src/gep/skillDistiller.js +1 -1
  68. package/src/gep/skillPublisher.js +8 -3
  69. package/src/gep/solidify.js +1 -1
  70. package/src/gep/strategy.js +1 -1
  71. package/src/gep/taskReceiver.js +3 -2
  72. package/src/gep/tokenSavings.js +1 -1
  73. package/src/gep/validator/index.js +7 -2
  74. package/src/gep/validator/reporter.js +7 -2
  75. package/src/gep/validator/stakeBootstrap.js +7 -2
  76. package/src/gep/workspaceKeychain.js +1 -1
  77. package/src/ops/lifecycle.js +501 -31
  78. package/src/proxy/clientSettings.js +405 -0
  79. package/src/proxy/extensions/traceControl.js +1 -1
  80. package/src/proxy/index.js +10 -5
  81. package/src/proxy/inject.js +1 -1
  82. package/src/proxy/lifecycle/manager.js +82 -16
  83. package/src/proxy/mailbox/state.js +207 -0
  84. package/src/proxy/mailbox/store.js +164 -64
  85. package/src/proxy/router/messages_route.js +4 -3
  86. package/src/proxy/server/http.js +40 -6
  87. package/src/proxy/sync/inbound.js +31 -12
  88. package/src/proxy/sync/outbound.js +157 -22
  89. package/src/proxy/trace/extractor.js +1 -1
  90. package/src/proxy/trace/usage.js +1 -1
package/README.ja-JP.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🧬 Evolver
2
2
 
3
- [![GitHub stars](https://img.shields.io/badge/Stars-8.7k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
3
+ [![GitHub stars](https://img.shields.io/badge/Stars-8.8k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
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
  [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
package/README.ko-KR.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🧬 Evolver
2
2
 
3
- [![GitHub stars](https://img.shields.io/badge/Stars-8.7k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
3
+ [![GitHub stars](https://img.shields.io/badge/Stars-8.8k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
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
  [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🧬 Evolver
2
2
 
3
- [![GitHub stars](https://img.shields.io/badge/Stars-8.7k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
3
+ [![GitHub stars](https://img.shields.io/badge/Stars-8.8k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
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
  [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
package/README.zh-CN.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # 🧬 Evolver
2
2
 
3
- [![GitHub stars](https://img.shields.io/badge/Stars-8.7k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
3
+ [![GitHub stars](https://img.shields.io/badge/Stars-8.8k-2b3137?logo=github&logoColor=white)](https://github.com/EvoMap/evolver/stargazers)
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
  [![npm downloads](https://img.shields.io/npm/dm/@evomap/evolver.svg)](https://www.npmjs.com/package/@evomap/evolver)
package/index.js CHANGED
@@ -464,6 +464,68 @@ function writeCycleProgressAtomic(progressPath, fields) {
464
464
  }
465
465
  }
466
466
 
467
+ function handleCycleTimeout({ error, cycleProgressPath, progressFields, suicideEnabled, args, logPath, spawnReplacementFn }) {
468
+ const msg = error && error.message ? String(error.message) : String(error);
469
+ console.error('[Daemon] ' + msg);
470
+
471
+ if (!suicideEnabled) {
472
+ console.warn('[Daemon] Cycle hard-timeout treated as non-fatal because EVOLVER_SUICIDE=false.');
473
+ writeCycleProgressAtomic(cycleProgressPath, Object.assign({}, progressFields, {
474
+ phase: 'cycle_timeout_nonfatal',
475
+ }));
476
+ return { action: 'continue' };
477
+ }
478
+
479
+ writeCycleProgressAtomic(cycleProgressPath, Object.assign({}, progressFields, {
480
+ phase: 'cycle_timeout_respawn',
481
+ }));
482
+ spawnReplacementFn({
483
+ reason: 'cycle_hard_timeout',
484
+ args: args,
485
+ logPath: logPath,
486
+ });
487
+ return { action: 'respawn' };
488
+ }
489
+
490
+ function logTimedOutEvolveRejection(error, logFn) {
491
+ const lateMsg = error && error.message ? String(error.message) : String(error);
492
+ try {
493
+ logFn('[Daemon] Timed-out evolve.run() eventually rejected: ' + lateMsg);
494
+ } catch (logError) {
495
+ const fallbackMsg = logError && logError.message ? String(logError.message) : String(logError);
496
+ console.error('[Daemon] Failed to log timed-out evolve.run() rejection: ' + fallbackMsg);
497
+ }
498
+ }
499
+
500
+ function observeTimedOutEvolvePromise(evolvePromise, logFn = console.error) {
501
+ if (!evolvePromise || typeof evolvePromise.then !== 'function') {
502
+ return { status: 'ignored' };
503
+ }
504
+
505
+ Promise.resolve(evolvePromise).then(
506
+ function () {},
507
+ function (error) {
508
+ logTimedOutEvolveRejection(error, logFn);
509
+ }
510
+ );
511
+
512
+ return { status: 'observing' };
513
+ }
514
+
515
+ async function waitForTimedOutEvolvePromise(evolvePromise, logFn = console.error) {
516
+ if (!evolvePromise || typeof evolvePromise.then !== 'function') {
517
+ return { status: 'ignored' };
518
+ }
519
+
520
+ try {
521
+ await evolvePromise;
522
+ return { status: 'resolved' };
523
+ } catch (error) {
524
+ logTimedOutEvolveRejection(error, logFn);
525
+ return { status: 'rejected' };
526
+ }
527
+ }
528
+
467
529
  function getLastSignals(statePath) {
468
530
  try {
469
531
  const st = readJsonSafe(statePath);
@@ -1357,6 +1419,7 @@ async function main() {
1357
1419
  const { startProxy } = require('./src/proxy');
1358
1420
  const proxyInfo = await startProxy({
1359
1421
  hubUrl: process.env.A2A_HUB_URL,
1422
+ clientSettings: {},
1360
1423
  });
1361
1424
  console.log('[Proxy] Started on ' + proxyInfo.url);
1362
1425
  try {
@@ -1592,13 +1655,12 @@ async function main() {
1592
1655
  if (typeof progressTicker.unref === 'function') progressTicker.unref();
1593
1656
  }
1594
1657
  let cycleTimeoutHandle = null;
1595
- let cycleTimedOut = false;
1658
+ let evolvePromise = null;
1596
1659
  try {
1597
- const evolvePromise = evolve.run();
1660
+ evolvePromise = evolve.run();
1598
1661
  if (cycleTimeoutEnabled && cycleTimeoutMs > 0) {
1599
1662
  const timeoutPromise = new Promise(function (_, reject) {
1600
1663
  cycleTimeoutHandle = setTimeout(function () {
1601
- cycleTimedOut = true;
1602
1664
  reject(new CycleTimeoutError(cycleTimeoutMs, 'evolve.run', cycleCount));
1603
1665
  }, cycleTimeoutMs);
1604
1666
  if (cycleTimeoutHandle && typeof cycleTimeoutHandle.unref === 'function') cycleTimeoutHandle.unref();
@@ -1621,25 +1683,32 @@ async function main() {
1621
1683
  } catch (error) {
1622
1684
  const msg = error && error.message ? String(error.message) : String(error);
1623
1685
  if (error && error.code === 'CYCLE_TIMEOUT') {
1624
- console.error('[Daemon] ' + msg);
1625
1686
  if (progressTicker) { clearInterval(progressTicker); progressTicker = null; }
1626
1687
  if (cycleTimeoutHandle) { clearTimeout(cycleTimeoutHandle); cycleTimeoutHandle = null; }
1627
- writeCycleProgressAtomic(cycleProgressPath, {
1628
- pid: process.pid,
1629
- outer_cycle: cycleCount,
1630
- inner_cycle: cycleCount,
1631
- started_at: t0,
1632
- phase: 'cycle_timeout_respawn',
1633
- });
1634
- spawnReplacementProcess({
1635
- reason: 'cycle_hard_timeout',
1636
- args: args,
1688
+ const timeoutAction = handleCycleTimeout({
1689
+ error,
1690
+ cycleProgressPath,
1691
+ progressFields: {
1692
+ pid: process.pid,
1693
+ outer_cycle: cycleCount,
1694
+ inner_cycle: cycleCount,
1695
+ started_at: t0,
1696
+ },
1697
+ suicideEnabled,
1698
+ args,
1637
1699
  logPath: getEvolverLogPath(),
1700
+ spawnReplacementFn: spawnReplacementProcess,
1638
1701
  });
1639
- releaseLock();
1640
- process.exit(1);
1702
+ if (timeoutAction.action === 'respawn') {
1703
+ releaseLock();
1704
+ process.exit(1);
1705
+ }
1706
+ if (timeoutAction.action === 'continue') {
1707
+ await waitForTimedOutEvolvePromise(evolvePromise);
1708
+ }
1709
+ } else {
1710
+ console.error(`Evolution cycle failed: ${msg}`);
1641
1711
  }
1642
- console.error(`Evolution cycle failed: ${msg}`);
1643
1712
  } finally {
1644
1713
  if (progressTicker) { clearInterval(progressTicker); progressTicker = null; }
1645
1714
  if (cycleTimeoutHandle) { clearTimeout(cycleTimeoutHandle); cycleTimeoutHandle = null; }
@@ -3328,5 +3397,8 @@ module.exports = {
3328
3397
  parseBoolEnv,
3329
3398
  CycleTimeoutError,
3330
3399
  writeCycleProgressAtomic,
3400
+ handleCycleTimeout,
3401
+ observeTimedOutEvolvePromise,
3402
+ waitForTimedOutEvolvePromise,
3331
3403
  spawnReplacementProcess,
3332
3404
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evomap/evolver",
3
- "version": "1.89.15",
3
+ "version": "1.89.18",
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,12 +1,41 @@
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 -->';
7
7
 
8
- function buildClaudeHooks(evolverRoot) {
9
- const scriptsBase = '.claude/hooks';
8
+ function shellSingleQuote(value) {
9
+ return `'${String(value).replace(/'/g, "'\\''")}'`;
10
+ }
11
+
12
+ function quoteEvalScript(script) {
13
+ if (process.platform === 'win32') {
14
+ if (script.includes('"')) {
15
+ throw new Error('unexpected double quote in generated hook wrapper');
16
+ }
17
+ return `"${script}"`;
18
+ }
19
+ return shellSingleQuote(script);
20
+ }
21
+
22
+ function buildSafeNodeHookCommand(scriptPath) {
23
+ const encodedPath = Buffer.from(String(scriptPath), 'utf8').toString('base64');
24
+ const js = [
25
+ "const {spawnSync}=require('child_process')",
26
+ `const p=Buffer.from('${encodedPath}','base64').toString('utf8')`,
27
+ "const r=spawnSync(process.execPath,[p],{stdio:'inherit',shell:false})",
28
+ "if(r.error){throw r.error}",
29
+ 'process.exit(r.status == null ? 1 : r.status)',
30
+ ].join(';');
31
+ return `node -e ${quoteEvalScript(js)} ${path.basename(scriptPath)}`;
32
+ }
33
+
34
+ function buildClaudeHooks(evolverRoot, configRoot) {
35
+ const scriptsBase = configRoot
36
+ ? path.join(configRoot, '.claude', 'hooks')
37
+ : path.join('.claude', 'hooks');
38
+ const hookCommand = (scriptName) => buildSafeNodeHookCommand(path.join(scriptsBase, scriptName));
10
39
  return {
11
40
  hooks: {
12
41
  SessionStart: [
@@ -14,7 +43,7 @@ function buildClaudeHooks(evolverRoot) {
14
43
  hooks: [
15
44
  {
16
45
  type: 'command',
17
- command: `node ${scriptsBase}/evolver-session-start.js`,
46
+ command: hookCommand('evolver-session-start.js'),
18
47
  timeout: 3,
19
48
  },
20
49
  ],
@@ -32,7 +61,7 @@ function buildClaudeHooks(evolverRoot) {
32
61
  // ABOVE that watchdog, so the host never kills the script
33
62
  // mid-write. A stuck/slow recall can never block or erase the
34
63
  // user's prompt.
35
- command: `node ${scriptsBase}/evolver-task-recall.js`,
64
+ command: hookCommand('evolver-task-recall.js'),
36
65
  timeout: 5,
37
66
  },
38
67
  ],
@@ -44,7 +73,7 @@ function buildClaudeHooks(evolverRoot) {
44
73
  hooks: [
45
74
  {
46
75
  type: 'command',
47
- command: `node ${scriptsBase}/evolver-signal-detect.js`,
76
+ command: hookCommand('evolver-signal-detect.js'),
48
77
  timeout: 2,
49
78
  },
50
79
  ],
@@ -55,7 +84,7 @@ function buildClaudeHooks(evolverRoot) {
55
84
  hooks: [
56
85
  {
57
86
  type: 'command',
58
- command: `node ${scriptsBase}/evolver-session-end.js`,
87
+ command: hookCommand('evolver-session-end.js'),
59
88
  timeout: 8,
60
89
  },
61
90
  ],
@@ -99,7 +128,7 @@ function install({ configRoot, evolverRoot, force }) {
99
128
 
100
129
  fs.mkdirSync(claudeDir, { recursive: true });
101
130
 
102
- const hooksCfg = buildClaudeHooks(evolverRoot);
131
+ const hooksCfg = buildClaudeHooks(evolverRoot, configRoot);
103
132
  mergeJsonFile(settingsPath, hooksCfg);
104
133
  console.log('[claude-code] Wrote ' + settingsPath);
105
134
 
@@ -146,7 +175,7 @@ function uninstall({ configRoot }) {
146
175
  const innerBefore = matcher.hooks.length;
147
176
  const filtered = matcher.hooks.filter(h => {
148
177
  const cmd = (h && h.command) || '';
149
- return !cmd.includes('evolver-session') && !cmd.includes('evolver-signal') && !cmd.includes('evolver-task-recall');
178
+ return !isEvolverHookCommand(cmd);
150
179
  });
151
180
  // A matcher containing both evolver and user hooks shrinks
152
181
  // 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];
@@ -10,7 +10,30 @@ const PLATFORMS = {
10
10
  opencode: { name: 'opencode', configDir: '.opencode', detector: '.opencode' },
11
11
  };
12
12
 
13
+ function detectPlatformFromEnv(env = process.env) {
14
+ const hasStrongClaudeSignal = env.CLAUDECODE || env.CLAUDE_CODE_ENTRYPOINT;
15
+ if (hasStrongClaudeSignal) {
16
+ return 'claude-code';
17
+ }
18
+
19
+ const hasCursorSignal =
20
+ env.CURSOR_TRACE_ID ||
21
+ env.CURSOR_SESSION_ID ||
22
+ env.CURSOR_PROJECT_DIR ||
23
+ String(env.TERM_PROGRAM || '').toLowerCase() === 'cursor';
24
+ if (hasCursorSignal) {
25
+ return 'cursor';
26
+ }
27
+ if (env.CLAUDE_PROJECT_DIR) {
28
+ return 'claude-code';
29
+ }
30
+ return null;
31
+ }
32
+
13
33
  function detectPlatform(cwd) {
34
+ const envPlatform = detectPlatformFromEnv();
35
+ if (envPlatform) return envPlatform;
36
+
14
37
  const root = cwd || process.cwd();
15
38
  const home = os.homedir();
16
39
  for (const [id, meta] of Object.entries(PLATFORMS)) {
@@ -76,7 +99,7 @@ function mergeWithHooksUnion(target, source) {
76
99
  if (tArr && sArr) {
77
100
  const isEvolverOwned = (entry) => {
78
101
  const cmds = collectCommands(entry);
79
- return cmds.some(c => c.includes('evolver-session') || c.includes('evolver-signal') || c.includes('evolver-task-recall'));
102
+ return cmds.some(isEvolverHookCommand);
80
103
  };
81
104
  const userEntries = tArr.filter(e => !isEvolverOwned(e));
82
105
  result.hooks[event] = [...userEntries, ...sArr];
@@ -101,6 +124,17 @@ function collectCommands(entry) {
101
124
  return out;
102
125
  }
103
126
 
127
+ function isEvolverHookCommand(command) {
128
+ if (typeof command !== 'string') return false;
129
+ return command.includes('evolver-session') ||
130
+ command.includes('evolver-signal') ||
131
+ command.includes('evolver-task-recall') ||
132
+ // Legacy installs briefly shipped this companion daemon hook. Treat it
133
+ // as evolver-owned so reinstall/merge can remove it instead of preserving
134
+ // a stale supervisor that may point clients at a dead proxy.
135
+ command.includes('evolver-daemon-start');
136
+ }
137
+
104
138
  function deepMerge(target, source) {
105
139
  const result = { ...target };
106
140
  for (const key of Object.keys(source)) {
@@ -226,7 +260,7 @@ function removeEvolverHooks(filePath, { markerKey = '_evolver_managed' } = {}) {
226
260
  const before = data.hooks[event].length;
227
261
  data.hooks[event] = data.hooks[event].filter(h => {
228
262
  const cmd = h.command || '';
229
- return !cmd.includes('evolver-session') && !cmd.includes('evolver-signal') && !cmd.includes('evolver-task-recall');
263
+ return !isEvolverHookCommand(cmd);
230
264
  });
231
265
  if (data.hooks[event].length !== before) changed = true;
232
266
  if (data.hooks[event].length === 0) delete data.hooks[event];
@@ -351,6 +385,7 @@ async function setupHooks({ platform, cwd, force, uninstall, evolverRoot } = {})
351
385
  }
352
386
 
353
387
  module.exports = {
388
+ detectPlatformFromEnv,
354
389
  detectPlatform,
355
390
  resolveConfigRoot,
356
391
  loadAdapter,
@@ -358,6 +393,7 @@ module.exports = {
358
393
  deepMerge,
359
394
  mergeWithHooksUnion,
360
395
  collectCommands,
396
+ isEvolverHookCommand,
361
397
  copyHookScripts,
362
398
  appendSectionToFile,
363
399
  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
  }
@@ -31,6 +31,7 @@ const {
31
31
  getHubUrl,
32
32
  getHubNodeSecret,
33
33
  buildHubHeaders,
34
+ buildNodeScopedHubHeaders,
34
35
  sendHelloToHub,
35
36
  } = require('../gep/a2aProtocol');
36
37
  const { submitDelivery } = require('./hubClient');
@@ -126,9 +127,10 @@ function _publishUrl() {
126
127
 
127
128
  async function _postJson(urlStr, body, timeoutMs) {
128
129
  const payload = JSON.stringify(body || {});
130
+ const buildHeaders = buildNodeScopedHubHeaders || buildHubHeaders;
129
131
  const headers = Object.assign(
130
132
  { 'Content-Type': 'application/json' },
131
- buildHubHeaders() || {},
133
+ buildHeaders() || {},
132
134
  );
133
135
 
134
136
  try {
@@ -15,7 +15,7 @@
15
15
  // bound to 127.0.0.1).
16
16
 
17
17
  const http = require('http');
18
- const { getHubUrl, buildHubHeaders, getNodeId } = require('../gep/a2aProtocol');
18
+ const { getHubUrl, buildHubHeaders, buildNodeScopedHubHeaders, getNodeId } = require('../gep/a2aProtocol');
19
19
  const { hubFetch } = require('../gep/hubFetch');
20
20
  const { getProxyUrl, getProxyToken } = require('../proxy/server/settings');
21
21
 
@@ -95,9 +95,10 @@ function _hubPost(pathSuffix, body, timeoutMs) {
95
95
  if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
96
96
  const endpoint = hubUrl.replace(/\/+$/, '') + pathSuffix;
97
97
  const timeout = timeoutMs || require('../config').HTTP_TRANSPORT_TIMEOUT_MS;
98
+ const buildHeaders = buildNodeScopedHubHeaders || buildHubHeaders;
98
99
  return hubFetch(endpoint, {
99
100
  method: 'POST',
100
- headers: buildHubHeaders(),
101
+ headers: buildHeaders(),
101
102
  body: JSON.stringify(body),
102
103
  signal: AbortSignal.timeout(timeout),
103
104
  })
@@ -123,9 +124,10 @@ function _hubGet(pathSuffix, timeoutMs) {
123
124
  if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
124
125
  const endpoint = hubUrl.replace(/\/+$/, '') + pathSuffix;
125
126
  const timeout = timeoutMs || require('../config').HTTP_TRANSPORT_TIMEOUT_MS;
127
+ const buildHeaders = buildNodeScopedHubHeaders || buildHubHeaders;
126
128
  return hubFetch(endpoint, {
127
129
  method: 'GET',
128
- headers: buildHubHeaders(),
130
+ headers: buildHeaders(),
129
131
  signal: AbortSignal.timeout(timeout),
130
132
  })
131
133
  .then(function (res) {
@@ -1,14 +1,15 @@
1
1
  // ATP Service Helper -- wraps marketplace service publishing for merchant agents.
2
2
 
3
- const { getNodeId, buildHubHeaders, getHubUrl } = require('../gep/a2aProtocol');
3
+ const { getNodeId, buildHubHeaders, buildNodeScopedHubHeaders, getHubUrl } = require('../gep/a2aProtocol');
4
4
  const { hubFetch } = require('../gep/hubFetch');
5
5
  const { HTTP_TRANSPORT_TIMEOUT_MS } = require('../config');
6
6
 
7
7
  async function postService(endpoint, body) {
8
8
  try {
9
+ const buildHeaders = buildNodeScopedHubHeaders || buildHubHeaders;
9
10
  const res = await hubFetch(endpoint, {
10
11
  method: 'POST',
11
- headers: Object.assign({ 'Content-Type': 'application/json' }, buildHubHeaders() || {}),
12
+ headers: Object.assign({ 'Content-Type': 'application/json' }, buildHeaders() || {}),
12
13
  body: JSON.stringify(body),
13
14
  signal: AbortSignal.timeout(HTTP_TRANSPORT_TIMEOUT_MS),
14
15
  });