@evomap/evolver 1.87.2 → 1.87.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 -1
- package/README.ko-KR.md +1 -1
- package/README.md +9 -8
- package/README.zh-CN.md +9 -8
- package/package.json +1 -1
- package/scripts/build_binaries.js +31 -7
- package/src/atp/atpExecute.js +35 -8
- package/src/atp/autoBuyer.js +71 -16
- package/src/atp/autoDeliver.js +16 -0
- package/src/atp/cliAutobuyPrompt.js +8 -22
- package/src/atp/hubClient.js +42 -4
- 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/gep/a2aProtocol.js +1 -1
- package/src/gep/assetStore.js +52 -5
- 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/epigenetics.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/paths.js +6 -2
- package/src/gep/personality.js +1 -1
- package/src/gep/policyCheck.js +1 -1
- package/src/gep/prompt.js +1 -1
- package/src/gep/recallVerifier.js +1 -1
- package/src/gep/reflection.js +1 -1
- package/src/gep/sanitize.js +57 -3
- package/src/gep/selector.js +1 -1
- package/src/gep/selfPR.js +34 -1
- package/src/gep/skill2gep.js +108 -29
- package/src/gep/skillDistiller.js +1 -1
- package/src/gep/solidify.js +1 -1
- package/src/gep/strategy.js +1 -1
- package/src/gep/workspaceKeychain.js +1 -1
- package/src/proxy/lifecycle/manager.js +97 -37
- package/src/proxy/router/messages_route.js +25 -0
- package/src/proxy/sync/engine.js +68 -31
- package/assets/gep/candidates.jsonl +0 -1
- package/assets/gep/capsules.json +0 -4
- package/assets/gep/events.jsonl +0 -0
- package/assets/gep/failed_capsules.json +0 -4
- package/assets/gep/genes.json +0 -245
- package/assets/gep/genes.jsonl +0 -0
package/src/gep/selfPR.js
CHANGED
|
@@ -40,12 +40,29 @@ const STATE_FILE = 'self_pr_state.json';
|
|
|
40
40
|
// fail-safe behavior (reject all files). We therefore stay silent on load
|
|
41
41
|
// failure here and only surface a warning when maybeCreatePR is actually
|
|
42
42
|
// invoked but the manifest cannot be read.
|
|
43
|
+
//
|
|
44
|
+
// Failed loads are retried after MANIFEST_RETRY_TTL_MS (default 5 min) so a
|
|
45
|
+
// transient FS error during process start (build script still writing the
|
|
46
|
+
// file, NFS hiccup, permission flap) does not freeze the cache at null for
|
|
47
|
+
// the entire daemon lifetime, silently disabling self-PR for days or weeks
|
|
48
|
+
// with no recovery. A successful load remains sticky because the manifest is
|
|
49
|
+
// effectively read-only at runtime.
|
|
43
50
|
let _obfuscatedFilesCache; // undefined = not loaded; Set | null after first attempt
|
|
44
51
|
let _manifestLoadError = null;
|
|
52
|
+
let _manifestLoadFailedAt = 0; // ms timestamp of the last failed load attempt
|
|
45
53
|
let _warnedAboutMissingManifest = false;
|
|
54
|
+
let _manifestRetryTtlMs = 5 * 60 * 1000;
|
|
46
55
|
|
|
47
56
|
function loadObfuscatedFromManifest() {
|
|
48
|
-
|
|
57
|
+
// Hit on a successful previous load — manifest does not change at runtime.
|
|
58
|
+
if (_obfuscatedFilesCache instanceof Set) return _obfuscatedFilesCache;
|
|
59
|
+
// Within the retry window after a failure: skip the FS hit, return cached
|
|
60
|
+
// null so callers stay in the fail-safe branch without hammering the disk.
|
|
61
|
+
if (_obfuscatedFilesCache === null &&
|
|
62
|
+
(Date.now() - _manifestLoadFailedAt) < _manifestRetryTtlMs) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// First-ever attempt OR retry window elapsed since the last failure.
|
|
49
66
|
try {
|
|
50
67
|
const manifestPath = path.join(getEvolverInstallRoot(), 'public.manifest.json');
|
|
51
68
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
@@ -60,9 +77,16 @@ function loadObfuscatedFromManifest() {
|
|
|
60
77
|
}
|
|
61
78
|
}
|
|
62
79
|
_obfuscatedFilesCache = new Set(manifest.obfuscate.map((f) => f.replace(/\\/g, '/').replace(/^\.\/+/, '')));
|
|
80
|
+
_manifestLoadError = null;
|
|
81
|
+
_manifestLoadFailedAt = 0;
|
|
82
|
+
// Allow a future failure (after this success) to surface its warning
|
|
83
|
+
// once, instead of staying silent because an earlier failure already
|
|
84
|
+
// warned in this process.
|
|
85
|
+
_warnedAboutMissingManifest = false;
|
|
63
86
|
} catch (e) {
|
|
64
87
|
_manifestLoadError = e.message;
|
|
65
88
|
_obfuscatedFilesCache = null;
|
|
89
|
+
_manifestLoadFailedAt = Date.now();
|
|
66
90
|
}
|
|
67
91
|
return _obfuscatedFilesCache;
|
|
68
92
|
}
|
|
@@ -72,7 +96,15 @@ function loadObfuscatedFromManifest() {
|
|
|
72
96
|
function _resetObfuscatedCache() {
|
|
73
97
|
_obfuscatedFilesCache = undefined;
|
|
74
98
|
_manifestLoadError = null;
|
|
99
|
+
_manifestLoadFailedAt = 0;
|
|
75
100
|
_warnedAboutMissingManifest = false;
|
|
101
|
+
_manifestRetryTtlMs = 5 * 60 * 1000;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Test-only: shrink (or grow) the retry TTL so a test can exercise the
|
|
105
|
+
// retry-after-transient-failure path without sleeping for real time.
|
|
106
|
+
function _setManifestRetryTtlForTests(ms) {
|
|
107
|
+
_manifestRetryTtlMs = Number(ms) || 0;
|
|
76
108
|
}
|
|
77
109
|
|
|
78
110
|
// Files that are included in the public manifest (superset patterns).
|
|
@@ -433,4 +465,5 @@ module.exports = {
|
|
|
433
465
|
// For testing
|
|
434
466
|
_loadObfuscatedFromManifest: loadObfuscatedFromManifest,
|
|
435
467
|
_resetObfuscatedCache,
|
|
468
|
+
_setManifestRetryTtlForTests,
|
|
436
469
|
};
|
package/src/gep/skill2gep.js
CHANGED
|
@@ -31,6 +31,16 @@ const envFingerprint = require('./envFingerprint');
|
|
|
31
31
|
const a2a = require('./a2aProtocol');
|
|
32
32
|
|
|
33
33
|
const SKILL2GEP_ID_PREFIX = 'gene_s2g_';
|
|
34
|
+
|
|
35
|
+
// Max strategy steps kept on a distilled Gene. The old value (10) silently
|
|
36
|
+
// truncated multi-section Skills, dropping the *governance* tail
|
|
37
|
+
// (candidate-gating, Human Gate, Output Contract, rollback) that lives at the
|
|
38
|
+
// end of a well-formed SKILL.md. extractSteps emits each list item flatly, so
|
|
39
|
+
// a rich Skill (workflow + governance sections) yields ~25-27 short one-line
|
|
40
|
+
// steps; the cap must clear that to keep the tail. 28 covers a well-formed
|
|
41
|
+
// SKILL.md while staying compact (short one-liners, far below a full Skill's
|
|
42
|
+
// token weight). Genuinely longer Skills are still bounded here.
|
|
43
|
+
const MAX_STRATEGY_STEPS = 28;
|
|
34
44
|
const CAPSULE_ID_PREFIX = 'cap_s2g_';
|
|
35
45
|
const LOG_FILE = 'skill2gep_log.jsonl';
|
|
36
46
|
const STATE_FILE = 'skill2gep_state.json';
|
|
@@ -131,6 +141,7 @@ function parseSkillMd(skillMd) {
|
|
|
131
141
|
});
|
|
132
142
|
Object.keys(sections).forEach((k) => { sections[k] = sections[k].join('\n').trim(); });
|
|
133
143
|
|
|
144
|
+
// Return the FIRST matching section (kept for signals, which wants one block).
|
|
134
145
|
function pickSection(keywords) {
|
|
135
146
|
for (const kw of keywords) {
|
|
136
147
|
for (const k of Object.keys(sections)) {
|
|
@@ -140,9 +151,60 @@ function parseSkillMd(skillMd) {
|
|
|
140
151
|
return '';
|
|
141
152
|
}
|
|
142
153
|
|
|
154
|
+
// Return ALL matching sections concatenated, in document order. A SKILL.md
|
|
155
|
+
// often spreads positive steps across several headed sections ("Quick
|
|
156
|
+
// Workflow", "Human Gate Defaults", "Output Contract"); picking only the
|
|
157
|
+
// first dropped the governance tail. Each section's title is preserved as a
|
|
158
|
+
// step-context line so a trailing "## Human Gate" still contributes its
|
|
159
|
+
// bullets. De-duplicated by section key.
|
|
160
|
+
function pickSectionsAll(keywords) {
|
|
161
|
+
const seen = new Set();
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const k of Object.keys(sections)) {
|
|
164
|
+
if (keywords.some((kw) => k.indexOf(kw) !== -1) && !seen.has(k)) {
|
|
165
|
+
seen.add(k);
|
|
166
|
+
out.push(sections[k]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return out.join('\n');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Extract ordered steps from a markdown block: every list item becomes its
|
|
173
|
+
// own step, in document order. This is the pre-PR flat behaviour, kept
|
|
174
|
+
// deliberately simple — an earlier version folded indented sub-bullets into
|
|
175
|
+
// their parent step to look tidier, but that indentation logic grew a long
|
|
176
|
+
// tail of edge cases (section-trim interaction, length-filtered parents,
|
|
177
|
+
// cross-section indentation). Folding was only cosmetic; flat extraction
|
|
178
|
+
// preserves the same governance tail with no indentation reasoning at all.
|
|
179
|
+
// opts.minLen / opts.maxLen bound each item (defaults 5..300, matching the
|
|
180
|
+
// original strategy/avoid gate). Preconditions pass {minLen: 1,
|
|
181
|
+
// maxLen: Infinity} so short prerequisites like "Git"/"npm" survive.
|
|
182
|
+
function extractSteps(block, opts) {
|
|
183
|
+
const minLen = opts && typeof opts.minLen === 'number' ? opts.minLen : 5;
|
|
184
|
+
const maxLen = opts && typeof opts.maxLen === 'number' ? opts.maxLen : 300;
|
|
185
|
+
const steps = [];
|
|
186
|
+
for (const line of String(block || '').split(/\n/)) {
|
|
187
|
+
const m = line.match(/^\s*(?:\d+\.|[-*])\s+(.+?)\s*$/);
|
|
188
|
+
if (!m) continue;
|
|
189
|
+
const txt = m[1].trim();
|
|
190
|
+
if (txt.length >= minLen && txt.length <= maxLen) steps.push(txt);
|
|
191
|
+
}
|
|
192
|
+
return steps;
|
|
193
|
+
}
|
|
194
|
+
|
|
143
195
|
const signals = [];
|
|
196
|
+
// Section keywords are matched against lower-cased headings. A SKILL.md may
|
|
197
|
+
// be authored in Chinese (e.g. game-* skills use "## 何时使用" / "## 触发条件"),
|
|
198
|
+
// whose heading key never contains an English token, so the CJK synonyms
|
|
199
|
+
// below are required for those skills to contribute signals/strategy/avoid
|
|
200
|
+
// at all — without them the distiller silently falls back to a thin gene.
|
|
201
|
+
// NOTE: this only fixes *section matching*. The signal tokenizer below still
|
|
202
|
+
// keeps ASCII [a-z0-9_] only, so signals for a Chinese skill come from its
|
|
203
|
+
// (English) frontmatter description, not from CJK body words. CJK signal
|
|
204
|
+
// tokenization needs a word segmenter and is intentionally out of scope here.
|
|
144
205
|
const signalSource = (frontmatter.description || '') + '\n' + pickSection([
|
|
145
206
|
'trigger', 'when to use', 'when', 'use when', 'scenario',
|
|
207
|
+
'何时使用', '什么时候使用', '触发条件', '触发', '使用场景', '核心目标', '适用',
|
|
146
208
|
]);
|
|
147
209
|
signalSource.split(/[`,.\n]/).forEach((tok) => {
|
|
148
210
|
const s = tok.trim().toLowerCase().replace(/[^a-z0-9_]/g, '_').replace(/^_+|_+$/g, '');
|
|
@@ -151,28 +213,27 @@ function parseSkillMd(skillMd) {
|
|
|
151
213
|
}
|
|
152
214
|
});
|
|
153
215
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
216
|
+
// Strategy spans the workflow AND the governance tail (Human Gate, Output
|
|
217
|
+
// Contract) — concatenate all matching sections so the candidate/gate/rollback
|
|
218
|
+
// discipline survives, and fold nested sub-bullets into their parent step.
|
|
219
|
+
const strategyBlock = pickSectionsAll([
|
|
220
|
+
'workflow', 'strategy', 'steps', 'procedure', 'quick start', 'how to',
|
|
221
|
+
'human gate', 'output contract', 'release', 'rollback', 'promotion',
|
|
222
|
+
// CJK synonyms: positive workflow + governance-tail headings.
|
|
223
|
+
'工作流', '流程', '步骤', '核心方法', '方法', '快速规则', '规则',
|
|
224
|
+
'输出门', '输出门槛', '人工确认', '人工门', '回滚', '发布', '晋级',
|
|
225
|
+
]);
|
|
226
|
+
const strategy = extractSteps(strategyBlock);
|
|
163
227
|
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (s.length >= 5 && s.length <= 300) avoid.push(s);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
228
|
+
const avoidBlock = pickSectionsAll([
|
|
229
|
+
'avoid', 'pitfall', 'anti-pattern', 'common mistake', 'do not', 'forbidden', "don't",
|
|
230
|
+
// CJK synonyms: anti-pattern / "do not" headings.
|
|
231
|
+
'不要做', '不要', '常见错误', '避免', '陷阱', '禁止',
|
|
232
|
+
]);
|
|
233
|
+
const avoid = extractSteps(avoidBlock);
|
|
173
234
|
|
|
174
235
|
const validation = [];
|
|
175
|
-
const valBlock = pickSection(['validation', 'test', 'verify', 'check']);
|
|
236
|
+
const valBlock = pickSection(['validation', 'test', 'verify', 'check', '校验', '验证', '测试', '检查']);
|
|
176
237
|
const fenceRe = /```(?:bash|sh|shell)?\s*\n([\s\S]*?)\n```/g;
|
|
177
238
|
let fm;
|
|
178
239
|
while ((fm = fenceRe.exec(valBlock)) !== null) {
|
|
@@ -182,12 +243,10 @@ function parseSkillMd(skillMd) {
|
|
|
182
243
|
});
|
|
183
244
|
}
|
|
184
245
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
preBlock
|
|
188
|
-
|
|
189
|
-
if (step) preconditions.push(step[1].trim());
|
|
190
|
-
});
|
|
246
|
+
// Preconditions keep the pre-PR behaviour: no length gate, no folding, so
|
|
247
|
+
// short items like "Git"/"npm" survive and preconditions_extracted is stable.
|
|
248
|
+
const preBlock = pickSection(['precondition', 'requirement', 'prerequisite', '前置条件', '前置', '先决条件', '要求']);
|
|
249
|
+
const preconditions = extractSteps(preBlock, { minLen: 1, maxLen: Infinity });
|
|
191
250
|
|
|
192
251
|
return {
|
|
193
252
|
frontmatter: frontmatter,
|
|
@@ -195,7 +254,7 @@ function parseSkillMd(skillMd) {
|
|
|
195
254
|
name: frontmatter.name || (sections['_preamble'] || '').split(/\n/)[0].replace(/^#+\s*/, '').trim(),
|
|
196
255
|
description: frontmatter.description || '',
|
|
197
256
|
signals_match: signals.slice(0, 8),
|
|
198
|
-
strategy: strategy.slice(0,
|
|
257
|
+
strategy: strategy.slice(0, MAX_STRATEGY_STEPS),
|
|
199
258
|
avoid: avoid.slice(0, 5),
|
|
200
259
|
validation: validation.slice(0, 5),
|
|
201
260
|
preconditions: preconditions.slice(0, 4),
|
|
@@ -280,7 +339,7 @@ function synthesizeGene(parsed, execution, opts) {
|
|
|
280
339
|
preconditions: (parsed.preconditions && parsed.preconditions.length > 0)
|
|
281
340
|
? parsed.preconditions
|
|
282
341
|
: ['Skill ' + (parsed.name || 'unknown') + ' has just been executed locally'],
|
|
283
|
-
strategy: strategy.slice(0,
|
|
342
|
+
strategy: strategy.slice(0, MAX_STRATEGY_STEPS),
|
|
284
343
|
avoid: avoid,
|
|
285
344
|
constraints: {
|
|
286
345
|
max_files: (opts && opts.maxFiles) || skillDistiller.DISTILLED_MAX_FILES,
|
|
@@ -309,8 +368,27 @@ function synthesizeGene(parsed, execution, opts) {
|
|
|
309
368
|
|
|
310
369
|
function inferCategory(signals, description) {
|
|
311
370
|
const hay = ((description || '') + ' ' + (signals || []).join(' ')).toLowerCase();
|
|
312
|
-
|
|
313
|
-
|
|
371
|
+
// Priority repair -> innovate -> optimize, mirroring the sibling
|
|
372
|
+
// inferCategoryFromSignals() in skillDistiller.js / solidify.js.
|
|
373
|
+
//
|
|
374
|
+
// REPAIR set uses SUBSTRING matching (no \b): it must catch both inflected
|
|
375
|
+
// forms ("errors", "fixed", "crashes") and the project's underscore signal
|
|
376
|
+
// format ("log_error", "test_failure"), which a \b-anchored regex breaks
|
|
377
|
+
// (\b treats `_` as a word char, so "error" inside "log_error" has no
|
|
378
|
+
// boundary). Changes vs. the pre-PR original:
|
|
379
|
+
// - repair: removed "rollback"/"guard" — cross-cutting safety words common
|
|
380
|
+
// in *optimize* skills (e.g. paranoia-ai-system-evolver lists "rollback"
|
|
381
|
+
// in its safe-change method) that must not by themselves force repair.
|
|
382
|
+
// - innovate: "add" is matched with a \b word boundary so it catches the
|
|
383
|
+
// verb ("add a dashboard") without false-positives on address/additional/
|
|
384
|
+
// padding (the pre-PR bare-substring "add" matched all of those). The
|
|
385
|
+
// innovate set only reads natural-language description, so \b is safe here.
|
|
386
|
+
if (/error|fail|bug|crash|broken|incident|regress|debug|repair|fix/.test(hay)) {
|
|
387
|
+
return 'repair';
|
|
388
|
+
}
|
|
389
|
+
if (/feature|\badd\b|implement|new capability|capability|innovate|greenfield|prototype/.test(hay)) {
|
|
390
|
+
return 'innovate';
|
|
391
|
+
}
|
|
314
392
|
return 'optimize';
|
|
315
393
|
}
|
|
316
394
|
|
|
@@ -679,6 +757,7 @@ module.exports = {
|
|
|
679
757
|
RATIONALE_TEXT,
|
|
680
758
|
parseSkillMd,
|
|
681
759
|
synthesizeGene,
|
|
760
|
+
inferCategory,
|
|
682
761
|
detectForgery,
|
|
683
762
|
assembleCapsule,
|
|
684
763
|
runOnSkillInvocation,
|