@evomap/evolver 1.89.2 → 1.89.3
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.
- package/README.ja-JP.md +1 -3
- package/README.ko-KR.md +1 -3
- package/README.md +1 -3
- package/README.zh-CN.md +1 -3
- package/assets/gep/genes.seed.json +251 -0
- package/index.js +14 -47
- package/package.json +1 -1
- package/scripts/refresh_stars_badge.js +168 -0
- package/src/adapters/hookAdapter.js +2 -0
- package/src/adapters/scripts/_lockPaths.js +74 -0
- package/src/adapters/scripts/evolver-session-start.js +19 -27
- package/src/evolve/guards.js +1 -1
- package/src/evolve/pipeline/collect.js +1 -1
- package/src/evolve/pipeline/dispatch.js +1 -1
- package/src/evolve/pipeline/enrich.js +1 -1
- package/src/evolve/pipeline/hub.js +1 -1
- package/src/evolve/pipeline/select.js +1 -1
- package/src/evolve/pipeline/signals.js +1 -1
- package/src/evolve/utils.js +1 -1
- package/src/evolve.js +1 -1
- package/src/forceUpdate.js +200 -7
- package/src/gep/a2aProtocol.js +1 -1
- package/src/gep/autoDistillConv.js +1 -1
- package/src/gep/autoDistillLlm.js +1 -1
- package/src/gep/candidateEval.js +1 -1
- package/src/gep/candidates.js +1 -1
- package/src/gep/contentHash.js +1 -1
- package/src/gep/conversationSniffer.js +1 -1
- package/src/gep/crypto.js +1 -1
- package/src/gep/curriculum.js +1 -1
- package/src/gep/deviceId.js +1 -1
- package/src/gep/envFingerprint.js +1 -1
- package/src/gep/epigenetics.js +1 -1
- package/src/gep/execBridge.js +1 -1
- package/src/gep/explore.js +1 -1
- package/src/gep/hash.js +1 -1
- package/src/gep/hubFetch.js +1 -1
- package/src/gep/hubReview.js +1 -1
- package/src/gep/hubSearch.js +1 -1
- package/src/gep/hubVerify.js +1 -1
- package/src/gep/learningSignals.js +1 -1
- package/src/gep/memoryGraph.js +1 -1
- package/src/gep/memoryGraphAdapter.js +1 -1
- package/src/gep/mutation.js +1 -1
- package/src/gep/narrativeMemory.js +1 -1
- package/src/gep/openPRRegistry.js +1 -1
- package/src/gep/personality.js +1 -1
- package/src/gep/policyCheck.js +1 -1
- package/src/gep/prompt.js +1 -1
- package/src/gep/recallInject.js +1 -1
- package/src/gep/recallVerifier.js +1 -1
- package/src/gep/reflection.js +1 -1
- package/src/gep/selector.js +1 -1
- package/src/gep/skillDistiller.js +1 -1
- package/src/gep/solidify.js +1 -1
- package/src/gep/strategy.js +1 -1
- package/src/gep/tokenSavings.js +1 -1
- package/src/gep/workspaceKeychain.js +1 -1
- package/src/proxy/extensions/traceControl.js +1 -1
- package/src/proxy/index.js +4 -4
- package/src/proxy/inject.js +1 -1
- package/src/proxy/lifecycle/manager.js +11 -0
- package/src/proxy/router/messages_route.js +8 -0
- package/src/proxy/trace/extractor.js +1 -1
- package/src/proxy/trace/usage.js +1 -1
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
// _lockPaths.js
|
|
2
|
+
// Single source of truth for the daemon singleton-lock location and lease
|
|
3
|
+
// tunables, shared by the daemon (index.js) and the session-start hook's
|
|
4
|
+
// auto-restart guard (evolver-session-start.js).
|
|
5
|
+
//
|
|
6
|
+
// Issue #176: the hook used to replicate this logic inline ("keep index.js
|
|
7
|
+
// out of the hook's require graph"), which could silently diverge whenever
|
|
8
|
+
// the daemon's lock resolution changed. This module keeps that property —
|
|
9
|
+
// it depends only on fs/os/path — while making divergence structurally
|
|
10
|
+
// impossible.
|
|
11
|
+
//
|
|
12
|
+
// Deployed-layout constraint (PR #163): hook scripts are COPIED into the
|
|
13
|
+
// host's hooks dir (e.g. `.claude/hooks/`) and run from there, so every
|
|
14
|
+
// same-dir helper they require must be a sibling file listed in
|
|
15
|
+
// hookAdapter.js's copy/remove lists (enforced by test/adapters.test.js).
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
// Round-4 (see the history note above index.js's daemon-lock block): the
|
|
22
|
+
// pidfile previously defaulted to __dirname, which differs per install mode
|
|
23
|
+
// (global install vs dev clone vs npx cache), so two daemons launched under
|
|
24
|
+
// different install modes never saw each other's lock. Default now lives
|
|
25
|
+
// under the per-user state dir so all install modes converge.
|
|
26
|
+
// EVOLVER_LOCK_DIR still overrides for tests / sandboxed runs — note the
|
|
27
|
+
// basename differs from the default in that case (`evolver.pid` vs
|
|
28
|
+
// `instance.lock`).
|
|
29
|
+
function getLockFilePath(env) {
|
|
30
|
+
const e = env || process.env;
|
|
31
|
+
if (e.EVOLVER_LOCK_DIR) {
|
|
32
|
+
return path.join(e.EVOLVER_LOCK_DIR, 'evolver.pid');
|
|
33
|
+
}
|
|
34
|
+
// os.homedir() is cross-platform; process.env.HOME is unset on Windows.
|
|
35
|
+
return path.join(os.homedir(), '.evomap', 'instance.lock');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Round-9: lease tunables for the daemon lock. A live daemon refreshes the
|
|
39
|
+
// lock mtime every LOCK_REFRESH_MS; a lock whose mtime is older than
|
|
40
|
+
// STALE_LOCK_TTL_MS (and that was written by a lease-aware daemon) is
|
|
41
|
+
// treated as stale even if its PID happens to be alive — closing the
|
|
42
|
+
// "crash + PID reuse -> new daemon silently refuses to start" hole and the
|
|
43
|
+
// "SIGKILL leaves a stale lock nobody reclaims" hole. The TTL is well above
|
|
44
|
+
// the heartbeat interval (default 6min) so a healthy daemon never trips it.
|
|
45
|
+
// On Windows, SIGTERM is implemented as TerminateProcess() (not a catchable
|
|
46
|
+
// signal), so the daemon's releaseLock() never runs and the lock file stays
|
|
47
|
+
// on disk with the dead PID — hence the shorter Windows TTL, with a 1-min
|
|
48
|
+
// refresh (3x margin against transient FS hiccups). Unix: 5 min TTL is
|
|
49
|
+
// 2.5x the 2-min refresh cadence.
|
|
50
|
+
const STALE_LOCK_TTL_MS = process.platform === 'win32' ? 3 * 60_000 : 5 * 60_000;
|
|
51
|
+
const LOCK_REFRESH_MS = process.platform === 'win32' ? 1 * 60_000 : 2 * 60_000;
|
|
52
|
+
|
|
53
|
+
// Returns true if the lock was written by a lease-aware daemon AND its
|
|
54
|
+
// mtime is older than the stale TTL — i.e. no live owner is refreshing it,
|
|
55
|
+
// so it is safe to treat the recorded PID as dead regardless of whether
|
|
56
|
+
// process.kill(pid, 0) resolves (the PID may have been reused). Locks
|
|
57
|
+
// written by pre-lease daemons (payload.lease !== true) are never judged
|
|
58
|
+
// stale by mtime, so we never falsely steal an older daemon's lock.
|
|
59
|
+
function lockIsStaleByLease(lockFile, payload) {
|
|
60
|
+
if (!payload || payload.lease !== true) return false;
|
|
61
|
+
try {
|
|
62
|
+
const ageMs = Date.now() - fs.statSync(lockFile).mtimeMs;
|
|
63
|
+
return ageMs > STALE_LOCK_TTL_MS;
|
|
64
|
+
} catch (_) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = {
|
|
70
|
+
getLockFilePath,
|
|
71
|
+
lockIsStaleByLease,
|
|
72
|
+
STALE_LOCK_TTL_MS,
|
|
73
|
+
LOCK_REFRESH_MS,
|
|
74
|
+
};
|
|
@@ -9,6 +9,10 @@ const os = require('os');
|
|
|
9
9
|
|
|
10
10
|
const { findEvolverRoot, findMemoryGraph, resolveProjectDir, resolveWorkspaceId, isGitWorkspace } = require('./_runtimePaths');
|
|
11
11
|
const { filterRelevantOutcomes } = require('./_memoryFiltering');
|
|
12
|
+
// Top-level on purpose: a missing sibling helper in the deployed hooks dir
|
|
13
|
+
// must fail LOUD at load time (the #547 failure mode), not vanish inside
|
|
14
|
+
// _maybeRestartDaemon's catch-all and silently disable daemon auto-restart.
|
|
15
|
+
const lockPaths = require('./_lockPaths');
|
|
12
16
|
|
|
13
17
|
// Auto-restart guard: if the evolver daemon is not running when a new agent
|
|
14
18
|
// session starts, attempt a background restart. This covers the "idle-death"
|
|
@@ -35,43 +39,31 @@ function _maybeRestartDaemon(evolverRoot) {
|
|
|
35
39
|
if (!lifecyclePath || !fs.existsSync(lifecyclePath)) return;
|
|
36
40
|
|
|
37
41
|
// Check if daemon is running by looking for the PID file / lock file.
|
|
38
|
-
//
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
var lockFile = process.env.EVOLVER_LOCK_DIR
|
|
45
|
-
? path.join(process.env.EVOLVER_LOCK_DIR, 'evolver.pid')
|
|
46
|
-
: path.join(os.homedir(), '.evomap', 'instance.lock');
|
|
47
|
-
// R1: PID-reuse defense. process.kill(pid, 0) only proves SOME process
|
|
48
|
-
// owns that PID -- after macOS sleep / OOM, the kernel may have reused
|
|
49
|
-
// the slain daemon's PID for an unrelated process (Chrome tab, shell).
|
|
50
|
-
// Mirror index.js:_lockIsStaleByLease (search for STALE_LOCK_TTL_MS
|
|
51
|
-
// around line 373): a lease-aware daemon refreshes the lock mtime on a
|
|
52
|
-
// timer, so if mtime is older than the TTL the daemon is dead/wedged
|
|
53
|
-
// regardless of kill(0). Constants inlined to keep index.js out of the
|
|
54
|
-
// hook's require graph.
|
|
55
|
-
var STALE_LOCK_TTL_MS = process.platform === 'win32' ? 3 * 60_000 : 5 * 60_000;
|
|
42
|
+
// Lock resolution + lease staleness live in ./_lockPaths (issue #176) —
|
|
43
|
+
// the same module index.js uses, so the hook can never drift from the
|
|
44
|
+
// daemon again. It is fs/os/path-only, keeping index.js out of the
|
|
45
|
+
// hook's require graph (R12), and ships in hookAdapter.js's copy list
|
|
46
|
+
// for the deployed `.claude/hooks/` layout (PR #163).
|
|
47
|
+
var lockFile = lockPaths.getLockFilePath();
|
|
56
48
|
var daemonRunning = false;
|
|
57
49
|
try {
|
|
58
50
|
if (fs.existsSync(lockFile)) {
|
|
59
51
|
var raw = fs.readFileSync(lockFile, 'utf8').trim();
|
|
60
52
|
var payload = raw && raw[0] === '{' ? JSON.parse(raw) : { pid: parseInt(raw, 10) };
|
|
61
53
|
if (payload && payload.pid > 0) {
|
|
54
|
+
// R1: PID-reuse defense. process.kill(pid, 0) only proves SOME
|
|
55
|
+
// process owns that PID -- after macOS sleep / OOM, the kernel may
|
|
56
|
+
// have reused the slain daemon's PID for an unrelated process.
|
|
62
57
|
try { process.kill(payload.pid, 0); daemonRunning = true; } catch (e) {
|
|
63
58
|
// EPERM = process exists but owned by a different user; still a live daemon.
|
|
64
59
|
if (e && e.code === 'EPERM') daemonRunning = true;
|
|
65
60
|
}
|
|
66
|
-
// Lease staleness overrides kill(0)=alive
|
|
67
|
-
// the
|
|
68
|
-
//
|
|
69
|
-
//
|
|
70
|
-
if (daemonRunning &&
|
|
71
|
-
|
|
72
|
-
var ageMs = Date.now() - fs.statSync(lockFile).mtimeMs;
|
|
73
|
-
if (ageMs > STALE_LOCK_TTL_MS) daemonRunning = false;
|
|
74
|
-
} catch (_) { /* stat failed: leave running flag as-is */ }
|
|
61
|
+
// Lease staleness overrides kill(0)=alive: a lease-aware daemon
|
|
62
|
+
// refreshes the lock mtime on a timer, so an expired lease means
|
|
63
|
+
// dead/wedged regardless of kill(0). Pre-lease locks are never
|
|
64
|
+
// judged stale by mtime (lockIsStaleByLease handles both).
|
|
65
|
+
if (daemonRunning && lockPaths.lockIsStaleByLease(lockFile, payload)) {
|
|
66
|
+
daemonRunning = false;
|
|
75
67
|
}
|
|
76
68
|
}
|
|
77
69
|
}
|