@evomap/evolver 1.70.0-beta.5 → 1.70.0
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/assets/gep/candidates.jsonl +6 -3
- package/index.js +2 -56
- package/package.json +1 -1
- package/scripts/validate-suite.js +6 -21
- package/src/adapters/hookAdapter.js +1 -3
- package/src/adapters/scripts/evolver-session-start.js +0 -62
- package/src/evolve.js +1 -1
- package/src/gep/.integrity +0 -0
- package/src/gep/a2aProtocol.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/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/explore.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/integrityCheck.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/personality.js +1 -1
- package/src/gep/policyCheck.js +1 -1
- package/src/gep/prompt.js +1 -1
- package/src/gep/reflection.js +1 -1
- package/src/gep/selector.js +1 -1
- package/src/gep/shield.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/validator/sandboxExecutor.js +2 -11
- package/src/proxy/lifecycle/manager.js +1 -5
- package/src/proxy/mailbox/store.js +0 -5
- package/src/proxy/server/http.js +4 -47
- package/src/adapters/kiro.js +0 -203
|
@@ -1,3 +1,6 @@
|
|
|
1
|
-
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:
|
|
2
|
-
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:
|
|
3
|
-
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:
|
|
1
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:47:45.106Z","signals":["memory_missing","user_missing","session_logs_missing"],"tags":["memory_missing","user_missing","session_logs_missing","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
2
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:47:47.051Z","signals":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, seo, approaches, different, compare, comparison, note, within, optimization, keyword, red, xiaohongshu, xiaohongshu-ops, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
3
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:47:48.939Z","signals":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, seo, approaches, different, compare, comparison, note, within, optimization, keyword, red, xiaohongshu, xiaohongshu-ops, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
4
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:49:00.343Z","signals":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, seo, approaches, different, compare, comparison, note, within, optimization, keyword, red, xiaohongshu, xiaohongshu-ops, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
5
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:49:02.561Z","signals":["bounty_task","external_task","skills","monetize","how","monetization","reduce","optimization","prompt","negative","image-generation","ai-art","ai-image-generation","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","skills","monetize","how","monetization","reduce","optimization","prompt","negative","image-generation","ai-art","ai-image-generation","memory_missing","user_missing","session_logs_missing","problem:protocol","action:optimize","area:prompt","area:orchestration","area:memory","area:skills"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, skills, monetize, how, monetization, reduce, optimization, prompt, negative, image-generation, ai-art, ai-image-generation, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
|
6
|
+
{"type":"CapabilityCandidate","id":"cand_b9a66a5c","title":"Harden session log detection and fallback behavior","source":"signals","created_at":"2026-04-27T05:49:04.551Z","signals":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing"],"tags":["bounty_task","external_task","seo","approaches","different","compare","comparison","note","within","optimization","keyword","red","xiaohongshu","xiaohongshu-ops","memory_missing","user_missing","session_logs_missing","area:orchestration","area:memory"],"shape":{"title":"Harden session log detection and fallback behavior","input":"Recent session transcript + memory snippets + user instructions","output":"A safe, auditable evolution patch guided by GEP assets","invariants":"Protocol order, small reversible patches, validation, append-only events","params":"Signals: bounty_task, external_task, seo, approaches, different, compare, comparison, note, within, optimization, keyword, red, xiaohongshu, xiaohongshu-ops, memory_missing, user_missing, session_logs_missing","failure_points":"Missing signals, over-broad changes, skipped validation, missing knowledge solidification","evidence":"Signal present: session_logs_missing"}}
|
package/index.js
CHANGED
|
@@ -847,23 +847,6 @@ async function main() {
|
|
|
847
847
|
const data = await resp.json();
|
|
848
848
|
const outFlag = args.find(a => typeof a === 'string' && a.startsWith('--out='));
|
|
849
849
|
const safeId = String(data.skill_id || skillId).replace(/[^a-zA-Z0-9_\-\.]/g, '_');
|
|
850
|
-
// Reject safeId values that would either stay inside cwd instead of
|
|
851
|
-
// descending into skills/, or escape cwd entirely. The sanitizing regex
|
|
852
|
-
// above permits `.`, so `..` / `.` / empty survive it; `path.join('.',
|
|
853
|
-
// 'skills', '..')` collapses to `.` which turns the download directory
|
|
854
|
-
// into the user's working directory and lets Hub-supplied bundled_files
|
|
855
|
-
// overwrite `index.js`, `package.json`, etc. See GHSA-cfcj-hqpf-hccf.
|
|
856
|
-
if (
|
|
857
|
-
safeId === '' ||
|
|
858
|
-
safeId === '.' ||
|
|
859
|
-
safeId === '..' ||
|
|
860
|
-
safeId.includes('/') ||
|
|
861
|
-
safeId.includes('\\') ||
|
|
862
|
-
safeId.includes('\0')
|
|
863
|
-
) {
|
|
864
|
-
console.error('[fetch] Hub returned an invalid skill_id: ' + JSON.stringify(safeId));
|
|
865
|
-
process.exit(1);
|
|
866
|
-
}
|
|
867
850
|
let outDir;
|
|
868
851
|
if (outFlag) {
|
|
869
852
|
const rawOut = outFlag.slice('--out='.length);
|
|
@@ -886,16 +869,7 @@ async function main() {
|
|
|
886
869
|
}
|
|
887
870
|
outDir = resolvedOut;
|
|
888
871
|
} else {
|
|
889
|
-
|
|
890
|
-
// branch so any remaining path-smuggling shape in `safeId` is caught.
|
|
891
|
-
const candidate = path.resolve(process.cwd(), 'skills', safeId);
|
|
892
|
-
const skillsRoot = path.resolve(process.cwd(), 'skills');
|
|
893
|
-
const rel = path.relative(skillsRoot, candidate);
|
|
894
|
-
if (rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
895
|
-
console.error('[fetch] Hub-provided skill_id escapes skills/ directory: ' + JSON.stringify(safeId));
|
|
896
|
-
process.exit(1);
|
|
897
|
-
}
|
|
898
|
-
outDir = candidate;
|
|
872
|
+
outDir = path.join('.', 'skills', safeId);
|
|
899
873
|
}
|
|
900
874
|
|
|
901
875
|
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
@@ -911,24 +885,12 @@ async function main() {
|
|
|
911
885
|
'.yml', '.yaml',
|
|
912
886
|
]);
|
|
913
887
|
const MAX_SKILL_FILE_BYTES = 512 * 1024;
|
|
914
|
-
// Even with outDir locked to skills/, a legitimate-looking skill can
|
|
915
|
-
// ship a bundled file named `package.json`, `index.js`, or any other
|
|
916
|
-
// top-level project artifact whose name collides with something the
|
|
917
|
-
// user may later copy back up. Prefix-guard the resolved path so every
|
|
918
|
-
// write stays strictly within the resolved outDir (no trailing `/..`
|
|
919
|
-
// in basename, no absolute path smuggling) and never points at cwd.
|
|
920
|
-
const resolvedOutDir = path.resolve(outDir);
|
|
921
|
-
const resolvedCwd = path.resolve(process.cwd());
|
|
922
888
|
|
|
923
889
|
const bundled = Array.isArray(data.bundled_files) ? data.bundled_files : [];
|
|
924
890
|
const skippedFiles = [];
|
|
925
891
|
for (const file of bundled) {
|
|
926
892
|
if (!file || !file.name || typeof file.content !== 'string') continue;
|
|
927
893
|
const safeName = path.basename(file.name);
|
|
928
|
-
if (!safeName || safeName === '.' || safeName === '..') {
|
|
929
|
-
skippedFiles.push(String(file.name));
|
|
930
|
-
continue;
|
|
931
|
-
}
|
|
932
894
|
const ext = path.extname(safeName).toLowerCase();
|
|
933
895
|
if (!ALLOWED_SKILL_EXTENSIONS.has(ext)) {
|
|
934
896
|
console.warn('[fetch] Skipped skill file with disallowed extension: ' + safeName);
|
|
@@ -940,23 +902,7 @@ async function main() {
|
|
|
940
902
|
skippedFiles.push(safeName);
|
|
941
903
|
continue;
|
|
942
904
|
}
|
|
943
|
-
|
|
944
|
-
const relToOut = path.relative(resolvedOutDir, destPath);
|
|
945
|
-
if (relToOut.startsWith('..') || path.isAbsolute(relToOut)) {
|
|
946
|
-
console.warn('[fetch] Skipped bundled file whose resolved path escapes outDir: ' + safeName);
|
|
947
|
-
skippedFiles.push(safeName);
|
|
948
|
-
continue;
|
|
949
|
-
}
|
|
950
|
-
// Never let a bundled write touch the evolver's own cwd -- this is
|
|
951
|
-
// the concrete attack shape from GHSA-cfcj-hqpf-hccf (fetch default
|
|
952
|
-
// branch writing to `./index.js`). outDir should always be under
|
|
953
|
-
// skills/ now, but belt-and-braces keep the guarantee explicit.
|
|
954
|
-
if (path.dirname(destPath) === resolvedCwd) {
|
|
955
|
-
console.warn('[fetch] Skipped bundled file that would land in cwd: ' + safeName);
|
|
956
|
-
skippedFiles.push(safeName);
|
|
957
|
-
continue;
|
|
958
|
-
}
|
|
959
|
-
fs.writeFileSync(destPath, file.content, 'utf8');
|
|
905
|
+
fs.writeFileSync(path.join(outDir, safeName), file.content, 'utf8');
|
|
960
906
|
}
|
|
961
907
|
|
|
962
908
|
console.log('[fetch] Skill downloaded to: ' + outDir);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver",
|
|
3
|
-
"version": "1.70.0
|
|
3
|
+
"version": "1.70.0",
|
|
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,8 +1,6 @@
|
|
|
1
|
-
// Usage: node scripts/validate-suite.js [test-glob-pattern
|
|
1
|
+
// Usage: node scripts/validate-suite.js [test-glob-pattern]
|
|
2
2
|
// Runs the evolver test suite -- repo root is derived from script location, no shell glob needed.
|
|
3
|
-
|
|
4
|
-
// See community PR #514.
|
|
5
|
-
const { execFileSync } = require('child_process');
|
|
3
|
+
const { execSync } = require('child_process');
|
|
6
4
|
const path = require('path');
|
|
7
5
|
const fs = require('fs');
|
|
8
6
|
|
|
@@ -10,22 +8,10 @@ const EVOLVER_REPO_ROOT = path.join(__dirname, '..');
|
|
|
10
8
|
const pattern = process.argv[2] || 'test/*.test.js';
|
|
11
9
|
|
|
12
10
|
function expandTestGlob(repoRoot, pat) {
|
|
13
|
-
const
|
|
14
|
-
if (fs.existsSync(fullPattern) && fs.statSync(fullPattern).isFile()) {
|
|
15
|
-
return fullPattern.endsWith('.test.js') ? [fullPattern] : [];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const dir = path.dirname(pat);
|
|
19
|
-
const basenamePattern = path.basename(pat);
|
|
11
|
+
const dir = pat.replace(/\/\*\.test\.js$/, '');
|
|
20
12
|
const fullDir = path.isAbsolute(dir) ? dir : path.join(repoRoot, dir);
|
|
21
|
-
if (!fs.existsSync(fullDir) || !fs.statSync(fullDir).isDirectory()) return [];
|
|
22
|
-
|
|
23
|
-
const escaped = basenamePattern
|
|
24
|
-
.replace(/[.+?^${}()|[\]\\]/g, '\\$&')
|
|
25
|
-
.replace(/\*/g, '.*');
|
|
26
|
-
const matcher = new RegExp('^' + escaped + '$');
|
|
27
13
|
return fs.readdirSync(fullDir)
|
|
28
|
-
.filter(f => f.endsWith('.test.js')
|
|
14
|
+
.filter(f => f.endsWith('.test.js'))
|
|
29
15
|
.map(f => path.join(fullDir, f))
|
|
30
16
|
.sort();
|
|
31
17
|
}
|
|
@@ -36,6 +22,7 @@ if (files.length === 0) {
|
|
|
36
22
|
process.exit(1);
|
|
37
23
|
}
|
|
38
24
|
|
|
25
|
+
const cmd = 'node --test ' + files.join(' ');
|
|
39
26
|
const env = Object.assign({}, process.env, {
|
|
40
27
|
NODE_ENV: 'test',
|
|
41
28
|
EVOLVER_REPO_ROOT,
|
|
@@ -43,11 +30,9 @@ const env = Object.assign({}, process.env, {
|
|
|
43
30
|
});
|
|
44
31
|
delete env.EVOLVE_BRIDGE;
|
|
45
32
|
delete env.OPENCLAW_WORKSPACE;
|
|
46
|
-
// Clear NODE_TEST_CONTEXT so nested runs from within node --test work.
|
|
47
|
-
delete env.NODE_TEST_CONTEXT;
|
|
48
33
|
|
|
49
34
|
try {
|
|
50
|
-
const output =
|
|
35
|
+
const output = execSync(cmd, {
|
|
51
36
|
cwd: EVOLVER_REPO_ROOT,
|
|
52
37
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
53
38
|
timeout: 180000,
|
|
@@ -6,7 +6,6 @@ const PLATFORMS = {
|
|
|
6
6
|
cursor: { name: 'Cursor', configDir: '.cursor', detector: '.cursor' },
|
|
7
7
|
'claude-code': { name: 'Claude Code', configDir: '.claude', detector: '.claude' },
|
|
8
8
|
codex: { name: 'Codex', configDir: '.codex', detector: '.codex' },
|
|
9
|
-
kiro: { name: 'Kiro', configDir: '.kiro', detector: '.kiro' },
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
function detectPlatform(cwd) {
|
|
@@ -36,7 +35,6 @@ function loadAdapter(platformId) {
|
|
|
36
35
|
case 'cursor': return require('./cursor');
|
|
37
36
|
case 'claude-code': return require('./claudeCode');
|
|
38
37
|
case 'codex': return require('./codex');
|
|
39
|
-
case 'kiro': return require('./kiro');
|
|
40
38
|
default: return null;
|
|
41
39
|
}
|
|
42
40
|
}
|
|
@@ -165,7 +163,7 @@ async function setupHooks({ platform, cwd, force, uninstall, evolverRoot } = {})
|
|
|
165
163
|
const platformId = platform || detectPlatform(effectiveCwd);
|
|
166
164
|
|
|
167
165
|
if (!platformId) {
|
|
168
|
-
console.error('[setup-hooks] Could not detect platform. Use --platform=cursor|claude-code|codex
|
|
166
|
+
console.error('[setup-hooks] Could not detect platform. Use --platform=cursor|claude-code|codex');
|
|
169
167
|
return { ok: false, error: 'platform_not_detected' };
|
|
170
168
|
}
|
|
171
169
|
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
const fs = require('fs');
|
|
7
7
|
const path = require('path');
|
|
8
|
-
const os = require('os');
|
|
9
8
|
|
|
10
9
|
function findEvolverRoot() {
|
|
11
10
|
const candidates = [
|
|
@@ -59,68 +58,7 @@ function formatOutcome(entry) {
|
|
|
59
58
|
return `[${icon}] ${ts} score=${score} signals=[${signals}] ${note}`.slice(0, 200);
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
// Dedup guard: on platforms like Kiro, the sessionStart-equivalent event
|
|
63
|
-
// (`promptSubmit`) fires on every user message in a session. Without this
|
|
64
|
-
// guard, recent memory would be re-injected on every prompt. We key the
|
|
65
|
-
// dedup on (platform, cwd) with a short TTL so a fresh agent session within
|
|
66
|
-
// the same workspace still gets the injection, but mid-session prompts do
|
|
67
|
-
// not. Cursor/Claude Code/Codex have true sessionStart events and should
|
|
68
|
-
// bypass this check (controlled by EVOLVER_SESSION_START_DEDUP env var,
|
|
69
|
-
// which the Kiro adapter sets on the hook command line implicitly via the
|
|
70
|
-
// runtime environment, and other adapters leave unset).
|
|
71
|
-
function getDedupStatePath() {
|
|
72
|
-
const dir = process.env.EVOLVER_SESSION_STATE_DIR
|
|
73
|
-
|| path.join(os.homedir(), '.evolver');
|
|
74
|
-
try { fs.mkdirSync(dir, { recursive: true }); } catch { /* ignore */ }
|
|
75
|
-
return path.join(dir, 'session-start-state.json');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function shouldSkipInjection() {
|
|
79
|
-
// Only apply dedup when explicitly enabled (set by Kiro adapter) OR when
|
|
80
|
-
// we detect a per-prompt-firing platform via PROMPT_SUBMIT heuristic in
|
|
81
|
-
// stdin. The stdin is drained in main(), so we rely on env flag here.
|
|
82
|
-
const dedupEnabled = String(process.env.EVOLVER_SESSION_START_DEDUP || '').toLowerCase() === '1'
|
|
83
|
-
|| String(process.env.EVOLVER_SESSION_START_DEDUP || '').toLowerCase() === 'true';
|
|
84
|
-
if (!dedupEnabled) return false;
|
|
85
|
-
|
|
86
|
-
const ttlMs = Number(process.env.EVOLVER_SESSION_START_DEDUP_TTL_MS) || (30 * 60 * 1000);
|
|
87
|
-
const key = process.cwd();
|
|
88
|
-
const statePath = getDedupStatePath();
|
|
89
|
-
|
|
90
|
-
let state = {};
|
|
91
|
-
try {
|
|
92
|
-
if (fs.existsSync(statePath)) {
|
|
93
|
-
state = JSON.parse(fs.readFileSync(statePath, 'utf8')) || {};
|
|
94
|
-
}
|
|
95
|
-
} catch { state = {}; }
|
|
96
|
-
|
|
97
|
-
const now = Date.now();
|
|
98
|
-
const last = state[key];
|
|
99
|
-
if (typeof last === 'number' && now - last < ttlMs) {
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
state[key] = now;
|
|
104
|
-
try {
|
|
105
|
-
for (const k of Object.keys(state)) {
|
|
106
|
-
if (typeof state[k] !== 'number' || now - state[k] > 24 * 60 * 60 * 1000) {
|
|
107
|
-
delete state[k];
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const tmp = statePath + '.tmp';
|
|
111
|
-
fs.writeFileSync(tmp, JSON.stringify(state), 'utf8');
|
|
112
|
-
fs.renameSync(tmp, statePath);
|
|
113
|
-
} catch { /* best-effort */ }
|
|
114
|
-
|
|
115
|
-
return false;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
61
|
function main() {
|
|
119
|
-
if (shouldSkipInjection()) {
|
|
120
|
-
process.stdout.write(JSON.stringify({}));
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
62
|
const evolverRoot = findEvolverRoot();
|
|
125
63
|
const graphPath = findMemoryGraph(evolverRoot);
|
|
126
64
|
|