@evomap/evolver 1.29.0 → 1.29.4
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/index.js +51 -0
- package/package.json +1 -1
- package/src/evolve.js +24 -8
- package/src/gep/a2aProtocol.js +19 -5
- package/src/gep/assetStore.js +41 -10
- package/src/gep/envFingerprint.js +16 -1
- package/src/gep/paths.js +22 -5
- package/src/gep/skillDistiller.js +17 -1
- package/src/gep/skillPublisher.js +163 -0
package/index.js
CHANGED
|
@@ -24,6 +24,37 @@ function readJsonSafe(p) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function rejectPendingRun(statePath) {
|
|
28
|
+
try {
|
|
29
|
+
const { getRepoRoot } = require('./src/gep/paths');
|
|
30
|
+
const { execSync } = require('child_process');
|
|
31
|
+
const repoRoot = getRepoRoot();
|
|
32
|
+
|
|
33
|
+
execSync('git checkout -- .', { cwd: repoRoot, encoding: 'utf8', timeout: 30000 });
|
|
34
|
+
execSync('git clean -fd', { cwd: repoRoot, encoding: 'utf8', timeout: 30000 });
|
|
35
|
+
} catch (e) {
|
|
36
|
+
console.warn('[Loop] Pending run rollback failed: ' + (e.message || e));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const state = readJsonSafe(statePath);
|
|
41
|
+
if (state && state.last_run && state.last_run.run_id) {
|
|
42
|
+
state.last_solidify = {
|
|
43
|
+
run_id: state.last_run.run_id,
|
|
44
|
+
rejected: true,
|
|
45
|
+
reason: 'loop_bridge_disabled_autoreject',
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + '\n', 'utf8');
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
} catch (e) {
|
|
52
|
+
console.warn('[Loop] Failed to clear pending run state: ' + (e.message || e));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
27
58
|
function isPendingSolidify(state) {
|
|
28
59
|
const lastRun = state && state.last_run ? state.last_run : null;
|
|
29
60
|
const lastSolid = state && state.last_solidify ? state.last_solidify : null;
|
|
@@ -80,6 +111,16 @@ async function main() {
|
|
|
80
111
|
const isLoop = args.includes('--loop') || args.includes('--mad-dog');
|
|
81
112
|
|
|
82
113
|
if (command === 'run' || command === '/evolve' || isLoop) {
|
|
114
|
+
if (isLoop) {
|
|
115
|
+
const originalLog = console.log;
|
|
116
|
+
const originalWarn = console.warn;
|
|
117
|
+
const originalError = console.error;
|
|
118
|
+
function ts() { return '[' + new Date().toISOString() + ']'; }
|
|
119
|
+
console.log = (...args) => { originalLog.call(console, ts(), ...args); };
|
|
120
|
+
console.warn = (...args) => { originalWarn.call(console, ts(), ...args); };
|
|
121
|
+
console.error = (...args) => { originalError.call(console, ts(), ...args); };
|
|
122
|
+
}
|
|
123
|
+
|
|
83
124
|
console.log('Starting capability evolver...');
|
|
84
125
|
|
|
85
126
|
if (isLoop) {
|
|
@@ -137,6 +178,16 @@ async function main() {
|
|
|
137
178
|
try {
|
|
138
179
|
await evolve.run();
|
|
139
180
|
ok = true;
|
|
181
|
+
|
|
182
|
+
if (String(process.env.EVOLVE_BRIDGE || '').toLowerCase() === 'false') {
|
|
183
|
+
const stAfterRun = readJsonSafe(solidifyStatePath);
|
|
184
|
+
if (isPendingSolidify(stAfterRun)) {
|
|
185
|
+
const cleared = rejectPendingRun(solidifyStatePath);
|
|
186
|
+
if (cleared) {
|
|
187
|
+
console.warn('[Loop] Auto-rejected pending run because bridge is disabled in loop mode.');
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
140
191
|
} catch (error) {
|
|
141
192
|
const msg = error && error.message ? String(error.message) : String(error);
|
|
142
193
|
console.error(`Evolution cycle failed: ${msg}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver",
|
|
3
|
-
"version": "1.29.
|
|
3
|
+
"version": "1.29.4",
|
|
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": {
|
package/src/evolve.js
CHANGED
|
@@ -64,7 +64,9 @@ const TODAY_LOG = path.join(MEMORY_DIR, new Date().toISOString().split('T')[0] +
|
|
|
64
64
|
// Ensure memory directory exists so state/cache writes work.
|
|
65
65
|
try {
|
|
66
66
|
if (!fs.existsSync(MEMORY_DIR)) fs.mkdirSync(MEMORY_DIR, { recursive: true });
|
|
67
|
-
} catch (e) {
|
|
67
|
+
} catch (e) {
|
|
68
|
+
console.warn('[Evolver] Failed to create MEMORY_DIR (may cause downstream errors):', e && e.message || e);
|
|
69
|
+
}
|
|
68
70
|
|
|
69
71
|
function formatSessionLog(jsonlContent) {
|
|
70
72
|
const result = [];
|
|
@@ -1041,7 +1043,9 @@ async function run() {
|
|
|
1041
1043
|
try {
|
|
1042
1044
|
const { tryReadMemoryGraphEvents } = require('./gep/memoryGraph');
|
|
1043
1045
|
taskMemoryEvents = tryReadMemoryGraphEvents(1000);
|
|
1044
|
-
} catch {
|
|
1046
|
+
} catch (e) {
|
|
1047
|
+
console.warn('[TaskReceiver] MemoryGraph read failed (task selection proceeds without history):', e && e.message || e);
|
|
1048
|
+
}
|
|
1045
1049
|
const best = selectBestTask(hubTasks, taskMemoryEvents);
|
|
1046
1050
|
if (best) {
|
|
1047
1051
|
const alreadyClaimed = best.status === 'claimed';
|
|
@@ -1085,7 +1089,9 @@ async function run() {
|
|
|
1085
1089
|
}
|
|
1086
1090
|
}
|
|
1087
1091
|
}
|
|
1088
|
-
} catch {
|
|
1092
|
+
} catch (e) {
|
|
1093
|
+
console.warn('[Commitment] Overdue task check failed (non-fatal):', e && e.message || e);
|
|
1094
|
+
}
|
|
1089
1095
|
|
|
1090
1096
|
// --- Worker Pool: select task from heartbeat available_work (deferred claim) ---
|
|
1091
1097
|
// Only remember the best task and inject its signals; actual claim+complete
|
|
@@ -1099,7 +1105,9 @@ async function run() {
|
|
|
1099
1105
|
try {
|
|
1100
1106
|
const { tryReadMemoryGraphEvents } = require('./gep/memoryGraph');
|
|
1101
1107
|
taskMemoryEvents = tryReadMemoryGraphEvents(1000);
|
|
1102
|
-
} catch {
|
|
1108
|
+
} catch (e) {
|
|
1109
|
+
console.warn('[WorkerPool] MemoryGraph read failed (task selection proceeds without history):', e && e.message || e);
|
|
1110
|
+
}
|
|
1103
1111
|
const best = selectBestTask(workerTasks, taskMemoryEvents);
|
|
1104
1112
|
if (best) {
|
|
1105
1113
|
activeTask = best;
|
|
@@ -1174,7 +1182,9 @@ async function run() {
|
|
|
1174
1182
|
for (const c of newCandidates) {
|
|
1175
1183
|
try {
|
|
1176
1184
|
appendCandidateJsonl(c);
|
|
1177
|
-
} catch (e) {
|
|
1185
|
+
} catch (e) {
|
|
1186
|
+
console.warn('[Candidates] Failed to persist candidate:', e && e.message || e);
|
|
1187
|
+
}
|
|
1178
1188
|
}
|
|
1179
1189
|
const recentCandidates = readRecentCandidates(20);
|
|
1180
1190
|
const capabilityCandidatesPreview = renderCandidatesPreview(recentCandidates.slice(-8), 1600);
|
|
@@ -1237,7 +1247,9 @@ async function run() {
|
|
|
1237
1247
|
2
|
|
1238
1248
|
)}\n\`\`\``;
|
|
1239
1249
|
}
|
|
1240
|
-
} catch (e) {
|
|
1250
|
+
} catch (e) {
|
|
1251
|
+
console.warn('[ExternalCandidates] Preview build failed (non-fatal):', e && e.message || e);
|
|
1252
|
+
}
|
|
1241
1253
|
|
|
1242
1254
|
// Search-First Evolution: query Hub for reusable solutions before local reasoning.
|
|
1243
1255
|
let hubHit = null;
|
|
@@ -1425,7 +1437,9 @@ async function run() {
|
|
|
1425
1437
|
.split('\n')
|
|
1426
1438
|
.map(l => l.trim())
|
|
1427
1439
|
.filter(Boolean);
|
|
1428
|
-
} catch (e) {
|
|
1440
|
+
} catch (e) {
|
|
1441
|
+
console.warn('[SolidifyState] Failed to read baseline untracked files:', e && e.message || e);
|
|
1442
|
+
}
|
|
1429
1443
|
|
|
1430
1444
|
try {
|
|
1431
1445
|
const out = execSync('git rev-parse HEAD', {
|
|
@@ -1436,7 +1450,9 @@ async function run() {
|
|
|
1436
1450
|
windowsHide: true,
|
|
1437
1451
|
});
|
|
1438
1452
|
baselineHead = String(out || '').trim() || null;
|
|
1439
|
-
} catch (e) {
|
|
1453
|
+
} catch (e) {
|
|
1454
|
+
console.warn('[SolidifyState] Failed to read git HEAD:', e && e.message || e);
|
|
1455
|
+
}
|
|
1440
1456
|
|
|
1441
1457
|
const maxFiles =
|
|
1442
1458
|
selectedGene && selectedGene.constraints && Number.isFinite(Number(selectedGene.constraints.max_files))
|
package/src/gep/a2aProtocol.js
CHANGED
|
@@ -172,20 +172,23 @@ function buildPublishBundle(opts) {
|
|
|
172
172
|
if (!capsule || capsule.type !== 'Capsule' || !capsule.id) {
|
|
173
173
|
throw new Error('publishBundle: capsule must be a valid Capsule with type and id');
|
|
174
174
|
}
|
|
175
|
-
var geneAssetId = gene.asset_id || computeAssetId(gene);
|
|
176
|
-
var capsuleAssetId = capsule.asset_id || computeAssetId(capsule);
|
|
177
|
-
var nodeSecret = process.env.A2A_NODE_SECRET || getNodeId();
|
|
178
|
-
var signatureInput = [geneAssetId, capsuleAssetId].sort().join('|');
|
|
179
|
-
var signature = crypto.createHmac('sha256', nodeSecret).update(signatureInput).digest('hex');
|
|
180
175
|
if (o.modelName && typeof o.modelName === 'string') {
|
|
181
176
|
gene.model_name = o.modelName;
|
|
182
177
|
capsule.model_name = o.modelName;
|
|
183
178
|
}
|
|
179
|
+
gene.asset_id = computeAssetId(gene);
|
|
180
|
+
capsule.asset_id = computeAssetId(capsule);
|
|
181
|
+
var geneAssetId = gene.asset_id;
|
|
182
|
+
var capsuleAssetId = capsule.asset_id;
|
|
183
|
+
var nodeSecret = process.env.A2A_NODE_SECRET || getNodeId();
|
|
184
|
+
var signatureInput = [geneAssetId, capsuleAssetId].sort().join('|');
|
|
185
|
+
var signature = crypto.createHmac('sha256', nodeSecret).update(signatureInput).digest('hex');
|
|
184
186
|
var assets = [gene, capsule];
|
|
185
187
|
if (event && event.type === 'EvolutionEvent') {
|
|
186
188
|
if (o.modelName && typeof o.modelName === 'string') {
|
|
187
189
|
event.model_name = o.modelName;
|
|
188
190
|
}
|
|
191
|
+
event.asset_id = computeAssetId(event);
|
|
189
192
|
assets.push(event);
|
|
190
193
|
}
|
|
191
194
|
var publishPayload = {
|
|
@@ -396,6 +399,7 @@ var _heartbeatStartedAt = null;
|
|
|
396
399
|
var _heartbeatConsecutiveFailures = 0;
|
|
397
400
|
var _heartbeatTotalSent = 0;
|
|
398
401
|
var _heartbeatTotalFailed = 0;
|
|
402
|
+
var _heartbeatFpSent = false;
|
|
399
403
|
var _latestAvailableWork = [];
|
|
400
404
|
var _latestOverdueTasks = [];
|
|
401
405
|
var _pendingCommitmentUpdates = [];
|
|
@@ -513,6 +517,16 @@ function sendHeartbeat() {
|
|
|
513
517
|
meta.commitment_updates = _pendingCommitmentUpdates.splice(0);
|
|
514
518
|
}
|
|
515
519
|
|
|
520
|
+
if (!_heartbeatFpSent) {
|
|
521
|
+
try {
|
|
522
|
+
var fp = captureEnvFingerprint();
|
|
523
|
+
if (fp && fp.evolver_version) {
|
|
524
|
+
meta.env_fingerprint = fp;
|
|
525
|
+
_heartbeatFpSent = true;
|
|
526
|
+
}
|
|
527
|
+
} catch {}
|
|
528
|
+
}
|
|
529
|
+
|
|
516
530
|
if (Object.keys(meta).length > 0) {
|
|
517
531
|
bodyObj.meta = meta;
|
|
518
532
|
}
|
package/src/gep/assetStore.js
CHANGED
|
@@ -177,11 +177,27 @@ function readRecentCandidates(limit = 20) {
|
|
|
177
177
|
try {
|
|
178
178
|
const p = candidatesPath();
|
|
179
179
|
if (!fs.existsSync(p)) return [];
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
const stat = fs.statSync(p);
|
|
181
|
+
if (stat.size < 1024 * 1024) {
|
|
182
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
183
|
+
const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
|
|
184
|
+
return lines.slice(-limit).map(l => {
|
|
185
|
+
try { return JSON.parse(l); } catch { return null; }
|
|
186
|
+
}).filter(Boolean);
|
|
187
|
+
}
|
|
188
|
+
// Large file (>1MB): only read the tail to avoid OOM.
|
|
189
|
+
const fd = fs.openSync(p, 'r');
|
|
190
|
+
try {
|
|
191
|
+
const chunkSize = Math.min(stat.size, limit * 4096);
|
|
192
|
+
const buf = Buffer.alloc(chunkSize);
|
|
193
|
+
fs.readSync(fd, buf, 0, chunkSize, stat.size - chunkSize);
|
|
194
|
+
const lines = buf.toString('utf8').split('\n').map(l => l.trim()).filter(Boolean);
|
|
195
|
+
return lines.slice(-limit).map(l => {
|
|
196
|
+
try { return JSON.parse(l); } catch { return null; }
|
|
197
|
+
}).filter(Boolean);
|
|
198
|
+
} finally {
|
|
199
|
+
fs.closeSync(fd);
|
|
200
|
+
}
|
|
185
201
|
} catch { return []; }
|
|
186
202
|
}
|
|
187
203
|
|
|
@@ -189,11 +205,26 @@ function readRecentExternalCandidates(limit = 50) {
|
|
|
189
205
|
try {
|
|
190
206
|
const p = externalCandidatesPath();
|
|
191
207
|
if (!fs.existsSync(p)) return [];
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
208
|
+
const stat = fs.statSync(p);
|
|
209
|
+
if (stat.size < 1024 * 1024) {
|
|
210
|
+
const raw = fs.readFileSync(p, 'utf8');
|
|
211
|
+
const lines = raw.split('\n').map(l => l.trim()).filter(Boolean);
|
|
212
|
+
return lines.slice(-limit).map(l => {
|
|
213
|
+
try { return JSON.parse(l); } catch { return null; }
|
|
214
|
+
}).filter(Boolean);
|
|
215
|
+
}
|
|
216
|
+
const fd = fs.openSync(p, 'r');
|
|
217
|
+
try {
|
|
218
|
+
const chunkSize = Math.min(stat.size, limit * 4096);
|
|
219
|
+
const buf = Buffer.alloc(chunkSize);
|
|
220
|
+
fs.readSync(fd, buf, 0, chunkSize, stat.size - chunkSize);
|
|
221
|
+
const lines = buf.toString('utf8').split('\n').map(l => l.trim()).filter(Boolean);
|
|
222
|
+
return lines.slice(-limit).map(l => {
|
|
223
|
+
try { return JSON.parse(l); } catch { return null; }
|
|
224
|
+
}).filter(Boolean);
|
|
225
|
+
} finally {
|
|
226
|
+
fs.closeSync(fd);
|
|
227
|
+
}
|
|
197
228
|
} catch { return []; }
|
|
198
229
|
}
|
|
199
230
|
|
|
@@ -15,13 +15,28 @@ function captureEnvFingerprint() {
|
|
|
15
15
|
const repoRoot = getRepoRoot();
|
|
16
16
|
let pkgVersion = null;
|
|
17
17
|
let pkgName = null;
|
|
18
|
+
|
|
19
|
+
// Read evolver's own package.json via __dirname so that npm-installed
|
|
20
|
+
// deployments report the correct evolver version. getRepoRoot() walks
|
|
21
|
+
// up to the nearest .git directory, which resolves to the HOST project
|
|
22
|
+
// when evolver is an npm dependency -- producing a wrong name/version.
|
|
23
|
+
const ownPkgPath = path.resolve(__dirname, '..', '..', 'package.json');
|
|
18
24
|
try {
|
|
19
|
-
const raw = fs.readFileSync(
|
|
25
|
+
const raw = fs.readFileSync(ownPkgPath, 'utf8');
|
|
20
26
|
const pkg = JSON.parse(raw);
|
|
21
27
|
pkgVersion = pkg && pkg.version ? String(pkg.version) : null;
|
|
22
28
|
pkgName = pkg && pkg.name ? String(pkg.name) : null;
|
|
23
29
|
} catch (e) {}
|
|
24
30
|
|
|
31
|
+
if (!pkgVersion) {
|
|
32
|
+
try {
|
|
33
|
+
const raw = fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8');
|
|
34
|
+
const pkg = JSON.parse(raw);
|
|
35
|
+
pkgVersion = pkg && pkg.version ? String(pkg.version) : null;
|
|
36
|
+
pkgName = pkg && pkg.name ? String(pkg.name) : null;
|
|
37
|
+
} catch (e) {}
|
|
38
|
+
}
|
|
39
|
+
|
|
25
40
|
const region = (process.env.EVOLVER_REGION || '').trim().toLowerCase().slice(0, 5) || undefined;
|
|
26
41
|
|
|
27
42
|
return {
|
package/src/gep/paths.js
CHANGED
|
@@ -6,16 +6,33 @@ function getRepoRoot() {
|
|
|
6
6
|
return process.env.EVOLVER_REPO_ROOT;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
const ownDir = path.resolve(__dirname, '..', '..');
|
|
10
|
+
|
|
11
|
+
// Safety: check evolver's own directory first to prevent operating on a
|
|
12
|
+
// parent repo that happens to contain .git (which could cause data loss
|
|
13
|
+
// when git reset --hard runs in the wrong scope).
|
|
14
|
+
if (fs.existsSync(path.join(ownDir, '.git'))) {
|
|
15
|
+
return ownDir;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let dir = path.dirname(ownDir);
|
|
10
19
|
while (dir !== '/' && dir !== '.') {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
20
|
+
if (fs.existsSync(path.join(dir, '.git'))) {
|
|
21
|
+
if (process.env.EVOLVER_USE_PARENT_GIT === 'true') {
|
|
22
|
+
console.warn('[evolver] Using parent git repository at:', dir);
|
|
23
|
+
return dir;
|
|
24
|
+
}
|
|
25
|
+
console.warn(
|
|
26
|
+
'[evolver] Detected .git in parent directory', dir,
|
|
27
|
+
'-- ignoring. Set EVOLVER_USE_PARENT_GIT=true to override,',
|
|
28
|
+
'or EVOLVER_REPO_ROOT to specify the target directory explicitly.'
|
|
29
|
+
);
|
|
30
|
+
return ownDir;
|
|
14
31
|
}
|
|
15
32
|
dir = path.dirname(dir);
|
|
16
33
|
}
|
|
17
34
|
|
|
18
|
-
return
|
|
35
|
+
return ownDir;
|
|
19
36
|
}
|
|
20
37
|
|
|
21
38
|
function getWorkspaceRoot() {
|
|
@@ -223,7 +223,7 @@ function buildDistillationPrompt(analysis, existingGenes, sampleCapsules) {
|
|
|
223
223
|
});
|
|
224
224
|
|
|
225
225
|
return [
|
|
226
|
-
'You are a Gene synthesis engine for the GEP (
|
|
226
|
+
'You are a Gene synthesis engine for the GEP (Gene Expression Protocol).',
|
|
227
227
|
'',
|
|
228
228
|
'Analyze the following successful evolution capsules and extract a reusable Gene.',
|
|
229
229
|
'',
|
|
@@ -476,6 +476,22 @@ function completeDistillation(responseText) {
|
|
|
476
476
|
try { if (request.prompt_path) fs.unlinkSync(request.prompt_path); } catch (e) {}
|
|
477
477
|
|
|
478
478
|
console.log('[Distiller] Distillation complete. New gene: ' + gene.id);
|
|
479
|
+
|
|
480
|
+
if (process.env.SKILL_AUTO_PUBLISH !== '0') {
|
|
481
|
+
try {
|
|
482
|
+
var skillPublisher = require('./skillPublisher');
|
|
483
|
+
skillPublisher.publishSkillToHub(gene).then(function (res) {
|
|
484
|
+
if (res.ok) {
|
|
485
|
+
console.log('[Distiller] Skill published to Hub: ' + (res.result?.skill_id || gene.id));
|
|
486
|
+
} else {
|
|
487
|
+
console.warn('[Distiller] Skill publish failed: ' + (res.error || 'unknown'));
|
|
488
|
+
}
|
|
489
|
+
}).catch(function () {});
|
|
490
|
+
} catch (e) {
|
|
491
|
+
console.warn('[Distiller] Skill publisher unavailable: ' + e.message);
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
479
495
|
return { ok: true, gene: gene };
|
|
480
496
|
}
|
|
481
497
|
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var { getHubUrl, buildHubHeaders, getNodeId } = require('./a2aProtocol');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Convert a Gene object into SKILL.md format (Claude/Anthropic style).
|
|
7
|
+
*
|
|
8
|
+
* @param {object} gene - Gene asset
|
|
9
|
+
* @returns {string} SKILL.md content
|
|
10
|
+
*/
|
|
11
|
+
function geneToSkillMd(gene) {
|
|
12
|
+
var name = (gene.id || 'unnamed-skill').replace(/^gene_distilled_/, '').replace(/_/g, '-');
|
|
13
|
+
var desc = gene.summary || 'AI agent skill distilled from evolution experience.';
|
|
14
|
+
|
|
15
|
+
var lines = [
|
|
16
|
+
'---',
|
|
17
|
+
'name: ' + name,
|
|
18
|
+
'description: ' + desc,
|
|
19
|
+
'---',
|
|
20
|
+
'',
|
|
21
|
+
'# ' + name,
|
|
22
|
+
'',
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
if (gene.signals_match && gene.signals_match.length > 0) {
|
|
26
|
+
lines.push('## Trigger Signals');
|
|
27
|
+
lines.push('');
|
|
28
|
+
gene.signals_match.forEach(function (s) {
|
|
29
|
+
lines.push('- `' + s + '`');
|
|
30
|
+
});
|
|
31
|
+
lines.push('');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (gene.preconditions && gene.preconditions.length > 0) {
|
|
35
|
+
lines.push('## Preconditions');
|
|
36
|
+
lines.push('');
|
|
37
|
+
gene.preconditions.forEach(function (p) {
|
|
38
|
+
lines.push('- ' + p);
|
|
39
|
+
});
|
|
40
|
+
lines.push('');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (gene.strategy && gene.strategy.length > 0) {
|
|
44
|
+
lines.push('## Strategy');
|
|
45
|
+
lines.push('');
|
|
46
|
+
gene.strategy.forEach(function (step, i) {
|
|
47
|
+
lines.push((i + 1) + '. ' + step);
|
|
48
|
+
});
|
|
49
|
+
lines.push('');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (gene.constraints) {
|
|
53
|
+
lines.push('## Constraints');
|
|
54
|
+
lines.push('');
|
|
55
|
+
if (gene.constraints.max_files) {
|
|
56
|
+
lines.push('- Max files: ' + gene.constraints.max_files);
|
|
57
|
+
}
|
|
58
|
+
if (gene.constraints.forbidden_paths && gene.constraints.forbidden_paths.length > 0) {
|
|
59
|
+
lines.push('- Forbidden paths: ' + gene.constraints.forbidden_paths.join(', '));
|
|
60
|
+
}
|
|
61
|
+
lines.push('');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (gene.validation && gene.validation.length > 0) {
|
|
65
|
+
lines.push('## Validation');
|
|
66
|
+
lines.push('');
|
|
67
|
+
gene.validation.forEach(function (cmd) {
|
|
68
|
+
lines.push('```bash');
|
|
69
|
+
lines.push(cmd);
|
|
70
|
+
lines.push('```');
|
|
71
|
+
lines.push('');
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
lines.push('---');
|
|
76
|
+
lines.push('');
|
|
77
|
+
lines.push('*This Skill was generated by [Evolver](https://github.com/autogame-17/evolver) and is distributed under the [EvoMap Skill License (ESL-1.0)](https://evomap.ai/terms). Unauthorized redistribution, bulk scraping, or republishing is prohibited. See LICENSE file for full terms.*');
|
|
78
|
+
lines.push('');
|
|
79
|
+
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Publish a Gene as a Skill to the Hub skill store.
|
|
85
|
+
*
|
|
86
|
+
* @param {object} gene - Gene asset
|
|
87
|
+
* @param {object} [opts] - { category, tags }
|
|
88
|
+
* @returns {Promise<{ok: boolean, result?: object, error?: string}>}
|
|
89
|
+
*/
|
|
90
|
+
function publishSkillToHub(gene, opts) {
|
|
91
|
+
opts = opts || {};
|
|
92
|
+
var hubUrl = getHubUrl();
|
|
93
|
+
if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
|
|
94
|
+
|
|
95
|
+
var content = geneToSkillMd(gene);
|
|
96
|
+
var nodeId = getNodeId();
|
|
97
|
+
var skillId = 'skill_' + (gene.id || 'unnamed').replace(/^gene_/, '');
|
|
98
|
+
|
|
99
|
+
var body = {
|
|
100
|
+
sender_id: nodeId,
|
|
101
|
+
skill_id: skillId,
|
|
102
|
+
content: content,
|
|
103
|
+
category: opts.category || gene.category || null,
|
|
104
|
+
tags: opts.tags || gene.signals_match || [],
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
var endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/skill/store/publish';
|
|
108
|
+
|
|
109
|
+
return fetch(endpoint, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: buildHubHeaders(),
|
|
112
|
+
body: JSON.stringify(body),
|
|
113
|
+
signal: AbortSignal.timeout(15000),
|
|
114
|
+
})
|
|
115
|
+
.then(function (res) { return res.json().then(function (data) { return { status: res.status, data: data }; }); })
|
|
116
|
+
.then(function (result) {
|
|
117
|
+
if (result.status === 201 || result.status === 200) {
|
|
118
|
+
return { ok: true, result: result.data };
|
|
119
|
+
}
|
|
120
|
+
if (result.status === 409) {
|
|
121
|
+
return updateSkillOnHub(nodeId, skillId, content, opts, gene);
|
|
122
|
+
}
|
|
123
|
+
return { ok: false, error: result.data?.error || 'publish_failed', status: result.status };
|
|
124
|
+
})
|
|
125
|
+
.catch(function (err) {
|
|
126
|
+
return { ok: false, error: err.message };
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Update an existing Skill on the Hub (new version).
|
|
132
|
+
*/
|
|
133
|
+
function updateSkillOnHub(nodeId, skillId, content, opts, gene) {
|
|
134
|
+
var hubUrl = getHubUrl();
|
|
135
|
+
if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
|
|
136
|
+
|
|
137
|
+
var body = {
|
|
138
|
+
sender_id: nodeId,
|
|
139
|
+
skill_id: skillId,
|
|
140
|
+
content: content,
|
|
141
|
+
category: opts.category || gene.category || null,
|
|
142
|
+
tags: opts.tags || gene.signals_match || [],
|
|
143
|
+
changelog: 'Iterative evolution update',
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
var endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/skill/store/update';
|
|
147
|
+
|
|
148
|
+
return fetch(endpoint, {
|
|
149
|
+
method: 'PUT',
|
|
150
|
+
headers: buildHubHeaders(),
|
|
151
|
+
body: JSON.stringify(body),
|
|
152
|
+
signal: AbortSignal.timeout(15000),
|
|
153
|
+
})
|
|
154
|
+
.then(function (res) { return res.json(); })
|
|
155
|
+
.then(function (data) { return { ok: true, result: data }; })
|
|
156
|
+
.catch(function (err) { return { ok: false, error: err.message }; });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
module.exports = {
|
|
160
|
+
geneToSkillMd: geneToSkillMd,
|
|
161
|
+
publishSkillToHub: publishSkillToHub,
|
|
162
|
+
updateSkillOnHub: updateSkillOnHub,
|
|
163
|
+
};
|