@evomap/evolver-core 2.0.0-beta.17 → 2.0.0-beta.19
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.js +21 -2
- package/dist/algo/cycleEngine.d.ts +12 -0
- package/dist/algo/cycleEngine.js +36 -4
- package/dist/algo/geneHealth.d.ts +2 -2
- package/dist/algo/geneHealth.js +5 -4
- package/dist/algo/geneSelection.d.ts +1 -1
- package/dist/algo/orchestrator.js +9 -2
- package/dist/assetstore/assetSidecarRecords.js +4 -0
- package/dist/assetstore/assetStoreHealth.js +41 -24
- package/dist/assetstore/assetStoreStorage.d.ts +1 -1
- package/dist/assetstore/assetStoreStorage.js +16 -7
- package/dist/assetstore/localJsonl.d.ts +2 -1
- package/dist/assetstore/localJsonl.js +54 -10
- package/dist/assetstore/provenance.d.ts +24 -0
- package/dist/assetstore/provenance.js +219 -12
- package/dist/assetstore/provider.d.ts +20 -1
- package/dist/assetstore/provider.js +34 -1
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/v1EnvCompat.d.ts +110 -0
- package/dist/bootstrap/v1EnvCompat.js +256 -0
- package/dist/events/public.d.ts +1 -1
- package/dist/events/public.js +1 -1
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +18 -1
- package/dist/exec/autoExec.js +24 -9
- package/dist/exec/autonomousCycle.d.ts +19 -4
- package/dist/exec/autonomousCycle.js +63 -13
- package/dist/exec/claudeBridge.d.ts +25 -7
- package/dist/exec/claudeBridge.js +264 -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/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 +11 -1
- package/dist/hub/fake.d.ts +2 -2
- package/dist/hub/fake.js +1 -1
- 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 +38 -2
- package/dist/mailbox/store.js +416 -27
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +202 -0
- package/dist/signals/expand.js +17 -6
- package/dist/signals/index.d.ts +2 -1
- package/dist/signals/index.js +2 -1
- package/dist/strategy/constraintAblation.js +115 -369
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +339 -0
- package/dist/trace/index.d.ts +2 -1
- package/dist/trace/index.js +2 -1
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/verify/validation.d.ts +11 -1
- package/dist/verify/validation.js +31 -0
- package/package.json +4 -1
|
@@ -0,0 +1,339 @@
|
|
|
1
|
+
const FACTUAL_TAIL_PREFIX_TERMS = new Set([
|
|
2
|
+
'after', 'although', 'as', 'because', 'before', 'however', 'since', 'than', 'though',
|
|
3
|
+
'whereas', 'yet',
|
|
4
|
+
]);
|
|
5
|
+
const RELATIVE_TERMS = new Set(['that', 'which', 'who']);
|
|
6
|
+
const SUBORDINATOR_TERMS = new Set(['after', 'as', 'before']);
|
|
7
|
+
const NOMINAL_MODIFIER_TERMS = new Set([
|
|
8
|
+
'access', 'audit', 'backup', 'cold', 'data', 'offline', 'ongoing',
|
|
9
|
+
'policy', 'remote', 'secure', 'security', 'token',
|
|
10
|
+
]);
|
|
11
|
+
const NOMINAL_HEAD_TERMS = new Set([
|
|
12
|
+
'access', 'approval', 'archive', 'audit', 'auditor', 'credentials', 'data', 'details',
|
|
13
|
+
'leak', 'leaks', 'metadata', 'policy', 'receipt', 'records', 'request', 'review', 'server',
|
|
14
|
+
'storage', 'system', 'transit',
|
|
15
|
+
]);
|
|
16
|
+
const MATRIX_NOMINAL_COLLOCATIONS = new Set(['access:policy']);
|
|
17
|
+
const OBJECT_COORDINATOR_TERMS = new Set(['and', 'or', 'then']);
|
|
18
|
+
const FACTUAL_COORDINATOR_TERMS = new Set(['and', 'but', 'then']);
|
|
19
|
+
const TRAILING_MODIFIER_TERMS = new Set([
|
|
20
|
+
'earlier', 'here', 'now', 'soon', 'today', 'tonight', 'there', 'yesterday',
|
|
21
|
+
]);
|
|
22
|
+
const MATRIX_PREDICATE_TERMS = new Set([
|
|
23
|
+
'arrive', 'arrives', 'become', 'becomes', 'exist', 'exists', 'expire', 'expires',
|
|
24
|
+
'leak', 'leaks', 'match', 'matches', 'pass', 'passes', 'remain', 'remains', 'trigger', 'triggers',
|
|
25
|
+
]);
|
|
26
|
+
class ProvidedTargetPrefixClassifier {
|
|
27
|
+
input;
|
|
28
|
+
tokens;
|
|
29
|
+
matchedObjectForms = new Set();
|
|
30
|
+
matchedObjectIndexes = new Set();
|
|
31
|
+
constructor(input) {
|
|
32
|
+
this.input = input;
|
|
33
|
+
this.tokens = input.predicates.tokenize(input.source).tokens;
|
|
34
|
+
}
|
|
35
|
+
classify() {
|
|
36
|
+
const objectPrefixEnd = this.matchObjectPrefix();
|
|
37
|
+
if (objectPrefixEnd < 0)
|
|
38
|
+
return 'none';
|
|
39
|
+
if (this.hasFiniteNonObjectPrefix(objectPrefixEnd))
|
|
40
|
+
return 'conditional';
|
|
41
|
+
const tailStart = this.consumeRemainingObjectTerms(objectPrefixEnd + 1);
|
|
42
|
+
return this.classifyObjectTail(tailStart);
|
|
43
|
+
}
|
|
44
|
+
matchObjectPrefix() {
|
|
45
|
+
const requiredMatches = Math.min(2, this.input.objectForms.length);
|
|
46
|
+
const deferredOverlaps = [];
|
|
47
|
+
for (let tokenIndex = 0; tokenIndex < this.tokens.length; tokenIndex += 1) {
|
|
48
|
+
const [formIndex, ...overlaps] = this.unmatchedObjectFormIndexes(this.valueAt(tokenIndex));
|
|
49
|
+
if (formIndex === undefined)
|
|
50
|
+
continue;
|
|
51
|
+
this.matchedObjectForms.add(formIndex);
|
|
52
|
+
this.matchedObjectIndexes.add(tokenIndex);
|
|
53
|
+
deferredOverlaps.push(...overlaps.map((overlap) => ({ formIndex: overlap, tokenIndex })));
|
|
54
|
+
if (this.matchedObjectForms.size >= requiredMatches)
|
|
55
|
+
return tokenIndex;
|
|
56
|
+
}
|
|
57
|
+
for (const { formIndex, tokenIndex } of deferredOverlaps) {
|
|
58
|
+
if (this.matchedObjectForms.has(formIndex))
|
|
59
|
+
continue;
|
|
60
|
+
this.matchedObjectForms.add(formIndex);
|
|
61
|
+
this.matchedObjectIndexes.add(tokenIndex);
|
|
62
|
+
if (this.matchedObjectForms.size >= requiredMatches) {
|
|
63
|
+
return Math.max(...this.matchedObjectIndexes);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return -1;
|
|
67
|
+
}
|
|
68
|
+
unmatchedObjectFormIndexes(value) {
|
|
69
|
+
return this.input.objectForms.flatMap((forms, candidateIndex) => (!this.matchedObjectForms.has(candidateIndex) && forms.has(value) ? [candidateIndex] : []));
|
|
70
|
+
}
|
|
71
|
+
hasFiniteNonObjectPrefix(objectPrefixEnd) {
|
|
72
|
+
const prefix = this.tokens
|
|
73
|
+
.slice(0, objectPrefixEnd + 1)
|
|
74
|
+
.filter((_, index) => !this.matchedObjectIndexes.has(index))
|
|
75
|
+
.map((token) => token.value)
|
|
76
|
+
.join(' ');
|
|
77
|
+
return this.input.predicates.finiteClauseSubjectPolarity(prefix) !== 'absent'
|
|
78
|
+
|| this.input.predicates.hasIndependentAffirmativeAction(prefix, this.input.actionForms);
|
|
79
|
+
}
|
|
80
|
+
skipModifiers(initialIndex) {
|
|
81
|
+
let index = initialIndex;
|
|
82
|
+
while (index < this.tokens.length && this.isSkippableModifier(this.valueAt(index)))
|
|
83
|
+
index += 1;
|
|
84
|
+
return index;
|
|
85
|
+
}
|
|
86
|
+
isSkippableModifier(term) {
|
|
87
|
+
return this.input.lexicon.leadingClauseModifiers.has(term)
|
|
88
|
+
|| TRAILING_MODIFIER_TERMS.has(term)
|
|
89
|
+
|| /ly$/u.test(term);
|
|
90
|
+
}
|
|
91
|
+
consumeRemainingObjectTerms(initialIndex) {
|
|
92
|
+
let index = this.skipModifiers(initialIndex);
|
|
93
|
+
const remaining = new Set(this.input.objectForms.keys());
|
|
94
|
+
for (const matched of this.matchedObjectForms)
|
|
95
|
+
remaining.delete(matched);
|
|
96
|
+
while (index < this.tokens.length) {
|
|
97
|
+
const candidate = [...remaining].find((formIndex) => (this.input.objectForms[formIndex]?.has(this.valueAt(index))));
|
|
98
|
+
if (candidate === undefined)
|
|
99
|
+
break;
|
|
100
|
+
remaining.delete(candidate);
|
|
101
|
+
index = this.skipModifiers(index + 1);
|
|
102
|
+
}
|
|
103
|
+
return index;
|
|
104
|
+
}
|
|
105
|
+
isPastPredicate(term) {
|
|
106
|
+
return term.endsWith('ed')
|
|
107
|
+
|| this.input.lexicon.irregularSimplePast.has(term)
|
|
108
|
+
|| this.input.lexicon.irregularPastParticiples.has(term);
|
|
109
|
+
}
|
|
110
|
+
consumeNominal(initialIndex) {
|
|
111
|
+
let index = this.skipModifiers(initialIndex);
|
|
112
|
+
if (this.input.lexicon.subjectArticles.has(this.valueAt(index)))
|
|
113
|
+
index += 1;
|
|
114
|
+
while (index + 1 < this.tokens.length && this.isSimpleNominalModifier(this.valueAt(index)))
|
|
115
|
+
index += 1;
|
|
116
|
+
if (index < this.tokens.length)
|
|
117
|
+
index += 1;
|
|
118
|
+
return this.skipModifiers(index);
|
|
119
|
+
}
|
|
120
|
+
isSimpleNominalModifier(term) {
|
|
121
|
+
return this.input.predicates.isAttributiveTargetModifier(term) || NOMINAL_MODIFIER_TERMS.has(term);
|
|
122
|
+
}
|
|
123
|
+
isObjectForm(term) {
|
|
124
|
+
return this.input.objectForms.some((forms) => forms.has(term));
|
|
125
|
+
}
|
|
126
|
+
isMatrixPredicate(term) {
|
|
127
|
+
return MATRIX_PREDICATE_TERMS.has(term)
|
|
128
|
+
|| this.input.predicates.isFinitePredicateTerm(term)
|
|
129
|
+
|| (this.input.actionForms.has(term) && !this.isObjectForm(term));
|
|
130
|
+
}
|
|
131
|
+
isNominalHead(term) {
|
|
132
|
+
return NOMINAL_HEAD_TERMS.has(term) || this.isObjectForm(term);
|
|
133
|
+
}
|
|
134
|
+
isNominalModifier(term) {
|
|
135
|
+
return this.input.predicates.isAttributiveTargetModifier(term)
|
|
136
|
+
|| NOMINAL_MODIFIER_TERMS.has(term)
|
|
137
|
+
|| this.isObjectForm(term);
|
|
138
|
+
}
|
|
139
|
+
reachesKnownNominalHead(initialIndex, genericModifierBudget) {
|
|
140
|
+
let index = initialIndex;
|
|
141
|
+
let remainingBudget = genericModifierBudget;
|
|
142
|
+
while (index < this.tokens.length) {
|
|
143
|
+
const term = this.valueAt(index);
|
|
144
|
+
if (this.isNominalHead(term))
|
|
145
|
+
return true;
|
|
146
|
+
if (!this.isNominalModifier(term)) {
|
|
147
|
+
if (remainingBudget <= 0)
|
|
148
|
+
return false;
|
|
149
|
+
remainingBudget -= 1;
|
|
150
|
+
}
|
|
151
|
+
index = this.skipModifiers(index + 1);
|
|
152
|
+
}
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
isMatrixNominalCollocation(initialIndex) {
|
|
156
|
+
const modifier = this.valueAt(initialIndex);
|
|
157
|
+
let index = this.skipModifiers(initialIndex + 1);
|
|
158
|
+
while (index < this.tokens.length) {
|
|
159
|
+
const term = this.valueAt(index);
|
|
160
|
+
if (this.isNominalHead(term))
|
|
161
|
+
return MATRIX_NOMINAL_COLLOCATIONS.has(`${modifier}:${term}`);
|
|
162
|
+
if (!this.isNominalModifier(term))
|
|
163
|
+
return false;
|
|
164
|
+
index = this.skipModifiers(index + 1);
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
consumeNominalPhrase(initialIndex) {
|
|
169
|
+
let index = this.skipModifiers(initialIndex);
|
|
170
|
+
const hadArticle = this.input.lexicon.subjectArticles.has(this.valueAt(index));
|
|
171
|
+
if (hadArticle)
|
|
172
|
+
index += 1;
|
|
173
|
+
const prenominal = this.consumePrenominalModifiers(index, hadArticle ? 2 : 1);
|
|
174
|
+
index = prenominal.index;
|
|
175
|
+
const head = this.valueAt(index);
|
|
176
|
+
if (!this.isValidNominalHead(head, prenominal.sawModifier))
|
|
177
|
+
return index;
|
|
178
|
+
return this.consumeGerundComplements(this.skipModifiers(index + 1));
|
|
179
|
+
}
|
|
180
|
+
consumePrenominalModifiers(initialIndex, genericLimit) {
|
|
181
|
+
let index = initialIndex;
|
|
182
|
+
let genericModifiers = 0;
|
|
183
|
+
let sawModifier = false;
|
|
184
|
+
while (index + 1 < this.tokens.length) {
|
|
185
|
+
const nextIndex = this.skipModifiers(index + 1);
|
|
186
|
+
const term = this.valueAt(index);
|
|
187
|
+
if (this.isPrenominalBoundary(nextIndex))
|
|
188
|
+
break;
|
|
189
|
+
const knownModifier = this.isNominalModifier(term);
|
|
190
|
+
const genericModifier = !knownModifier
|
|
191
|
+
&& !this.isNominalHead(term)
|
|
192
|
+
&& genericModifiers < genericLimit
|
|
193
|
+
&& this.reachesKnownNominalHead(nextIndex, genericLimit - genericModifiers - 1);
|
|
194
|
+
if (!knownModifier && !genericModifier)
|
|
195
|
+
break;
|
|
196
|
+
if (genericModifier)
|
|
197
|
+
genericModifiers += 1;
|
|
198
|
+
sawModifier = true;
|
|
199
|
+
index = nextIndex;
|
|
200
|
+
}
|
|
201
|
+
return { index, sawModifier };
|
|
202
|
+
}
|
|
203
|
+
isPrenominalBoundary(nextIndex) {
|
|
204
|
+
const next = this.valueAt(nextIndex);
|
|
205
|
+
return nextIndex >= this.tokens.length
|
|
206
|
+
|| RELATIVE_TERMS.has(next)
|
|
207
|
+
|| this.input.lexicon.conditionalMarkers.has(next)
|
|
208
|
+
|| this.isFactualTailPrefix(next)
|
|
209
|
+
|| this.input.lexicon.subjectArticles.has(next)
|
|
210
|
+
|| (this.isMatrixPredicate(next) && !this.isMatrixNominalCollocation(nextIndex));
|
|
211
|
+
}
|
|
212
|
+
isValidNominalHead(head, sawPrenominalModifier) {
|
|
213
|
+
return Boolean(head)
|
|
214
|
+
&& !RELATIVE_TERMS.has(head)
|
|
215
|
+
&& !this.input.lexicon.conditionalMarkers.has(head)
|
|
216
|
+
&& !this.isFactualTailPrefix(head)
|
|
217
|
+
&& !this.input.lexicon.subjectArticles.has(head)
|
|
218
|
+
&& !this.isMatrixPredicate(head)
|
|
219
|
+
&& (!sawPrenominalModifier || this.isNominalHead(head));
|
|
220
|
+
}
|
|
221
|
+
consumeGerundComplements(initialIndex) {
|
|
222
|
+
let index = initialIndex;
|
|
223
|
+
while (this.valueAt(index).endsWith('ing')) {
|
|
224
|
+
const complementStart = this.skipModifiers(index + 1);
|
|
225
|
+
const complementEnd = this.consumeNominalPhrase(complementStart);
|
|
226
|
+
if (complementEnd <= complementStart)
|
|
227
|
+
break;
|
|
228
|
+
index = complementEnd;
|
|
229
|
+
}
|
|
230
|
+
return index;
|
|
231
|
+
}
|
|
232
|
+
consumeTerminalNominal(initialIndex) {
|
|
233
|
+
const nominalEnd = this.consumeNominalPhrase(initialIndex);
|
|
234
|
+
if (nominalEnd >= this.tokens.length)
|
|
235
|
+
return nominalEnd;
|
|
236
|
+
const prefixHead = this.firstNominalHead(initialIndex, nominalEnd);
|
|
237
|
+
return this.isNominalHead(this.valueAt(nominalEnd))
|
|
238
|
+
&& prefixHead === nominalEnd
|
|
239
|
+
&& this.skipModifiers(nominalEnd + 1) >= this.tokens.length
|
|
240
|
+
? this.tokens.length
|
|
241
|
+
: nominalEnd;
|
|
242
|
+
}
|
|
243
|
+
firstNominalHead(initialIndex, nominalEnd) {
|
|
244
|
+
let index = this.skipModifiers(initialIndex);
|
|
245
|
+
if (this.input.lexicon.subjectArticles.has(this.valueAt(index))) {
|
|
246
|
+
index = this.skipModifiers(index + 1);
|
|
247
|
+
}
|
|
248
|
+
while (index < nominalEnd && !this.isNominalHead(this.valueAt(index))) {
|
|
249
|
+
index = this.skipModifiers(index + 1);
|
|
250
|
+
}
|
|
251
|
+
return index;
|
|
252
|
+
}
|
|
253
|
+
classifyRelativeTail(initialIndex) {
|
|
254
|
+
let index = this.skipModifiers(initialIndex);
|
|
255
|
+
const first = this.valueAt(index);
|
|
256
|
+
if (this.input.lexicon.subjectArticles.has(first)) {
|
|
257
|
+
index = this.consumeNominal(index);
|
|
258
|
+
}
|
|
259
|
+
else if (this.hasElidedPastSubject(index)) {
|
|
260
|
+
index = this.skipModifiers(index + 1);
|
|
261
|
+
}
|
|
262
|
+
while (this.input.lexicon.passiveAuxiliaries.has(this.valueAt(index))) {
|
|
263
|
+
index = this.skipModifiers(index + 1);
|
|
264
|
+
}
|
|
265
|
+
if (!this.isPastPredicate(this.valueAt(index)))
|
|
266
|
+
return 'conditional';
|
|
267
|
+
index = this.skipModifiers(index + 1);
|
|
268
|
+
return this.isFactualTerminalNominal(index);
|
|
269
|
+
}
|
|
270
|
+
hasElidedPastSubject(index) {
|
|
271
|
+
const first = this.valueAt(index);
|
|
272
|
+
return !this.isPastPredicate(first)
|
|
273
|
+
&& !this.input.lexicon.passiveAuxiliaries.has(first)
|
|
274
|
+
&& this.isPastPredicate(this.valueAt(index + 1));
|
|
275
|
+
}
|
|
276
|
+
classifySubordinateTail(initialIndex) {
|
|
277
|
+
let index = this.skipModifiers(initialIndex);
|
|
278
|
+
if (index >= this.tokens.length)
|
|
279
|
+
return 'factual';
|
|
280
|
+
if (this.isPastPredicate(this.valueAt(index)))
|
|
281
|
+
index = this.skipModifiers(index + 1);
|
|
282
|
+
return this.isFactualTerminalNominal(index);
|
|
283
|
+
}
|
|
284
|
+
isFactualTerminalNominal(index) {
|
|
285
|
+
if (index >= this.tokens.length)
|
|
286
|
+
return 'factual';
|
|
287
|
+
return this.consumeTerminalNominal(index) >= this.tokens.length ? 'factual' : 'conditional';
|
|
288
|
+
}
|
|
289
|
+
classifyObjectTail(initialIndex) {
|
|
290
|
+
const index = this.skipModifiers(initialIndex);
|
|
291
|
+
if (index >= this.tokens.length)
|
|
292
|
+
return 'factual';
|
|
293
|
+
const term = this.valueAt(index);
|
|
294
|
+
if (OBJECT_COORDINATOR_TERMS.has(term))
|
|
295
|
+
return this.classifyCoordinatedTail(index + 1);
|
|
296
|
+
if (RELATIVE_TERMS.has(term))
|
|
297
|
+
return this.classifyRelativeTail(index + 1);
|
|
298
|
+
if (this.input.lexicon.commaDelimitedPrepositions.has(term)) {
|
|
299
|
+
return this.classifyObjectTail(this.consumeTerminalNominal(index + 1));
|
|
300
|
+
}
|
|
301
|
+
if (this.input.lexicon.conditionalMarkers.has(term))
|
|
302
|
+
return 'conditional';
|
|
303
|
+
if (SUBORDINATOR_TERMS.has(term))
|
|
304
|
+
return this.classifySubordinateTail(index + 1);
|
|
305
|
+
if (this.isFactualTailPrefix(term))
|
|
306
|
+
return 'factual';
|
|
307
|
+
if (term.endsWith('ing'))
|
|
308
|
+
return this.classifyObjectTail(this.consumeTerminalNominal(index + 1));
|
|
309
|
+
return 'conditional';
|
|
310
|
+
}
|
|
311
|
+
classifyCoordinatedTail(initialIndex) {
|
|
312
|
+
let index = this.skipModifiers(initialIndex);
|
|
313
|
+
if (index >= this.tokens.length)
|
|
314
|
+
return 'factual';
|
|
315
|
+
const coordinatedTerm = this.valueAt(index);
|
|
316
|
+
if (this.input.actionForms.has(coordinatedTerm) && this.isPastPredicate(coordinatedTerm)) {
|
|
317
|
+
index = this.skipModifiers(index + 1);
|
|
318
|
+
if (index >= this.tokens.length)
|
|
319
|
+
return 'factual';
|
|
320
|
+
if (this.input.lexicon.commaDelimitedPrepositions.has(this.valueAt(index))) {
|
|
321
|
+
return this.classifyObjectTail(index);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return this.classifyObjectTail(this.consumeTerminalNominal(index));
|
|
325
|
+
}
|
|
326
|
+
isFactualTailPrefix(term) {
|
|
327
|
+
return FACTUAL_TAIL_PREFIX_TERMS.has(term)
|
|
328
|
+
|| this.input.lexicon.commaDelimitedPrepositions.has(term)
|
|
329
|
+
|| FACTUAL_COORDINATOR_TERMS.has(term);
|
|
330
|
+
}
|
|
331
|
+
valueAt(index) {
|
|
332
|
+
return this.tokens[index]?.value ?? '';
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
export function classifyProvidedTargetPrefix(input) {
|
|
336
|
+
if (!input.source || input.objectForms.length === 0)
|
|
337
|
+
return 'none';
|
|
338
|
+
return new ProvidedTargetPrefixClassifier(input).classify();
|
|
339
|
+
}
|
package/dist/trace/index.d.ts
CHANGED
package/dist/trace/index.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type TraceReadOptions } from './trajectoryExport.js';
|
|
2
|
+
import { type TraceTurnDraft } from './trajectory.js';
|
|
3
|
+
/** One agent run's wall-clock window (epoch ms, same host clock as the proxy's ts). */
|
|
4
|
+
export interface RunTurnWindow {
|
|
5
|
+
startMs: number;
|
|
6
|
+
endMs: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SelectRunLlmTurnsOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Exact-match correlation key: when the caller knows the spawned agent's session id, only that session's
|
|
11
|
+
* turns are returned (the window heuristic is skipped — the id is authoritative).
|
|
12
|
+
*/
|
|
13
|
+
sessionId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CollectRunLlmTurnsOptions extends SelectRunLlmTurnsOptions {
|
|
16
|
+
/** Decryption material forwarded to readTraceRowsFromJsonl. allowPartial is always forced on. */
|
|
17
|
+
readOptions?: TraceReadOptions;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Pure selector (unit-testable without fs): pick the turns that belong to the run per the correlation
|
|
21
|
+
* contract above, sorted by ts ascending (stable — equal timestamps keep day-file append order, and the
|
|
22
|
+
* recorder's fold order becomes the sequence order).
|
|
23
|
+
*/
|
|
24
|
+
export declare function selectRunLlmTurns(turns: readonly TraceTurnDraft[], window: RunTurnWindow, opts?: SelectRunLlmTurnsOptions): TraceTurnDraft[];
|
|
25
|
+
/**
|
|
26
|
+
* Read the proxy trace day-files in `dir` and return this run's llm_turns (see the correlation contract
|
|
27
|
+
* above), ready to fold via recordLlmTurn. Reuses readTraceRowsFromJsonl (decryption + row parsing) and
|
|
28
|
+
* traceRecordToTurnDraft (normalization). Never throws: any failure — no proxy, missing dir, unreadable
|
|
29
|
+
* file, undecryptable rows — degrades to [].
|
|
30
|
+
*/
|
|
31
|
+
export declare function collectRunLlmTurns(dir: string, window: RunTurnWindow, opts?: CollectRunLlmTurnsOptions): TraceTurnDraft[];
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Proxy llm_turn → run fold (Learning Ops slice 5): collect the per-request llm_turn records the LLM proxy
|
|
2
|
+
// captured DURING one agent run's wall-clock window, normalized as TraceTurnDrafts ready for
|
|
3
|
+
// AgentRunTraceRecorder.recordLlmTurn. This is what upgrades a run's trajectory from the bridge's single
|
|
4
|
+
// coarse model.called (the headless runner is a black box) to real per-request fidelity
|
|
5
|
+
// (provider/model/usage/latency/stop_reason + tool-call detail).
|
|
6
|
+
//
|
|
7
|
+
// Correlation contract (why time window + session-first-turn, not session_id alone): the headless runner does
|
|
8
|
+
// not report its session id back to the bridge (`claude -p --output-format text` is opaque), and llm_turn rows
|
|
9
|
+
// carry no cwd — so the run has no exact key to look up. What the run DOES own is its wall-clock window on the
|
|
10
|
+
// same host the proxy writes from (one shared clock, no skew). A turn belongs to the run iff:
|
|
11
|
+
// 1. its ts falls inside [startMs, endMs], AND
|
|
12
|
+
// 2. its session's FIRST observed turn also falls inside the window — a session spawned by this run cannot
|
|
13
|
+
// have traffic predating the run, while a concurrent interactive session (started earlier) is excluded by
|
|
14
|
+
// its pre-window history. Sessionless turns (session_id null) fall back to the window test alone.
|
|
15
|
+
// Callers that DO know the spawned agent's session id (e.g. a future runner passing --session-id) can pass
|
|
16
|
+
// `sessionId` for exact-match correlation instead of the heuristic.
|
|
17
|
+
//
|
|
18
|
+
// Residual risk, accepted + documented: an interactive session whose very first request starts inside the run
|
|
19
|
+
// window is indistinguishable from the run's own agent. On an unattended daemon host this is rare, and the
|
|
20
|
+
// fold is observability-only — it can bias a trace, never a verdict.
|
|
21
|
+
//
|
|
22
|
+
// Everything degrades silently to [] (missing dir, unreadable file, undecryptable envelope, bad ts): the
|
|
23
|
+
// learning trace must never fail or slow a task.
|
|
24
|
+
import { readdirSync, readFileSync } from 'node:fs';
|
|
25
|
+
import { join } from 'node:path';
|
|
26
|
+
import { readTraceRowsFromJsonl } from './trajectoryExport.js';
|
|
27
|
+
import { traceRecordToTurnDraft } from './trajectory.js';
|
|
28
|
+
/** Any proxy day-file (`llm-trace-*.jsonl`). */
|
|
29
|
+
const TRACE_FILE_RE = /^llm-trace-.*\.jsonl$/i;
|
|
30
|
+
/** The canonical day-stamped name the proxy's JsonlTraceSink writes (`llm-trace-YYYYMMDD.jsonl`, UTC). */
|
|
31
|
+
const DAY_STAMPED_FILE_RE = /^llm-trace-(\d{8})\.jsonl$/i;
|
|
32
|
+
function utcDayStamp(ms) {
|
|
33
|
+
const d = new Date(ms);
|
|
34
|
+
const p = (n) => String(n).padStart(2, '0');
|
|
35
|
+
return `${d.getUTCFullYear()}${p(d.getUTCMonth() + 1)}${p(d.getUTCDate())}`;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Keep day-stamped files that could contain the window's turns. Heuristic correlation must inspect all earlier
|
|
39
|
+
* day files to prove that an in-window session did not start before the run; exact session-id correlation only
|
|
40
|
+
* needs files that overlap the run window.
|
|
41
|
+
* Non-day-stamped `llm-trace-*.jsonl` names (custom sinks/tests) are kept conservatively — the ts window
|
|
42
|
+
* filter below is the authority; the filename filter only trims read volume.
|
|
43
|
+
*/
|
|
44
|
+
function fileCoversWindow(name, window, includeSessionHistory) {
|
|
45
|
+
const match = DAY_STAMPED_FILE_RE.exec(name);
|
|
46
|
+
if (!match)
|
|
47
|
+
return true;
|
|
48
|
+
const stamp = match[1];
|
|
49
|
+
return (includeSessionHistory || stamp >= utcDayStamp(window.startMs)) && stamp <= utcDayStamp(window.endMs);
|
|
50
|
+
}
|
|
51
|
+
function turnTsMs(turn) {
|
|
52
|
+
if (turn.ts === null)
|
|
53
|
+
return null;
|
|
54
|
+
const ms = Date.parse(turn.ts);
|
|
55
|
+
return Number.isFinite(ms) ? ms : null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Pure selector (unit-testable without fs): pick the turns that belong to the run per the correlation
|
|
59
|
+
* contract above, sorted by ts ascending (stable — equal timestamps keep day-file append order, and the
|
|
60
|
+
* recorder's fold order becomes the sequence order).
|
|
61
|
+
*/
|
|
62
|
+
export function selectRunLlmTurns(turns, window, opts = {}) {
|
|
63
|
+
const stamped = turns
|
|
64
|
+
.map((turn) => ({ turn, tsMs: turnTsMs(turn) }))
|
|
65
|
+
.filter((entry) => entry.tsMs !== null);
|
|
66
|
+
let selected;
|
|
67
|
+
if (opts.sessionId !== undefined) {
|
|
68
|
+
selected = stamped.filter(({ turn }) => turn.session_id === opts.sessionId);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
// First observed turn per session across ALL provided turns (including pre-window rows from the same
|
|
72
|
+
// day files) — this is what tells an in-run spawned session apart from an older concurrent one.
|
|
73
|
+
const firstTsBySession = new Map();
|
|
74
|
+
for (const { turn, tsMs } of stamped) {
|
|
75
|
+
if (turn.session_id === null)
|
|
76
|
+
continue;
|
|
77
|
+
const prev = firstTsBySession.get(turn.session_id);
|
|
78
|
+
if (prev === undefined || tsMs < prev)
|
|
79
|
+
firstTsBySession.set(turn.session_id, tsMs);
|
|
80
|
+
}
|
|
81
|
+
selected = stamped.filter(({ turn, tsMs }) => {
|
|
82
|
+
if (tsMs < window.startMs || tsMs > window.endMs)
|
|
83
|
+
return false;
|
|
84
|
+
if (turn.session_id === null)
|
|
85
|
+
return true;
|
|
86
|
+
const firstTs = firstTsBySession.get(turn.session_id);
|
|
87
|
+
return firstTs !== undefined && firstTs >= window.startMs;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return selected
|
|
91
|
+
.map((entry, index) => ({ ...entry, index }))
|
|
92
|
+
.sort((a, b) => a.tsMs - b.tsMs || a.index - b.index)
|
|
93
|
+
.map(({ turn }) => turn);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Read the proxy trace day-files in `dir` and return this run's llm_turns (see the correlation contract
|
|
97
|
+
* above), ready to fold via recordLlmTurn. Reuses readTraceRowsFromJsonl (decryption + row parsing) and
|
|
98
|
+
* traceRecordToTurnDraft (normalization). Never throws: any failure — no proxy, missing dir, unreadable
|
|
99
|
+
* file, undecryptable rows — degrades to [].
|
|
100
|
+
*/
|
|
101
|
+
export function collectRunLlmTurns(dir, window, opts = {}) {
|
|
102
|
+
try {
|
|
103
|
+
if (!(window.endMs >= window.startMs))
|
|
104
|
+
return [];
|
|
105
|
+
const names = readdirSync(dir)
|
|
106
|
+
.filter((name) => TRACE_FILE_RE.test(name) && fileCoversWindow(name, window, opts.sessionId === undefined))
|
|
107
|
+
.sort();
|
|
108
|
+
const turns = [];
|
|
109
|
+
for (const name of names) {
|
|
110
|
+
const dayMatch = DAY_STAMPED_FILE_RE.exec(name);
|
|
111
|
+
const isHistoricalDay = opts.sessionId === undefined
|
|
112
|
+
&& dayMatch !== null
|
|
113
|
+
&& dayMatch[1] < utcDayStamp(window.startMs);
|
|
114
|
+
let text;
|
|
115
|
+
try {
|
|
116
|
+
text = readFileSync(join(dir, name), 'utf8');
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
// allowPartial forced on: an undecryptable envelope is a coverage gap, never a fold failure.
|
|
122
|
+
const { rows } = readTraceRowsFromJsonl(text, { ...(opts.readOptions ?? {}), allowPartial: true });
|
|
123
|
+
for (const row of rows) {
|
|
124
|
+
const turn = traceRecordToTurnDraft(row);
|
|
125
|
+
if (turn !== null) {
|
|
126
|
+
// Older day files establish that a session predates this run; they can never contribute candidate
|
|
127
|
+
// turns even if a malformed row carries an in-window timestamp.
|
|
128
|
+
turns.push(isHistoricalDay ? { ...turn, ts: new Date(window.startMs - 1).toISOString() } : turn);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return selectRunLlmTurns(turns, window, opts.sessionId !== undefined ? { sessionId: opts.sessionId } : {});
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -38,4 +38,14 @@ export declare function summarizeStdout(stdout: string): string;
|
|
|
38
38
|
export declare function runValidation(plan: ValidationPlan, run: CommandRunner): Promise<{
|
|
39
39
|
results: ValidationResult[];
|
|
40
40
|
passed: boolean;
|
|
41
|
-
}>;
|
|
41
|
+
}>;
|
|
42
|
+
/**
|
|
43
|
+
* 检查验证命令是否安全可作为 Gene.validation 条目使用(忠实移植 v1 policyCheck.isValidationCommandAllowed)。
|
|
44
|
+
* Gene 验证命令只允许 `node …` 形式,不允许非 node 命令(如 pnpm/npm/echo 等)。
|
|
45
|
+
* 安全条件:
|
|
46
|
+
* 1. 必须以 `node ` 开头
|
|
47
|
+
* 2. 不包含命令替换(` 或 $()
|
|
48
|
+
* 3. 不包含 shell 元字符(引号外)
|
|
49
|
+
* 4. 不包含被阻止的 node 标志(-e/--eval/--print 等)
|
|
50
|
+
*/
|
|
51
|
+
export declare function isValidationCommandAllowed(cmd: string): boolean;
|
|
@@ -82,4 +82,35 @@ export async function runValidation(plan, run) {
|
|
|
82
82
|
// 全部 allowed 的命令都 exit 0 才算通过; 有命令被 deny 视为未通过(校验不完整)
|
|
83
83
|
const passed = results.length > 0 && results.every((r) => r.allowed && r.passed);
|
|
84
84
|
return { results, passed };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* 检查验证命令是否安全可作为 Gene.validation 条目使用(忠实移植 v1 policyCheck.isValidationCommandAllowed)。
|
|
88
|
+
* Gene 验证命令只允许 `node …` 形式,不允许非 node 命令(如 pnpm/npm/echo 等)。
|
|
89
|
+
* 安全条件:
|
|
90
|
+
* 1. 必须以 `node ` 开头
|
|
91
|
+
* 2. 不包含命令替换(` 或 $()
|
|
92
|
+
* 3. 不包含 shell 元字符(引号外)
|
|
93
|
+
* 4. 不包含被阻止的 node 标志(-e/--eval/--print 等)
|
|
94
|
+
*/
|
|
95
|
+
export function isValidationCommandAllowed(cmd) {
|
|
96
|
+
const c = String(cmd || '').trim();
|
|
97
|
+
if (!c)
|
|
98
|
+
return false;
|
|
99
|
+
// 只允许 node 命令作为验证命令
|
|
100
|
+
if (!c.startsWith('node '))
|
|
101
|
+
return false;
|
|
102
|
+
// 不允许命令替换
|
|
103
|
+
if (/`|\$\(/.test(c))
|
|
104
|
+
return false;
|
|
105
|
+
// 移除引号内的内容后检查 shell 元字符
|
|
106
|
+
const stripped = c.replace(/"[^"]*"/g, '').replace(/'[^']*'/g, '');
|
|
107
|
+
if (SHELL_METACHARS.test(stripped))
|
|
108
|
+
return false;
|
|
109
|
+
// 检查被阻止的 node 标志
|
|
110
|
+
const tokens = c.split(/\s+/);
|
|
111
|
+
const executable = tokens[0] ?? '';
|
|
112
|
+
const args = tokens.slice(1);
|
|
113
|
+
if (nodeFlagViolation(executable, args))
|
|
114
|
+
return false;
|
|
115
|
+
return true;
|
|
85
116
|
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver-core",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.19",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": "^22.13.0 || >=23.4.0"
|
|
8
|
+
},
|
|
6
9
|
"description": "hub-无关核心: 算法引擎/原材料/mailbox/资产库/workflow",
|
|
7
10
|
"main": "./dist/index.js",
|
|
8
11
|
"types": "./dist/index.d.ts",
|