@evomap/evolver 1.29.8 → 1.30.2
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 +19 -15
- package/package.json +1 -1
- package/src/evolve.js +159 -47
- package/src/gep/a2aProtocol.js +33 -0
- package/src/gep/candidates.js +5 -1
- package/src/gep/executionTrace.js +201 -0
- package/src/gep/hubSearch.js +152 -72
- package/src/gep/selector.js +55 -8
- package/src/gep/skillDistiller.js +128 -22
- package/src/gep/skillPublisher.js +142 -34
- package/src/gep/solidify.js +21 -1
|
@@ -3,54 +3,90 @@
|
|
|
3
3
|
var { getHubUrl, buildHubHeaders, getNodeId } = require('./a2aProtocol');
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* @param {object} gene - Gene asset
|
|
9
|
-
* @returns {string} SKILL.md content
|
|
6
|
+
* Sanitize a raw gene id into a human-readable kebab-case skill name.
|
|
7
|
+
* Returns null if the name is unsalvageable (pure numbers, tool name, etc.).
|
|
10
8
|
*/
|
|
11
9
|
function sanitizeSkillName(rawName) {
|
|
12
10
|
var name = rawName.replace(/[\r\n]+/g, '-').replace(/^gene_distilled_/, '').replace(/^gene_/, '').replace(/_/g, '-');
|
|
11
|
+
// Strip ALL embedded timestamps (10+ digit sequences) anywhere in the name
|
|
12
|
+
name = name.replace(/-?\d{10,}-?/g, '-').replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
13
13
|
if (/^\d{8,}/.test(name) || /^(cursor|vscode|vim|emacs|windsurf|copilot|cline|codex)[-]?\d*$/i.test(name)) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
|
-
name = name.replace(/-?\d{10,}$/g, '').replace(/-+$/, '');
|
|
17
16
|
if (name.replace(/[-]/g, '').length < 6) return null;
|
|
18
17
|
return name;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Derive a Title Case display name from a kebab-case skill name.
|
|
22
|
+
* "retry-with-backoff" -> "Retry With Backoff"
|
|
23
|
+
*/
|
|
24
|
+
function toTitleCase(kebabName) {
|
|
25
|
+
return kebabName.split('-').map(function (w) {
|
|
26
|
+
if (!w) return '';
|
|
27
|
+
return w.charAt(0).toUpperCase() + w.slice(1);
|
|
28
|
+
}).join(' ');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Derive fallback name words from gene signals/summary when id is not usable.
|
|
33
|
+
*/
|
|
34
|
+
function deriveFallbackName(gene) {
|
|
35
|
+
var fallbackWords = [];
|
|
36
|
+
var STOP = new Set(['the', 'and', 'for', 'with', 'from', 'that', 'this', 'into', 'when', 'are', 'was', 'has', 'had', 'not', 'but', 'its']);
|
|
37
|
+
if (Array.isArray(gene.signals_match)) {
|
|
38
|
+
gene.signals_match.slice(0, 3).forEach(function (s) {
|
|
39
|
+
String(s).toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim().split(/\s+/).forEach(function (w) {
|
|
40
|
+
if (w.length >= 3 && !STOP.has(w) && fallbackWords.length < 5) fallbackWords.push(w);
|
|
36
41
|
});
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
if (fallbackWords.length < 2 && gene.summary) {
|
|
45
|
+
String(gene.summary).toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim().split(/\s+/).forEach(function (w) {
|
|
46
|
+
if (w.length >= 3 && !STOP.has(w) && fallbackWords.length < 5) fallbackWords.push(w);
|
|
47
|
+
});
|
|
41
48
|
}
|
|
42
|
-
var
|
|
49
|
+
var seen = {};
|
|
50
|
+
fallbackWords = fallbackWords.filter(function (w) { if (seen[w]) return false; seen[w] = true; return true; });
|
|
51
|
+
return fallbackWords.length >= 2 ? fallbackWords.join('-') : 'auto-distilled-skill';
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Convert a Gene object into SKILL.md format -- marketplace-quality content.
|
|
56
|
+
*
|
|
57
|
+
* @param {object} gene - Gene asset
|
|
58
|
+
* @returns {string} SKILL.md content
|
|
59
|
+
*/
|
|
60
|
+
function geneToSkillMd(gene) {
|
|
61
|
+
var rawName = gene.id || 'unnamed-skill';
|
|
62
|
+
var name = sanitizeSkillName(rawName) || deriveFallbackName(gene);
|
|
63
|
+
var displayName = toTitleCase(name);
|
|
64
|
+
var desc = (gene.summary || '').replace(/[\r\n]+/g, ' ').replace(/\s*\d{10,}\s*$/g, '').trim();
|
|
65
|
+
if (!desc || desc.length < 10) desc = 'AI agent skill distilled from evolution experience.';
|
|
43
66
|
|
|
44
67
|
var lines = [
|
|
45
68
|
'---',
|
|
46
|
-
'name: ' +
|
|
69
|
+
'name: ' + displayName,
|
|
47
70
|
'description: ' + desc,
|
|
48
71
|
'---',
|
|
49
72
|
'',
|
|
50
|
-
'# ' +
|
|
73
|
+
'# ' + displayName,
|
|
74
|
+
'',
|
|
75
|
+
desc,
|
|
51
76
|
'',
|
|
52
77
|
];
|
|
53
78
|
|
|
79
|
+
// -- When to Use (derived from signals; preconditions go in their own section) --
|
|
80
|
+
if (gene.signals_match && gene.signals_match.length > 0) {
|
|
81
|
+
lines.push('## When to Use');
|
|
82
|
+
lines.push('');
|
|
83
|
+
lines.push('- When your project encounters: ' + gene.signals_match.slice(0, 4).map(function (s) {
|
|
84
|
+
return '`' + s + '`';
|
|
85
|
+
}).join(', '));
|
|
86
|
+
lines.push('');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// -- Trigger Signals --
|
|
54
90
|
if (gene.signals_match && gene.signals_match.length > 0) {
|
|
55
91
|
lines.push('## Trigger Signals');
|
|
56
92
|
lines.push('');
|
|
@@ -60,6 +96,7 @@ function geneToSkillMd(gene) {
|
|
|
60
96
|
lines.push('');
|
|
61
97
|
}
|
|
62
98
|
|
|
99
|
+
// -- Preconditions --
|
|
63
100
|
if (gene.preconditions && gene.preconditions.length > 0) {
|
|
64
101
|
lines.push('## Preconditions');
|
|
65
102
|
lines.push('');
|
|
@@ -69,27 +106,36 @@ function geneToSkillMd(gene) {
|
|
|
69
106
|
lines.push('');
|
|
70
107
|
}
|
|
71
108
|
|
|
109
|
+
// -- Strategy --
|
|
72
110
|
if (gene.strategy && gene.strategy.length > 0) {
|
|
73
111
|
lines.push('## Strategy');
|
|
74
112
|
lines.push('');
|
|
75
113
|
gene.strategy.forEach(function (step, i) {
|
|
76
|
-
|
|
114
|
+
var text = String(step);
|
|
115
|
+
var verb = extractStepVerb(text);
|
|
116
|
+
if (verb) {
|
|
117
|
+
lines.push((i + 1) + '. **' + verb + '** -- ' + stripLeadingVerb(text));
|
|
118
|
+
} else {
|
|
119
|
+
lines.push((i + 1) + '. ' + text);
|
|
120
|
+
}
|
|
77
121
|
});
|
|
78
122
|
lines.push('');
|
|
79
123
|
}
|
|
80
124
|
|
|
125
|
+
// -- Constraints --
|
|
81
126
|
if (gene.constraints) {
|
|
82
127
|
lines.push('## Constraints');
|
|
83
128
|
lines.push('');
|
|
84
129
|
if (gene.constraints.max_files) {
|
|
85
|
-
lines.push('- Max files: ' + gene.constraints.max_files);
|
|
130
|
+
lines.push('- Max files per invocation: ' + gene.constraints.max_files);
|
|
86
131
|
}
|
|
87
132
|
if (gene.constraints.forbidden_paths && gene.constraints.forbidden_paths.length > 0) {
|
|
88
|
-
lines.push('- Forbidden paths: ' + gene.constraints.forbidden_paths.join(', '));
|
|
133
|
+
lines.push('- Forbidden paths: ' + gene.constraints.forbidden_paths.map(function (p) { return '`' + p + '`'; }).join(', '));
|
|
89
134
|
}
|
|
90
135
|
lines.push('');
|
|
91
136
|
}
|
|
92
137
|
|
|
138
|
+
// -- Validation --
|
|
93
139
|
if (gene.validation && gene.validation.length > 0) {
|
|
94
140
|
lines.push('## Validation');
|
|
95
141
|
lines.push('');
|
|
@@ -101,6 +147,16 @@ function geneToSkillMd(gene) {
|
|
|
101
147
|
});
|
|
102
148
|
}
|
|
103
149
|
|
|
150
|
+
// -- Metadata --
|
|
151
|
+
lines.push('## Metadata');
|
|
152
|
+
lines.push('');
|
|
153
|
+
lines.push('- Category: `' + (gene.category || 'innovate') + '`');
|
|
154
|
+
lines.push('- Schema version: `' + (gene.schema_version || '1.6.0') + '`');
|
|
155
|
+
if (gene._distilled_meta && gene._distilled_meta.source_capsule_count) {
|
|
156
|
+
lines.push('- Distilled from: ' + gene._distilled_meta.source_capsule_count + ' successful capsules');
|
|
157
|
+
}
|
|
158
|
+
lines.push('');
|
|
159
|
+
|
|
104
160
|
lines.push('---');
|
|
105
161
|
lines.push('');
|
|
106
162
|
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.*');
|
|
@@ -109,6 +165,31 @@ function geneToSkillMd(gene) {
|
|
|
109
165
|
return lines.join('\n');
|
|
110
166
|
}
|
|
111
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Extract the leading verb from a strategy step for bolding.
|
|
170
|
+
* Only extracts a single verb to avoid splitting compound phrases.
|
|
171
|
+
* e.g. "Verify Cursor CLI installation" -> "Verify"
|
|
172
|
+
* "Run `npm test` to check" -> "Run"
|
|
173
|
+
* "Configure non-interactive mode" -> "Configure"
|
|
174
|
+
*/
|
|
175
|
+
function extractStepVerb(step) {
|
|
176
|
+
// Only match a capitalized verb at the very start (no leading backtick/special chars)
|
|
177
|
+
var match = step.match(/^([A-Z][a-z]+)/);
|
|
178
|
+
return match ? match[1] : '';
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Remove the leading verb from a step (already shown in bold).
|
|
183
|
+
*/
|
|
184
|
+
function stripLeadingVerb(step) {
|
|
185
|
+
var verb = extractStepVerb(step);
|
|
186
|
+
if (verb && step.startsWith(verb)) {
|
|
187
|
+
var rest = step.slice(verb.length).replace(/^[\s:.\-]+/, '');
|
|
188
|
+
return rest || step;
|
|
189
|
+
}
|
|
190
|
+
return step;
|
|
191
|
+
}
|
|
192
|
+
|
|
112
193
|
/**
|
|
113
194
|
* Publish a Gene as a Skill to the Hub skill store.
|
|
114
195
|
*
|
|
@@ -121,18 +202,37 @@ function publishSkillToHub(gene, opts) {
|
|
|
121
202
|
var hubUrl = getHubUrl();
|
|
122
203
|
if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
|
|
123
204
|
|
|
124
|
-
|
|
205
|
+
// Shallow-copy gene to avoid mutating the caller's object
|
|
206
|
+
var geneCopy = {};
|
|
207
|
+
Object.keys(gene).forEach(function (k) { geneCopy[k] = gene[k]; });
|
|
208
|
+
if (Array.isArray(geneCopy.signals_match)) {
|
|
209
|
+
try {
|
|
210
|
+
var distiller = require('./skillDistiller');
|
|
211
|
+
geneCopy.signals_match = distiller.sanitizeSignalsMatch(geneCopy.signals_match);
|
|
212
|
+
} catch (e) { /* distiller not available, skip */ }
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
var content = geneToSkillMd(geneCopy);
|
|
125
216
|
var nodeId = getNodeId();
|
|
126
217
|
var fmName = content.match(/^name:\s*(.+)$/m);
|
|
127
218
|
var derivedName = fmName ? fmName[1].trim().toLowerCase().replace(/[^a-z0-9]+/g, '_') : (gene.id || 'unnamed').replace(/^gene_/, '');
|
|
219
|
+
// Strip ALL embedded timestamps from skillId
|
|
220
|
+
derivedName = derivedName.replace(/_?\d{10,}_?/g, '_').replace(/_+/g, '_').replace(/^_|_$/g, '');
|
|
128
221
|
var skillId = 'skill_' + derivedName;
|
|
129
222
|
|
|
223
|
+
// Clean tags: use already-sanitized signals from geneCopy
|
|
224
|
+
var tags = opts.tags || geneCopy.signals_match || [];
|
|
225
|
+
tags = tags.filter(function (t) {
|
|
226
|
+
var s = String(t || '').trim();
|
|
227
|
+
return s.length >= 3 && !/^\d+$/.test(s) && !/\d{10,}/.test(s);
|
|
228
|
+
});
|
|
229
|
+
|
|
130
230
|
var body = {
|
|
131
231
|
sender_id: nodeId,
|
|
132
232
|
skill_id: skillId,
|
|
133
233
|
content: content,
|
|
134
|
-
category: opts.category ||
|
|
135
|
-
tags:
|
|
234
|
+
category: opts.category || geneCopy.category || null,
|
|
235
|
+
tags: tags,
|
|
136
236
|
};
|
|
137
237
|
|
|
138
238
|
var endpoint = hubUrl.replace(/\/+$/, '') + '/a2a/skill/store/publish';
|
|
@@ -165,12 +265,18 @@ function updateSkillOnHub(nodeId, skillId, content, opts, gene) {
|
|
|
165
265
|
var hubUrl = getHubUrl();
|
|
166
266
|
if (!hubUrl) return Promise.resolve({ ok: false, error: 'no_hub_url' });
|
|
167
267
|
|
|
268
|
+
var tags = opts.tags || gene.signals_match || [];
|
|
269
|
+
tags = tags.filter(function (t) {
|
|
270
|
+
var s = String(t || '').trim();
|
|
271
|
+
return s.length >= 3 && !/^\d+$/.test(s) && !/\d{10,}/.test(s);
|
|
272
|
+
});
|
|
273
|
+
|
|
168
274
|
var body = {
|
|
169
275
|
sender_id: nodeId,
|
|
170
276
|
skill_id: skillId,
|
|
171
277
|
content: content,
|
|
172
278
|
category: opts.category || gene.category || null,
|
|
173
|
-
tags:
|
|
279
|
+
tags: tags,
|
|
174
280
|
changelog: 'Iterative evolution update',
|
|
175
281
|
};
|
|
176
282
|
|
|
@@ -196,4 +302,6 @@ module.exports = {
|
|
|
196
302
|
geneToSkillMd: geneToSkillMd,
|
|
197
303
|
publishSkillToHub: publishSkillToHub,
|
|
198
304
|
updateSkillOnHub: updateSkillOnHub,
|
|
305
|
+
sanitizeSkillName: sanitizeSkillName,
|
|
306
|
+
toTitleCase: toTitleCase,
|
|
199
307
|
};
|
package/src/gep/solidify.js
CHANGED
|
@@ -20,6 +20,7 @@ const { buildValidationReport } = require('./validationReport');
|
|
|
20
20
|
const { logAssetCall } = require('./assetCallLog');
|
|
21
21
|
const { recordNarrative } = require('./narrativeMemory');
|
|
22
22
|
const { isLlmReviewEnabled, runLlmReview } = require('./llmReview');
|
|
23
|
+
const { buildExecutionTrace } = require('./executionTrace');
|
|
23
24
|
|
|
24
25
|
function nowIso() {
|
|
25
26
|
return new Date().toISOString();
|
|
@@ -1225,6 +1226,22 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
|
|
|
1225
1226
|
memory_graph: memoryGraphPath(),
|
|
1226
1227
|
},
|
|
1227
1228
|
};
|
|
1229
|
+
// Build desensitized execution trace for cross-agent experience sharing
|
|
1230
|
+
const executionTrace = buildExecutionTrace({
|
|
1231
|
+
gene: geneUsed,
|
|
1232
|
+
mutation,
|
|
1233
|
+
signals,
|
|
1234
|
+
blast,
|
|
1235
|
+
constraintCheck,
|
|
1236
|
+
validation,
|
|
1237
|
+
canary,
|
|
1238
|
+
outcomeStatus,
|
|
1239
|
+
startedAt: validation.startedAt,
|
|
1240
|
+
});
|
|
1241
|
+
if (executionTrace) {
|
|
1242
|
+
event.execution_trace = executionTrace;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1228
1245
|
event.asset_id = computeAssetId(event);
|
|
1229
1246
|
|
|
1230
1247
|
let capsule = null;
|
|
@@ -1350,7 +1367,10 @@ function solidify({ intent, summary, dryRun = false, rollbackOnFailure = true }
|
|
|
1350
1367
|
state.last_solidify = {
|
|
1351
1368
|
run_id: runId, at: ts, event_id: event.id, capsule_id: capsuleId, outcome: event.outcome,
|
|
1352
1369
|
};
|
|
1353
|
-
if (!dryRun)
|
|
1370
|
+
if (!dryRun) {
|
|
1371
|
+
state.solidify_count = (state.solidify_count || 0) + 1;
|
|
1372
|
+
writeStateForSolidify(state);
|
|
1373
|
+
}
|
|
1354
1374
|
|
|
1355
1375
|
if (!dryRun) {
|
|
1356
1376
|
try {
|