@evomap/evolver-core 2.0.0-beta.8 → 2.0.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/dist/algo/candidateAssembly.d.ts +7 -2
- package/dist/algo/candidateAssembly.js +81 -13
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/conversationSniffer.js +20 -7
- package/dist/algo/cycleEngine.d.ts +23 -2
- package/dist/algo/cycleEngine.js +236 -36
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +2 -2
- package/dist/algo/geneHealth.js +5 -4
- package/dist/algo/geneSelection.d.ts +63 -5
- package/dist/algo/geneSelection.js +330 -30
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +2 -0
- package/dist/algo/orchestrator.d.ts +8 -1
- package/dist/algo/orchestrator.js +15 -3
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +52 -0
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetstore/assetSidecarRecords.d.ts +7 -2
- package/dist/assetstore/assetSidecarRecords.js +238 -0
- package/dist/assetstore/assetSidecarRecovery.js +2 -2
- package/dist/assetstore/assetStoreHealth.d.ts +6 -0
- package/dist/assetstore/assetStoreHealth.js +56 -14
- package/dist/assetstore/assetStoreStorage.d.ts +1 -1
- package/dist/assetstore/assetStoreStorage.js +27 -9
- package/dist/assetstore/assetSyncLedger.d.ts +86 -0
- package/dist/assetstore/assetSyncLedger.js +683 -4
- package/dist/assetstore/index.d.ts +1 -0
- package/dist/assetstore/index.js +1 -0
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +4 -2
- package/dist/assetstore/localJsonl.js +91 -11
- package/dist/assetstore/provenance.d.ts +80 -4
- package/dist/assetstore/provenance.js +313 -5
- package/dist/assetstore/provider.d.ts +58 -1
- package/dist/assetstore/provider.js +97 -6
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/v1EnvCompat.d.ts +111 -0
- package/dist/bootstrap/v1EnvCompat.js +280 -0
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +1 -0
- package/dist/events/ingest.js +1 -0
- package/dist/events/paths.d.ts +3 -1
- package/dist/events/paths.js +4 -0
- package/dist/events/public.d.ts +2 -2
- package/dist/events/public.js +2 -2
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +43 -2
- package/dist/exec/autoExec.js +76 -9
- package/dist/exec/autonomousCycle.d.ts +22 -4
- package/dist/exec/autonomousCycle.js +64 -13
- package/dist/exec/claudeBridge.d.ts +32 -7
- package/dist/exec/claudeBridge.js +281 -29
- package/dist/exec/prompt.js +5 -1
- package/dist/exec/runnerRegistry.d.ts +68 -26
- package/dist/exec/runnerRegistry.js +307 -72
- package/dist/exec/selfPr.js +1 -7
- package/dist/feedback/envelope.d.ts +61 -0
- package/dist/feedback/envelope.js +168 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/hooks/hooks.js +1 -0
- package/dist/hub/assetCallLog.d.ts +35 -1
- package/dist/hub/assetCallLog.js +124 -1
- package/dist/hub/bindings.d.ts +8 -1
- package/dist/hub/bindings.js +17 -6
- package/dist/hub/capability.d.ts +90 -3
- package/dist/hub/fake.d.ts +2 -2
- package/dist/hub/fake.js +1 -1
- package/dist/hub/questionGenerator.d.ts +5 -1
- package/dist/hub/questionGenerator.js +8 -6
- package/dist/hub/sanitize.js +3 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -1
- package/dist/mailbox/dispatch.d.ts +1 -1
- package/dist/mailbox/dispatch.js +22 -6
- package/dist/mailbox/envelope.d.ts +7 -1
- package/dist/mailbox/envelope.js +9 -2
- package/dist/mailbox/ipcServer.d.ts +10 -2
- package/dist/mailbox/ipcServer.js +163 -13
- package/dist/mailbox/store.d.ts +89 -3
- package/dist/mailbox/store.js +895 -41
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/selfUpdate.d.ts +8 -0
- package/dist/ops/selfUpdate.js +24 -8
- package/dist/personality/schema.d.ts +16 -16
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/signal.d.ts +3 -3
- package/dist/schema/signal.js +1 -1
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +202 -0
- package/dist/signals/cycleHistoryFromEvents.js +17 -8
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +169 -1
- package/dist/signals/extractor.d.ts +2 -2
- package/dist/signals/extractor.js +31 -6
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +3 -1
- package/dist/signals/metaSignals.d.ts +4 -0
- package/dist/signals/metaSignals.js +42 -0
- package/dist/signals/signalGate.js +1 -1
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/strategy/constraintAblation.d.ts +64 -0
- package/dist/strategy/constraintAblation.js +2820 -0
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +339 -0
- package/dist/strategy/index.d.ts +2 -1
- package/dist/strategy/index.js +2 -1
- package/dist/trace/index.d.ts +3 -1
- package/dist/trace/index.js +3 -1
- package/dist/trace/learningTrace.d.ts +209 -0
- package/dist/trace/learningTrace.js +276 -0
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/verify/sandboxRunner.d.ts +28 -0
- package/dist/verify/sandboxRunner.js +218 -19
- package/dist/verify/sandboxedValidation.d.ts +9 -0
- package/dist/verify/sandboxedValidation.js +113 -13
- package/dist/verify/validation.d.ts +11 -1
- package/dist/verify/validation.js +31 -0
- package/package.json +5 -2
package/dist/signals/expand.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
// Signal expansion & tag classification (ported from v1 src/gep/learningSignals.js).
|
|
2
3
|
// Raw signals (e.g. '429') are expanded into broader semantic tags (e.g. 'problem:reliability',
|
|
3
4
|
// 'action:repair') so gene selection can match by MEANING, not just literal string intersection —
|
|
@@ -8,6 +9,18 @@
|
|
|
8
9
|
// reach an English-keyworded gene through tagOverlapScore. This is the v2-native port of v1 #99's
|
|
9
10
|
// multilingual signals_match aliases: v1 attached `|`-pipe synonyms to shipped seed genes, but v2 has
|
|
10
11
|
// no seed-gene catalog and no pipe-alias parsing — its expansion rules ARE the shared signal vocabulary.
|
|
12
|
+
const SUCCESS_OUTCOME_TAGS = ['signal:success', 'action:optimize', 'action:innovate'];
|
|
13
|
+
const EXPLICIT_SUCCESS_SIGNALS = new Set([
|
|
14
|
+
'issue_already_resolved',
|
|
15
|
+
'issue_resolved',
|
|
16
|
+
'openclaw_self_healed',
|
|
17
|
+
'resolved',
|
|
18
|
+
'self_healed',
|
|
19
|
+
'stable_success_plateau',
|
|
20
|
+
'success_prose',
|
|
21
|
+
'verified-success',
|
|
22
|
+
'verified_success',
|
|
23
|
+
]);
|
|
11
24
|
const EXPANSION_RULES = [
|
|
12
25
|
{ re: /(error|exception|failed|unstable|log_error|runtime|429|错误|异常|エラー|오류|例外|예외|失败|失敗|실패|不稳定|不安定|불안정)/, tags: ['problem:reliability', 'action:repair'] },
|
|
13
26
|
// Korean 감사 alone is a homograph (audit == "thank you"), so it is matched only via compounds / audit-context
|
|
@@ -19,13 +32,28 @@ const EXPANSION_RULES = [
|
|
|
19
32
|
// 功能/機能/기능 alone are too broad: as unanchored substrings they match the malfunction vocabulary
|
|
20
33
|
// (機能不全 / 기능장애 / 功能障碍), which are reliability problems — use the v1 #99 feature-request compounds.
|
|
21
34
|
{ re: /(feature|capability_gap|user_feature_request|external_opportunity|stagnation recommendation|功能请求|機能リクエスト|기능요청|能力缺口|機能ギャップ|역량공백|改进建议|改善提案|개선제안|外部机会|外部機会|외부기회)/, tags: ['problem:capability', 'action:innovate'] },
|
|
22
|
-
|
|
35
|
+
// Negative lookbehind on `plateau`: `stable_success_plateau` contains the substring `plateau`, but it is a
|
|
36
|
+
// SUCCESS signal (#578), not a stagnation signal. Without (?<!success_) the stagnation rule would fire on it,
|
|
37
|
+
// producing contradictory tags (problem:stagnation + signal:success) in a single expandSignals pass.
|
|
38
|
+
{ re: /(stagnation|(?<!success_)plateau|steady_state|saturation|empty_cycle_loop|loop_detected|recurring)/, tags: ['problem:stagnation', 'action:innovate'] },
|
|
23
39
|
{ re: /(task|worker|heartbeat|hub|commitment|assignment|orchestration)/, tags: ['area:orchestration'] },
|
|
24
40
|
// Tool-integrity (ported from v1 #99 gene_tool_integrity): bypassing a registered tool or looping on raw
|
|
25
41
|
// shell is an orchestration-discipline / validation risk. CN/JA/KO aliases included for recall parity.
|
|
26
42
|
{ re: /(tool_bypass|tool_loop|工具绕过|工具循环|ツール迂回|ツールループ|도구우회|도구반복)/, tags: ['area:orchestration', 'risk:validation'] },
|
|
27
43
|
{ re: /(memory|narrative|reflection)/, tags: ['area:memory'] },
|
|
28
44
|
{ re: /(skill|dashboard)/, tags: ['area:skills'] },
|
|
45
|
+
// Harness context budget (v1 context-compression gene family port): an agent harness that injects large
|
|
46
|
+
// tool/MCP schemas, long skill manuals, agent-type descriptions, a memory index, or a pasted transcript can
|
|
47
|
+
// exhaust the model's context window before the task starts. That is a prompt-assembly (area:prompt) cost
|
|
48
|
+
// problem, and shrinking it is an optimize action — NOT a reliability failure. v2 has no seed-gene catalog,
|
|
49
|
+
// so these rules ARE the shared vocabulary that lets a context-bloat signal reach a compression strategy.
|
|
50
|
+
{ re: /(context_bloat|context_explosion|context window|token_budget|prompt_budget|上下文爆|上下文预算|token 超限|token超限)/, tags: ['problem:context_budget', 'action:optimize', 'area:prompt'] },
|
|
51
|
+
// Tool/MCP schema weight is the always-on half of the budget; lazy-loading it is the corrective action.
|
|
52
|
+
{ re: /(tool_schema|schema_bloat|mcp_tool_schema|lazy_load_schema|just-in-time schema|工具 schema|工具schema)/, tags: ['problem:context_budget', 'action:optimize', 'area:prompt', 'area:orchestration'] },
|
|
53
|
+
// Skill manuals / agent-type descriptions are the other always-on half (area:skills already exists above).
|
|
54
|
+
{ re: /(skill_list_bloat|skill_manual_bloat|agent type description|mcp server instructions|skill 列表太长|技能列表太长)/, tags: ['problem:context_budget', 'action:optimize', 'area:skills', 'area:prompt'] },
|
|
55
|
+
// A pasted transcript / session handoff is the caller-supplied half — compressing it is a memory-shaped concern.
|
|
56
|
+
{ re: /(transcript_bloat|transcript_context_bloat|conversation_handoff|session handoff|pasted transcript|会话上下文就爆|完整转录)/, tags: ['problem:context_budget', 'action:optimize', 'area:memory'] },
|
|
29
57
|
{ re: /(validation|canary|rollback|constraint|blast radius|destructive)/, tags: ['risk:validation'] },
|
|
30
58
|
];
|
|
31
59
|
/**
|
|
@@ -41,6 +69,11 @@ export function expandSignals(signals, extraText = '') {
|
|
|
41
69
|
if (base && base !== str)
|
|
42
70
|
tags.add(base); // namespace prefix (e.g. 'auth:token' → 'auth')
|
|
43
71
|
}
|
|
72
|
+
// #578 success markers are structured signals; prose inference accepts negated forms too easily.
|
|
73
|
+
if (signals.some((signal) => EXPLICIT_SUCCESS_SIGNALS.has(String(signal).trim().toLowerCase().normalize('NFKC')))) {
|
|
74
|
+
for (const tag of SUCCESS_OUTCOME_TAGS)
|
|
75
|
+
tags.add(tag);
|
|
76
|
+
}
|
|
44
77
|
const text = (signals.join(' ') + ' ' + extraText).toLowerCase().normalize('NFKC');
|
|
45
78
|
for (const rule of EXPANSION_RULES) {
|
|
46
79
|
if (rule.re.test(text))
|
|
@@ -122,4 +155,139 @@ export function tagOverlapScore(signals, gene) {
|
|
|
122
155
|
if (geneTagSet.has(t))
|
|
123
156
|
hits++;
|
|
124
157
|
return hits / sigTags.length;
|
|
158
|
+
}
|
|
159
|
+
const SEMANTIC_IDF_DOCUMENT_LIMIT = 1_000;
|
|
160
|
+
const SEMANTIC_IDF_TEXT_CHARS_PER_DOCUMENT = 4_096;
|
|
161
|
+
const SEMANTIC_IDF_TAG_INPUTS_PER_DOCUMENT = 256;
|
|
162
|
+
const SEMANTIC_IDF_TAG_CHARS = 1_024;
|
|
163
|
+
const SEMANTIC_IDF_TAGS_PER_DOCUMENT = 64;
|
|
164
|
+
const SEMANTIC_IDF_TOKENS_PER_DOCUMENT = 128;
|
|
165
|
+
const SEMANTIC_IDF_TAG_VOCABULARY_LIMIT = 8_192;
|
|
166
|
+
const SEMANTIC_IDF_TOKEN_VOCABULARY_LIMIT = 32_768;
|
|
167
|
+
function normalizeSemanticTag(tag) {
|
|
168
|
+
return tag.normalize('NFKC').trim().toLowerCase();
|
|
169
|
+
}
|
|
170
|
+
function inverseDocumentFrequency(documentCount, documentFrequency) {
|
|
171
|
+
return Math.log(documentCount / documentFrequency);
|
|
172
|
+
}
|
|
173
|
+
function tokenFrequency(text) {
|
|
174
|
+
const frequencies = new Map();
|
|
175
|
+
for (const token of tokenize(text)) {
|
|
176
|
+
frequencies.set(token, (frequencies.get(token) ?? 0) + 1);
|
|
177
|
+
}
|
|
178
|
+
return frequencies;
|
|
179
|
+
}
|
|
180
|
+
function compareCodeUnits(left, right) {
|
|
181
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
182
|
+
}
|
|
183
|
+
function boundedUnique(values, limit) {
|
|
184
|
+
return [...new Set(values.filter(Boolean))].sort(compareCodeUnits).slice(0, limit);
|
|
185
|
+
}
|
|
186
|
+
function boundedVocabulary(valuesByDocument, limit) {
|
|
187
|
+
const vocabulary = new Set();
|
|
188
|
+
for (const values of valuesByDocument) {
|
|
189
|
+
for (const value of values)
|
|
190
|
+
vocabulary.add(value);
|
|
191
|
+
}
|
|
192
|
+
// Selecting by code-unit order makes the cap independent of corpus/document order. The source set is itself
|
|
193
|
+
// bounded by the per-document and document-count limits above, so this sort cannot grow without bound.
|
|
194
|
+
return new Set([...vocabulary].sort(compareCodeUnits).slice(0, limit));
|
|
195
|
+
}
|
|
196
|
+
function semanticProfileVersion(documentCount, tagDocumentFrequency, tokenDocumentFrequency) {
|
|
197
|
+
// Hash only normalized document-frequency entries so decision telemetry never contains plaintext corpus data.
|
|
198
|
+
// A 128-bit SHA-256 prefix keeps the identifier compact while avoiding the collision rate of a 32-bit checksum.
|
|
199
|
+
const hash = createHash('sha256');
|
|
200
|
+
hash.update(`documents:${documentCount};`, 'utf8');
|
|
201
|
+
for (const [tag, frequency] of [...tagDocumentFrequency].sort(([left], [right]) => compareCodeUnits(left, right))) {
|
|
202
|
+
hash.update(`tag:${tag.length}:${tag}:${frequency};`, 'utf8');
|
|
203
|
+
}
|
|
204
|
+
for (const [token, frequency] of [...tokenDocumentFrequency].sort(([left], [right]) => compareCodeUnits(left, right))) {
|
|
205
|
+
hash.update(`token:${token.length}:${token}:${frequency};`, 'utf8');
|
|
206
|
+
}
|
|
207
|
+
return `idf-2:n=${documentCount};tags=${tagDocumentFrequency.size};tokens=${tokenDocumentFrequency.size};sha256=${hash.digest('hex').slice(0, 32)}`;
|
|
208
|
+
}
|
|
209
|
+
export function buildSemanticIdfProfile(documents) {
|
|
210
|
+
const boundedDocuments = documents.slice(0, SEMANTIC_IDF_DOCUMENT_LIMIT).map((document) => ({
|
|
211
|
+
tags: boundedUnique(document.tags
|
|
212
|
+
.slice(0, SEMANTIC_IDF_TAG_INPUTS_PER_DOCUMENT)
|
|
213
|
+
.map((tag) => normalizeSemanticTag(tag.slice(0, SEMANTIC_IDF_TAG_CHARS))), SEMANTIC_IDF_TAGS_PER_DOCUMENT),
|
|
214
|
+
tokens: boundedUnique(tokenize(document.text.slice(0, SEMANTIC_IDF_TEXT_CHARS_PER_DOCUMENT)), SEMANTIC_IDF_TOKENS_PER_DOCUMENT),
|
|
215
|
+
}));
|
|
216
|
+
const tagVocabulary = boundedVocabulary(boundedDocuments.map((document) => document.tags), SEMANTIC_IDF_TAG_VOCABULARY_LIMIT);
|
|
217
|
+
const tokenVocabulary = boundedVocabulary(boundedDocuments.map((document) => document.tokens), SEMANTIC_IDF_TOKEN_VOCABULARY_LIMIT);
|
|
218
|
+
const tagDocumentFrequency = new Map();
|
|
219
|
+
const tokenDocumentFrequency = new Map();
|
|
220
|
+
for (const document of boundedDocuments) {
|
|
221
|
+
for (const tag of document.tags) {
|
|
222
|
+
if (!tagVocabulary.has(tag))
|
|
223
|
+
continue;
|
|
224
|
+
tagDocumentFrequency.set(tag, (tagDocumentFrequency.get(tag) ?? 0) + 1);
|
|
225
|
+
}
|
|
226
|
+
for (const token of document.tokens) {
|
|
227
|
+
if (!tokenVocabulary.has(token))
|
|
228
|
+
continue;
|
|
229
|
+
tokenDocumentFrequency.set(token, (tokenDocumentFrequency.get(token) ?? 0) + 1);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
const documentCount = boundedDocuments.length;
|
|
233
|
+
const tagIdf = new Map();
|
|
234
|
+
const tokenIdf = new Map();
|
|
235
|
+
for (const [tag, frequency] of tagDocumentFrequency) {
|
|
236
|
+
tagIdf.set(tag, inverseDocumentFrequency(documentCount, frequency));
|
|
237
|
+
}
|
|
238
|
+
for (const [token, frequency] of tokenDocumentFrequency) {
|
|
239
|
+
tokenIdf.set(token, inverseDocumentFrequency(documentCount, frequency));
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
documentCount,
|
|
243
|
+
tagIdf,
|
|
244
|
+
tokenIdf,
|
|
245
|
+
version: semanticProfileVersion(documentCount, tagDocumentFrequency, tokenDocumentFrequency),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
export function idfTagOverlapScore(signals, gene, profile) {
|
|
249
|
+
if (profile.documentCount <= 1)
|
|
250
|
+
return tagOverlapScore(signals, gene);
|
|
251
|
+
const signalTags = [...new Set(expandSignals(signals).map(normalizeSemanticTag))];
|
|
252
|
+
const geneTagSet = new Set(geneTags(gene).map(normalizeSemanticTag));
|
|
253
|
+
let matchedWeight = 0;
|
|
254
|
+
let totalWeight = 0;
|
|
255
|
+
for (const tag of signalTags) {
|
|
256
|
+
const weight = profile.tagIdf.get(tag);
|
|
257
|
+
if (weight === undefined)
|
|
258
|
+
continue;
|
|
259
|
+
totalWeight += weight;
|
|
260
|
+
if (geneTagSet.has(tag))
|
|
261
|
+
matchedWeight += weight;
|
|
262
|
+
}
|
|
263
|
+
return totalWeight > 0 ? matchedWeight / totalWeight : 0;
|
|
264
|
+
}
|
|
265
|
+
export function idfBagCosine(a, b, profile) {
|
|
266
|
+
if (profile.documentCount <= 1)
|
|
267
|
+
return bagCosine(a, b);
|
|
268
|
+
const bagA = tokenFrequency(a);
|
|
269
|
+
const bagB = tokenFrequency(b);
|
|
270
|
+
let dot = 0;
|
|
271
|
+
let normA = 0;
|
|
272
|
+
let normB = 0;
|
|
273
|
+
for (const [token, count] of bagA) {
|
|
274
|
+
const weight = profile.tokenIdf.get(token);
|
|
275
|
+
if (weight === undefined)
|
|
276
|
+
continue;
|
|
277
|
+
const weightedCount = count * weight;
|
|
278
|
+
normA += weightedCount * weightedCount;
|
|
279
|
+
const otherCount = bagB.get(token);
|
|
280
|
+
if (otherCount !== undefined)
|
|
281
|
+
dot += weightedCount * otherCount * weight;
|
|
282
|
+
}
|
|
283
|
+
for (const [token, count] of bagB) {
|
|
284
|
+
const weight = profile.tokenIdf.get(token);
|
|
285
|
+
if (weight === undefined)
|
|
286
|
+
continue;
|
|
287
|
+
const weightedCount = count * weight;
|
|
288
|
+
normB += weightedCount * weightedCount;
|
|
289
|
+
}
|
|
290
|
+
if (normA === 0 || normB === 0)
|
|
291
|
+
return 0;
|
|
292
|
+
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
125
293
|
}
|
|
@@ -6,8 +6,8 @@ export interface SignalSourceTurn {
|
|
|
6
6
|
errorMessage?: string;
|
|
7
7
|
isMeta?: boolean;
|
|
8
8
|
}
|
|
9
|
-
/**
|
|
10
|
-
export type SignalStrength = 'strong' | 'agent' | 'weak';
|
|
9
|
+
/** 信号强度四条腿(批注#13): strong=零成本结构化错误判断 / agent=自标记 / weak=需 LLM 分析 / success=成功信号(Issue#578). */
|
|
10
|
+
export type SignalStrength = 'strong' | 'agent' | 'weak' | 'success';
|
|
11
11
|
export interface ExtractedSignal {
|
|
12
12
|
id: string;
|
|
13
13
|
strength: SignalStrength;
|
|
@@ -4,7 +4,16 @@ const AGENT_MARKERS = [/\bEVOLVE_SIGNAL:\s*(.+)/i, /\[SIGNAL\]\s*(.+)/i];
|
|
|
4
4
|
/** 结构化强信号: 栈/退出码/显式 Error — 零成本判断, 不动 LLM. */
|
|
5
5
|
const STRONG_TEXT = /(^|\n)\s*(Error:|Traceback|Exception|FAILED|panic:|\bexit code [1-9])/;
|
|
6
6
|
/** 弱信号: 表达困难但无结构, 需 LLM 拼语境. */
|
|
7
|
-
const DIFFICULTY = /(无法|失败|搞不定|stuck|can'?t|unable to|not working|报错|卡住)/i;
|
|
7
|
+
const DIFFICULTY = /(无法|失败|搞不定|stuck|can'?t|unable to|not working|fail(?:ed|ing|ure)?|broken|did not pass|no tests? passed|报错|卡住)/i;
|
|
8
|
+
/** 成功信号: 工具执行成功/测试通过/构建成功 — 零成本结构化判断, 不动 LLM(Issue#578).
|
|
9
|
+
* 注意: bare `verified` 和 `green` 已移至 SUCCESS_PROSE(needsAnalysis:true), 因为它们在非成功语境下高频出现
|
|
10
|
+
* ("verified the bug exists" / "green field" / "green button") 且 SUCCESS_TEXT 的 needsAnalysis:false 无 LLM 兜底.
|
|
11
|
+
* Emoji are accepted only with test-result context; a bare checklist mark is not proof of session success. */
|
|
12
|
+
const SUCCESS_TEXT = /\b(exit code:?\s*0|all tests?\s+pass(ed)?|tests?\s+pass(ed)?|validation\s+pass(ed)?|build\s+succeed(ed)?|published\s+successfully|completed\s+successfully)\b|(?:^|\n)\s*(?:✓\s*(?:\d+\s+)?(?:tests?\s+)?pass(ed)?\b|(?:Tests|Test Files):?\s+[1-9]\d*\s+pass(ed)?\b|(?:=+\s*)?[1-9]\d*\s+pass(ed)?(?:\s+in\s+\d+(?:\.\d+)?s?|\s*=*\s*$)|PASS\s+\S+|ok\s+\S+\s+(?:\d+(?:\.\d+)?s|\(cached\))(?:\s|$))|\btests?\s+🟢(?:\s|$)/im;
|
|
13
|
+
/** Deterministic success must fail closed when the same result carries a current negative outcome. */
|
|
14
|
+
const SUCCESS_CONFLICT = /\b(?:no|zero|0)(?:\s+of\s+\d+)?\s+tests?\s+pass(ed)?\b|\bsome\s+tests?\s+pass(ed)?\b|\b(?:tests?|validation|verification|build)\s+(?:fail(?:ed|ing|ure)?|did\s+not\s+pass)\b|\b\d+\s+(?:tests?\s+)?failed\b|\b(?:implementation\s+)?(?:still\s+|remains?\s+)(?:broken|failing|not\s+working)\b|\b(?:not|never|did\s+not|has\s+not|hasn't|have\s+not|haven't)\s+(?:succeed(ed)?|complete(d)?\s+successfully)\b/i;
|
|
15
|
+
/** 成功措辞(非结构化但表达完成/解决): 需上下文确认(Issue#578). bare `verified` 也在此层: "verified the fix" vs "verified the bug" 需 LLM 判断. */
|
|
16
|
+
const SUCCESS_PROSE = /\b(successful(?:ly)?|verified|resolved|fixed|working\s+now|works\s+correctly|problem\s+solved|issue\s+resolved|done|complete)\b/i;
|
|
8
17
|
/**
|
|
9
18
|
* Harness-coordination noise — agent/tool mechanics, NOT engineering problems to evolve genes for.
|
|
10
19
|
* Observation showed these dominate the "strong" leg (tool errors) and drown out real problems: the agent
|
|
@@ -33,7 +42,8 @@ export function extractSignals(turns) {
|
|
|
33
42
|
continue;
|
|
34
43
|
}
|
|
35
44
|
const text = t.text ?? '';
|
|
36
|
-
|
|
45
|
+
const toolResult = t.toolResult ?? '';
|
|
46
|
+
if (!text.trim() && !toolResult.trim())
|
|
37
47
|
continue;
|
|
38
48
|
// 腿3 agent: 显式标记
|
|
39
49
|
const marker = AGENT_MARKERS.map((re) => text.match(re)).find(Boolean);
|
|
@@ -41,16 +51,31 @@ export function extractSignals(turns) {
|
|
|
41
51
|
out.push({ id: makeUlid(), strength: 'agent', kind: 'agent_marked', text: (marker[1] ?? text).trim().slice(0, 2000), needsAnalysis: false });
|
|
42
52
|
continue;
|
|
43
53
|
}
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
// Leg 1 strong: inspect both fields. Some adapters populate only toolResult, while older callers may populate
|
|
55
|
+
// text as well; a failure in either representation must outrank a positive substring in the other.
|
|
56
|
+
const resultText = toolResult.trim() ? toolResult : text;
|
|
57
|
+
const structuredFailureText = STRONG_TEXT.test(text) ? text : (STRONG_TEXT.test(toolResult) ? toolResult : '');
|
|
58
|
+
if (structuredFailureText) {
|
|
59
|
+
if (!isHarnessNoise(structuredFailureText)) {
|
|
60
|
+
out.push({ id: makeUlid(), strength: 'strong', kind: 'structured_error', text: structuredFailureText.slice(0, 2000), ...(t.toolName ? { toolName: t.toolName } : {}), needsAnalysis: false });
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
// Leg 4 success: prefer the actual tool result; adapters intentionally keep tool-turn text empty.
|
|
65
|
+
if (SUCCESS_TEXT.test(resultText) && !SUCCESS_CONFLICT.test(resultText)) {
|
|
66
|
+
if (!isHarnessNoise(resultText)) {
|
|
67
|
+
out.push({ id: makeUlid(), strength: 'success', kind: 'verified_success', text: resultText.slice(0, 2000), ...(t.toolName ? { toolName: t.toolName } : {}), needsAnalysis: false });
|
|
48
68
|
}
|
|
49
69
|
continue;
|
|
50
70
|
}
|
|
51
71
|
// 腿2 weak: 困难措辞无结构 → 交 LLM
|
|
52
72
|
if (DIFFICULTY.test(text)) {
|
|
53
73
|
out.push({ id: makeUlid(), strength: 'weak', kind: 'difficulty', text: text.slice(0, 2000), needsAnalysis: true });
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
// 腿4 success: 非结构化成功措辞(resolved/fixed/working now 等) → 需上下文确认(Issue#578)
|
|
77
|
+
if (SUCCESS_PROSE.test(text)) {
|
|
78
|
+
out.push({ id: makeUlid(), strength: 'success', kind: 'success_prose', text: text.slice(0, 2000), needsAnalysis: true });
|
|
54
79
|
}
|
|
55
80
|
}
|
|
56
81
|
return out;
|
package/dist/signals/index.d.ts
CHANGED
|
@@ -3,4 +3,6 @@ export * from './signalGate.js';
|
|
|
3
3
|
export * from './expand.js';
|
|
4
4
|
export * from './traceSignals.js';
|
|
5
5
|
export * from './metaSignals.js';
|
|
6
|
-
export * from './cycleHistoryFromEvents.js';
|
|
6
|
+
export * from './cycleHistoryFromEvents.js';
|
|
7
|
+
export * from './curriculum.js';
|
|
8
|
+
export * from './taskDomain.js';
|
package/dist/signals/index.js
CHANGED
|
@@ -3,4 +3,6 @@ export * from './signalGate.js';
|
|
|
3
3
|
export * from './expand.js';
|
|
4
4
|
export * from './traceSignals.js';
|
|
5
5
|
export * from './metaSignals.js';
|
|
6
|
-
export * from './cycleHistoryFromEvents.js';
|
|
6
|
+
export * from './cycleHistoryFromEvents.js';
|
|
7
|
+
export * from './curriculum.js';
|
|
8
|
+
export * from './taskDomain.js';
|
|
@@ -8,6 +8,10 @@ export interface CycleHistory {
|
|
|
8
8
|
consecutiveEmptyCycles: number;
|
|
9
9
|
/** Trailing run of consecutive failed cycles at the tail. */
|
|
10
10
|
consecutiveFailureCount: number;
|
|
11
|
+
/** Trailing run of consecutive successful cycles at the tail. */
|
|
12
|
+
consecutiveSuccessCount: number;
|
|
13
|
+
/** Successful cycles within the recent window. */
|
|
14
|
+
successCycleCount: number;
|
|
11
15
|
/** Fraction of the recent window that failed (0..1). */
|
|
12
16
|
recentFailureRatio: number;
|
|
13
17
|
/** geneId → times used within the recent window (informs which gene dominates a failure loop). */
|
|
@@ -27,6 +27,9 @@ function isEmptyCycle(r) {
|
|
|
27
27
|
function isFailed(r) {
|
|
28
28
|
return r.outcome?.status === 'failed';
|
|
29
29
|
}
|
|
30
|
+
function isSuccess(r) {
|
|
31
|
+
return r.outcome?.status === 'success';
|
|
32
|
+
}
|
|
30
33
|
/**
|
|
31
34
|
* Derive the v1 history counters from a normalized cycle-record window (newest LAST), mirroring v1
|
|
32
35
|
* analyzeRecentHistory. Pure + deterministic. Use {@link cycleRecordsFromEvents} to adapt the v2 event log.
|
|
@@ -37,6 +40,8 @@ export function deriveCycleHistory(records) {
|
|
|
37
40
|
emptyCycleCount: 0,
|
|
38
41
|
consecutiveEmptyCycles: 0,
|
|
39
42
|
consecutiveFailureCount: 0,
|
|
43
|
+
consecutiveSuccessCount: 0,
|
|
44
|
+
successCycleCount: 0,
|
|
40
45
|
recentFailureRatio: 0,
|
|
41
46
|
geneFreq: {},
|
|
42
47
|
};
|
|
@@ -90,11 +95,32 @@ export function deriveCycleHistory(records) {
|
|
|
90
95
|
if (isFailed(r))
|
|
91
96
|
recentFailureCount++;
|
|
92
97
|
}
|
|
98
|
+
// Consecutive successes at the tail (stable success plateau detection). Empty cycles are zero-blast
|
|
99
|
+
// no-ops: like the failure streak, they break the success streak instead of extending it.
|
|
100
|
+
let consecutiveSuccessCount = 0;
|
|
101
|
+
for (let i = recent.length - 1; i >= 0; i--) {
|
|
102
|
+
if (isEmptyCycle(recent[i]))
|
|
103
|
+
break;
|
|
104
|
+
if (isSuccess(recent[i]))
|
|
105
|
+
consecutiveSuccessCount++;
|
|
106
|
+
else
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
// Successful cycles within the frequency window (non-empty successes, same caliber as recentFailureRatio).
|
|
110
|
+
let successCycleCount = 0;
|
|
111
|
+
for (const r of tail) {
|
|
112
|
+
if (isEmptyCycle(r))
|
|
113
|
+
continue;
|
|
114
|
+
if (isSuccess(r))
|
|
115
|
+
successCycleCount++;
|
|
116
|
+
}
|
|
93
117
|
return {
|
|
94
118
|
consecutiveRepairCount,
|
|
95
119
|
emptyCycleCount,
|
|
96
120
|
consecutiveEmptyCycles,
|
|
97
121
|
consecutiveFailureCount,
|
|
122
|
+
consecutiveSuccessCount,
|
|
123
|
+
successCycleCount,
|
|
98
124
|
recentFailureRatio: nonEmptyCycleCount > 0 ? recentFailureCount / nonEmptyCycleCount : 0,
|
|
99
125
|
geneFreq,
|
|
100
126
|
};
|
|
@@ -153,6 +179,22 @@ export function computeMetaSignals(history) {
|
|
|
153
179
|
signals.push('high_failure_ratio');
|
|
154
180
|
signals.push('force_innovation_after_repair_loop');
|
|
155
181
|
}
|
|
182
|
+
// Stable success plateau: 5+ consecutive successes → system is in a stable success plateau.
|
|
183
|
+
// This is the SUCCESS counterpart of failure_loop_detected: instead of signaling "stuck failing",
|
|
184
|
+
// it signals "consistently succeeding" — the loop should distill WHAT WORKED into reusable genes.
|
|
185
|
+
if (history.consecutiveSuccessCount >= 5) {
|
|
186
|
+
signals.push('stable_success_plateau');
|
|
187
|
+
}
|
|
188
|
+
// Issue already resolved: 3+ consecutive successes after a prior failure context suggests
|
|
189
|
+
// the problem is solved — downstream can skip redundant repair attempts.
|
|
190
|
+
if (history.consecutiveSuccessCount >= 3 && history.recentFailureRatio > 0) {
|
|
191
|
+
signals.push('issue_already_resolved');
|
|
192
|
+
}
|
|
193
|
+
// Self-healed requires the current tail to be successful. Window totals alone would mislabel
|
|
194
|
+
// [success, success, failed] as recovered even though the latest outcome is a failure.
|
|
195
|
+
if (history.consecutiveSuccessCount > 0 && history.successCycleCount >= 2 && history.recentFailureRatio > 0 && history.recentFailureRatio < 0.5) {
|
|
196
|
+
signals.push('openclaw_self_healed');
|
|
197
|
+
}
|
|
156
198
|
// De-dup while preserving first-seen order.
|
|
157
199
|
return [...new Set(signals)];
|
|
158
200
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { normalizeText } from '../signatures/signatures.js';
|
|
3
|
-
const STRENGTH_WEIGHT = { strong: 1.0, agent: 0.8, weak: 0.4 };
|
|
3
|
+
const STRENGTH_WEIGHT = { strong: 1.0, agent: 0.8, success: 0.7, weak: 0.4 };
|
|
4
4
|
function signatureOf(s) {
|
|
5
5
|
return createHash('sha1').update(`${s.kind}\x1f${normalizeText(s.text)}`).digest('hex').slice(0, 16);
|
|
6
6
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const TASK_DOMAIN_SIGNAL_PREFIX: "task_domain:";
|
|
2
|
+
export type TaskDomainResolution = {
|
|
3
|
+
status: 'absent';
|
|
4
|
+
} | {
|
|
5
|
+
status: 'resolved';
|
|
6
|
+
slug: string;
|
|
7
|
+
} | {
|
|
8
|
+
status: 'ambiguous';
|
|
9
|
+
} | {
|
|
10
|
+
status: 'invalid';
|
|
11
|
+
};
|
|
12
|
+
/** Identify the namespace even when the value is malformed, so it cannot leak into generic matching. */
|
|
13
|
+
export declare function isTaskDomainSignal(raw: string): boolean;
|
|
14
|
+
/** Remove task-domain tokens from generic matching while preserving their original wire representation elsewhere. */
|
|
15
|
+
export declare function withoutTaskDomainSignals(signals: readonly string[]): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Resolve one canonical task domain from signal tokens. Parsing is order-independent and fail-closed:
|
|
18
|
+
* malformed tokens invalidate the whole dimension, and distinct valid slugs are ambiguous.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveTaskDomainSignals(signals: readonly string[]): TaskDomainResolution;
|
|
21
|
+
/** Emit a canonical wire token. Callers must supply an already-normalized lowercase slug. */
|
|
22
|
+
export declare function taskDomainSignal(slug: string): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const TASK_DOMAIN_SIGNAL_PREFIX = 'task_domain:';
|
|
2
|
+
const TASK_DOMAIN_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
3
|
+
/** Identify the namespace even when the value is malformed, so it cannot leak into generic matching. */
|
|
4
|
+
export function isTaskDomainSignal(raw) {
|
|
5
|
+
return raw.trim().toLowerCase().startsWith(TASK_DOMAIN_SIGNAL_PREFIX);
|
|
6
|
+
}
|
|
7
|
+
/** Remove task-domain tokens from generic matching while preserving their original wire representation elsewhere. */
|
|
8
|
+
export function withoutTaskDomainSignals(signals) {
|
|
9
|
+
return signals.filter((signal) => !isTaskDomainSignal(signal));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve one canonical task domain from signal tokens. Parsing is order-independent and fail-closed:
|
|
13
|
+
* malformed tokens invalidate the whole dimension, and distinct valid slugs are ambiguous.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveTaskDomainSignals(signals) {
|
|
16
|
+
const slugs = new Set();
|
|
17
|
+
let found = false;
|
|
18
|
+
for (const raw of signals) {
|
|
19
|
+
const signal = raw.trim();
|
|
20
|
+
const lower = signal.toLowerCase();
|
|
21
|
+
if (!lower.startsWith(TASK_DOMAIN_SIGNAL_PREFIX))
|
|
22
|
+
continue;
|
|
23
|
+
found = true;
|
|
24
|
+
if (signal !== raw)
|
|
25
|
+
return { status: 'invalid' };
|
|
26
|
+
const slug = lower.slice(TASK_DOMAIN_SIGNAL_PREFIX.length);
|
|
27
|
+
if (!TASK_DOMAIN_SLUG_RE.test(slug))
|
|
28
|
+
return { status: 'invalid' };
|
|
29
|
+
slugs.add(slug);
|
|
30
|
+
}
|
|
31
|
+
if (!found)
|
|
32
|
+
return { status: 'absent' };
|
|
33
|
+
if (slugs.size !== 1)
|
|
34
|
+
return { status: 'ambiguous' };
|
|
35
|
+
return { status: 'resolved', slug: slugs.values().next().value };
|
|
36
|
+
}
|
|
37
|
+
/** Emit a canonical wire token. Callers must supply an already-normalized lowercase slug. */
|
|
38
|
+
export function taskDomainSignal(slug) {
|
|
39
|
+
if (!TASK_DOMAIN_SLUG_RE.test(slug)) {
|
|
40
|
+
throw new Error(`invalid task_domain slug: ${slug}`);
|
|
41
|
+
}
|
|
42
|
+
return `${TASK_DOMAIN_SIGNAL_PREFIX}${slug}`;
|
|
43
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type ConstraintKind = 'must' | 'must_not';
|
|
2
|
+
export type ConstraintTraceSource = 'plan' | 'task' | 'trace';
|
|
3
|
+
export type ConstraintSeverity = 'low' | 'medium' | 'high';
|
|
4
|
+
export type SensitiveClass = 'credential' | 'email' | 'filesystem_path';
|
|
5
|
+
export type TaskSuccessStatus = 'success' | 'failure' | 'unknown';
|
|
6
|
+
export type TaskSuccessSource = 'oracle' | 'task_status' | 'manual' | 'trace';
|
|
7
|
+
export type SensitivitySuccessComparison = 'success_constraint_sensitive' | 'success_constraint_insensitive' | 'failure_constraint_sensitive' | 'failure_constraint_insensitive' | 'unknown_success';
|
|
8
|
+
export interface ConstraintTrace {
|
|
9
|
+
source: ConstraintTraceSource;
|
|
10
|
+
text: string;
|
|
11
|
+
traceId?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ExtractedConstraint {
|
|
14
|
+
id: string;
|
|
15
|
+
kind: ConstraintKind;
|
|
16
|
+
textHash: string;
|
|
17
|
+
redactedText: string;
|
|
18
|
+
source: ConstraintTraceSource;
|
|
19
|
+
traceId?: string;
|
|
20
|
+
sensitiveClasses: SensitiveClass[];
|
|
21
|
+
}
|
|
22
|
+
export interface ConstraintAblatedPrompt {
|
|
23
|
+
originalPromptHash: string;
|
|
24
|
+
ablatedPromptHash: string;
|
|
25
|
+
removedConstraintIds: string[];
|
|
26
|
+
redactedPreview?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ConstraintViolation {
|
|
29
|
+
constraintId: string;
|
|
30
|
+
kind: ConstraintKind;
|
|
31
|
+
severity: ConstraintSeverity;
|
|
32
|
+
evidenceHash: string;
|
|
33
|
+
matchedTerms: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface TaskSuccessLabel {
|
|
36
|
+
status: TaskSuccessStatus;
|
|
37
|
+
source: TaskSuccessSource;
|
|
38
|
+
}
|
|
39
|
+
export interface ConstraintAblationScore {
|
|
40
|
+
source: 'constraint_ablation_replay';
|
|
41
|
+
sensitivity: number;
|
|
42
|
+
ablationCount: number;
|
|
43
|
+
baselineViolationCount: number;
|
|
44
|
+
ablatedViolationCount: number;
|
|
45
|
+
mustViolationCount: number;
|
|
46
|
+
mustNotViolationCount: number;
|
|
47
|
+
taskSuccess: TaskSuccessLabel;
|
|
48
|
+
comparison: SensitivitySuccessComparison;
|
|
49
|
+
}
|
|
50
|
+
export interface ConstraintAblationScoreInput {
|
|
51
|
+
baselineViolations: readonly ConstraintViolation[];
|
|
52
|
+
ablatedViolations: readonly ConstraintViolation[];
|
|
53
|
+
removedConstraintIds: readonly string[];
|
|
54
|
+
ablationCount: number;
|
|
55
|
+
taskSuccess: TaskSuccessLabel;
|
|
56
|
+
sensitivityThreshold?: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function redactConstraintText(text: string): string;
|
|
59
|
+
export declare function extractConstraints(traces: readonly ConstraintTrace[]): ExtractedConstraint[];
|
|
60
|
+
export declare function buildConstraintAblatedPrompts(prompt: string, constraints: readonly ExtractedConstraint[], opts?: {
|
|
61
|
+
includeRedactedPreview?: boolean;
|
|
62
|
+
}): ConstraintAblatedPrompt[];
|
|
63
|
+
export declare function detectConstraintViolations(output: string, constraints: readonly ExtractedConstraint[]): ConstraintViolation[];
|
|
64
|
+
export declare function computeConstraintAblationScore(input: ConstraintAblationScoreInput): ConstraintAblationScore;
|