@evomap/evolver-core 2.0.0-beta.19 → 2.0.0-beta.22

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.
Files changed (160) hide show
  1. package/assets/gep/genes.jsonl +5 -5
  2. package/dist/algo/bans.d.ts +10 -1
  3. package/dist/algo/bans.js +56 -6
  4. package/dist/algo/candidateAssembly.d.ts +25 -2
  5. package/dist/algo/candidateAssembly.js +143 -20
  6. package/dist/algo/capabilityCandidates.js +10 -0
  7. package/dist/algo/cycleEngine.d.ts +40 -5
  8. package/dist/algo/cycleEngine.js +277 -37
  9. package/dist/algo/evolutionEvent.d.ts +26 -0
  10. package/dist/algo/evolutionEvent.js +32 -0
  11. package/dist/algo/exploration.d.ts +7 -0
  12. package/dist/algo/exploration.js +16 -3
  13. package/dist/algo/geneHealth.d.ts +34 -1
  14. package/dist/algo/geneHealth.js +42 -0
  15. package/dist/algo/geneIntake.d.ts +46 -1
  16. package/dist/algo/geneIntake.js +121 -10
  17. package/dist/algo/geneSelection.d.ts +115 -6
  18. package/dist/algo/geneSelection.js +459 -32
  19. package/dist/algo/index.d.ts +4 -1
  20. package/dist/algo/index.js +4 -1
  21. package/dist/algo/kautoProjection.d.ts +41 -0
  22. package/dist/algo/kautoProjection.js +95 -0
  23. package/dist/algo/kautoValidator.d.ts +68 -0
  24. package/dist/algo/kautoValidator.js +256 -0
  25. package/dist/algo/orchestrator.d.ts +14 -1
  26. package/dist/algo/orchestrator.js +9 -2
  27. package/dist/algo/solidify.d.ts +11 -2
  28. package/dist/algo/solidify.js +37 -7
  29. package/dist/algo/ucb1.d.ts +53 -0
  30. package/dist/algo/ucb1.js +156 -0
  31. package/dist/assetrepair/hubRejection.d.ts +12 -0
  32. package/dist/assetrepair/hubRejection.js +109 -0
  33. package/dist/assetrepair/index.d.ts +2 -0
  34. package/dist/assetrepair/index.js +2 -0
  35. package/dist/assetrepair/repair.d.ts +33 -0
  36. package/dist/assetrepair/repair.js +155 -0
  37. package/dist/assetstore/assetSidecarRecords.js +5 -1
  38. package/dist/assetstore/assetStoreHealth.d.ts +2 -3
  39. package/dist/assetstore/assetStoreHealth.js +4 -10
  40. package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
  41. package/dist/assetstore/foreignJsonlSource.js +150 -0
  42. package/dist/assetstore/index.d.ts +3 -0
  43. package/dist/assetstore/index.js +3 -0
  44. package/dist/assetstore/learningHistory.js +3 -3
  45. package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
  46. package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
  47. package/dist/assetstore/localJsonl.d.ts +8 -0
  48. package/dist/assetstore/localJsonl.js +128 -3
  49. package/dist/assetstore/provenance.d.ts +38 -3
  50. package/dist/assetstore/provenance.js +103 -45
  51. package/dist/assetstore/provider.d.ts +4 -0
  52. package/dist/assetstore/reviewFilter.d.ts +19 -1
  53. package/dist/assetstore/reviewFilter.js +36 -0
  54. package/dist/assetstore/unionReadStore.d.ts +25 -0
  55. package/dist/assetstore/unionReadStore.js +119 -0
  56. package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
  57. package/dist/benchmark/antiGeneBenchmark.js +4 -3
  58. package/dist/benchmark/antiGeneRollout.d.ts +2 -0
  59. package/dist/benchmark/antiGeneRollout.js +4 -3
  60. package/dist/benchmark/index.d.ts +2 -1
  61. package/dist/benchmark/index.js +2 -1
  62. package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
  63. package/dist/benchmark/selectionFlatAbstention.js +481 -0
  64. package/dist/bootstrap/envFingerprint.d.ts +9 -0
  65. package/dist/bootstrap/envFingerprint.js +5 -0
  66. package/dist/bootstrap/index.d.ts +2 -1
  67. package/dist/bootstrap/index.js +2 -1
  68. package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
  69. package/dist/bootstrap/lifecycleBootstrap.js +433 -0
  70. package/dist/bootstrap/v1EnvCompat.d.ts +3 -0
  71. package/dist/bootstrap/v1EnvCompat.js +48 -1
  72. package/dist/events/eventArchive.d.ts +2 -0
  73. package/dist/events/eventArchive.js +13 -3
  74. package/dist/events/eventSchema.d.ts +7 -7
  75. package/dist/events/eventStore.d.ts +2 -0
  76. package/dist/events/eventStore.js +5 -1
  77. package/dist/events/ingest.d.ts +2 -1
  78. package/dist/events/ingest.js +6 -0
  79. package/dist/exec/autoExec.d.ts +30 -3
  80. package/dist/exec/autoExec.js +294 -29
  81. package/dist/exec/autonomousCycle.d.ts +7 -0
  82. package/dist/exec/autonomousCycle.js +2 -0
  83. package/dist/exec/claudeBridge.d.ts +28 -6
  84. package/dist/exec/claudeBridge.js +411 -258
  85. package/dist/exec/executionBinding.d.ts +414 -0
  86. package/dist/exec/executionBinding.js +588 -0
  87. package/dist/exec/index.d.ts +1 -0
  88. package/dist/exec/index.js +1 -0
  89. package/dist/exec/proofOfWork.d.ts +1 -1
  90. package/dist/exec/proofOfWork.js +2 -2
  91. package/dist/exec/runnerRegistry.d.ts +5 -0
  92. package/dist/exec/runnerRegistry.js +54 -14
  93. package/dist/hub/capability.d.ts +40 -1
  94. package/dist/hub/conversationDistiller.d.ts +19 -0
  95. package/dist/hub/conversationDistiller.js +115 -37
  96. package/dist/hub/fake.d.ts +1 -0
  97. package/dist/hub/fake.js +1 -0
  98. package/dist/hub/index.d.ts +1 -0
  99. package/dist/hub/index.js +1 -0
  100. package/dist/hub/recipeCompose.d.ts +27 -0
  101. package/dist/hub/recipeCompose.js +90 -0
  102. package/dist/hub/sanitize.js +119 -5
  103. package/dist/index.d.ts +2 -0
  104. package/dist/index.js +2 -0
  105. package/dist/mailbox/ipcServer.d.ts +2 -0
  106. package/dist/mailbox/ipcServer.js +20 -0
  107. package/dist/mailbox/store.d.ts +52 -2
  108. package/dist/mailbox/store.js +546 -81
  109. package/dist/modelCompatibility.d.ts +164 -0
  110. package/dist/modelCompatibility.js +309 -0
  111. package/dist/observers/valueDigestObserver.d.ts +9 -0
  112. package/dist/observers/valueDigestObserver.js +35 -2
  113. package/dist/ops/cleanup.js +1 -1
  114. package/dist/ops/evolutionGraphProjection.d.ts +20 -0
  115. package/dist/ops/evolutionGraphProjection.js +315 -0
  116. package/dist/ops/index.d.ts +2 -1
  117. package/dist/ops/index.js +2 -1
  118. package/dist/ops/selfUpdate.d.ts +8 -0
  119. package/dist/ops/selfUpdate.js +24 -8
  120. package/dist/ops/valueOutreach.d.ts +3 -1
  121. package/dist/ops/valueOutreach.js +5 -1
  122. package/dist/personality/schema.d.ts +18 -18
  123. package/dist/schema/evolutionGraph.d.ts +784 -0
  124. package/dist/schema/evolutionGraph.js +187 -0
  125. package/dist/schema/index.d.ts +1 -0
  126. package/dist/schema/index.js +1 -0
  127. package/dist/schema/proofOfWork.d.ts +125 -6
  128. package/dist/schema/proofOfWork.js +102 -4
  129. package/dist/shadow/shadowHub.js +1 -0
  130. package/dist/signals/expand.d.ts +15 -1
  131. package/dist/signals/expand.js +148 -0
  132. package/dist/signals/index.d.ts +3 -1
  133. package/dist/signals/index.js +3 -1
  134. package/dist/signals/scopeVocabulary.d.ts +75 -0
  135. package/dist/signals/scopeVocabulary.js +91 -0
  136. package/dist/signals/taskDomain.d.ts +22 -0
  137. package/dist/signals/taskDomain.js +43 -0
  138. package/dist/trace/learningTrace.d.ts +7 -0
  139. package/dist/trace/learningTrace.js +22 -0
  140. package/dist/trace/trajectory.d.ts +8 -0
  141. package/dist/trace/trajectory.js +14 -2
  142. package/dist/util/fileLock.d.ts +92 -5
  143. package/dist/util/fileLock.js +809 -70
  144. package/dist/util/index.d.ts +2 -1
  145. package/dist/util/index.js +1 -1
  146. package/dist/verify/index.d.ts +2 -1
  147. package/dist/verify/index.js +1 -1
  148. package/dist/verify/sandboxRunner.d.ts +2 -0
  149. package/dist/verify/sandboxRunner.js +154 -10
  150. package/dist/verify/sandboxedValidation.d.ts +7 -2
  151. package/dist/verify/sandboxedValidation.js +102 -24
  152. package/dist/verify/validation.d.ts +24 -3
  153. package/dist/verify/validation.js +141 -24
  154. package/dist/wire/geneHints.d.ts +88 -16
  155. package/dist/wire/geneHints.js +124 -15
  156. package/dist/wire/index.d.ts +8 -3
  157. package/dist/wire/index.js +2 -2
  158. package/dist/wire/schemaGate.d.ts +21 -0
  159. package/dist/wire/schemaGate.js +119 -10
  160. package/package.json +5 -4
@@ -1,11 +1,15 @@
1
1
  import { StrategyPoint } from '../strategy/strategyPoint.js';
2
2
  import { geneHealthScore, HEALTH_WEIGHTS_VERSION } from './geneHealth.js';
3
- import { tagOverlapScore, bagCosine } from '../signals/expand.js';
4
- import { driftSelect } from './exploration.js';
3
+ import { bagCosine, buildSemanticIdfProfile, geneTags, idfBagCosine, idfTagOverlapScore, tagOverlapScore, } from '../signals/expand.js';
4
+ import { driftSelect, explorationWindowSize } from './exploration.js';
5
5
  import { isDistilledGeneId } from './geneIntake.js';
6
+ import { chooseUcb1Arm, ucb1StatsForCandidate, UCB1_REWARD_POLICY_VERSION, UCB1_SELECTION_POLICY_VERSION, } from './ucb1.js';
7
+ import { resolveTaskDomainSignals, withoutTaskDomainSignals, } from '../signals/taskDomain.js';
8
+ export const SELECTION_GUARD_VERSION = 'relevance-guard-v1';
6
9
  function decisionWithWarnings(input, decision) {
7
10
  const selected = decision.selectedGeneId
8
- ? decision.candidates.find((candidate) => candidate.geneId === decision.selectedGeneId || candidate.assetId === decision.selectedAssetId)
11
+ ? decision.candidates.find((candidate) => (candidate.geneId === decision.selectedGeneId
12
+ || (decision.selectedAssetId !== undefined && candidate.assetId === decision.selectedAssetId)))
9
13
  : undefined;
10
14
  const enriched = {
11
15
  ...decision,
@@ -17,11 +21,13 @@ function decisionWithWarnings(input, decision) {
17
21
  : enriched;
18
22
  }
19
23
  function signalMatchScore(signals, match) {
20
- if (signals.length === 0 || match.length === 0)
24
+ const genericSignals = withoutTaskDomainSignals(signals);
25
+ const genericMatch = withoutTaskDomainSignals(match);
26
+ if (genericSignals.length === 0 || genericMatch.length === 0)
21
27
  return 0;
22
- const set = new Set(signals);
23
- const hit = match.filter((m) => set.has(m)).length;
24
- return hit / match.length; // 覆盖率
28
+ const set = new Set(genericSignals);
29
+ const hit = genericMatch.filter((m) => set.has(m)).length;
30
+ return hit / genericMatch.length; // 覆盖率
25
31
  }
26
32
  // Weight of the bag-of-words cosine path when it fills the recall gap left by literal + tag overlap.
27
33
  const SEMANTIC_WEIGHT = 0.5;
@@ -39,12 +45,88 @@ export const CONFIDENCE_WEIGHT = 0.15;
39
45
  export const REUSE_WEIGHT = 0.1;
40
46
  /** Weight of scoped local MemoryGraph outcome evidence. */
41
47
  export const MEMORY_GRAPH_WEIGHT = 0.12;
48
+ /** Bounded weight for a canonical task-domain signal match (#628). */
49
+ export const TASK_DOMAIN_WEIGHT = 0.08;
50
+ /** signals_match is weak domain evidence; its maximum score contribution is 0.08 * 0.5 = 0.04. */
51
+ export const TASK_DOMAIN_SIGNAL_EVIDENCE = 0.5;
52
+ /**
53
+ * Soft boost for strict K_auto members. Smaller than CONFIDENCE_WEIGHT: membership is a writer-side property,
54
+ * not verified cycle history, so it only breaks near-ties between already-eligible candidates.
55
+ */
56
+ export const KAUTO_WEIGHT = 0.05;
57
+ /** λ is intentionally NOT inlined into SELECTION_WEIGHTS_VERSION text:
58
+ * decision.gene_selected root events already sit within ~2B of the 4096B line budget
59
+ * under UCB1/plateau fixtures; a `,kauto=0.05` suffix drops selectionPolicy. */
60
+ const SELECTION_GUARD_MATCH_EPS = 1e-6;
42
61
  /**
43
62
  * Version of the full engine-health weight vector (health 0.6 + signal-match 0.4 − epigenetic penalty
44
- * + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment). Bumped whenever a factor is added so golden
45
- * weight snapshots track the change. Composed from the health-weights version so a change to either layer shows.
63
+ * + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment + KAUTO_WEIGHT × kauto-member).
64
+ * Bumped whenever a factor is added so golden weight snapshots track the change. Composed from the
65
+ * health-weights version so a change to either layer shows.
46
66
  */
47
- export const SELECTION_WEIGHTS_VERSION = `sel-4(${HEALTH_WEIGHTS_VERSION},conf=${CONFIDENCE_WEIGHT},memory=${MEMORY_GRAPH_WEIGHT},reuse=${REUSE_WEIGHT})`;
67
+ export const LEGACY_SELECTION_WEIGHTS_VERSION = `sel-6-domain(${HEALTH_WEIGHTS_VERSION},conf=${CONFIDENCE_WEIGHT},memory=${MEMORY_GRAPH_WEIGHT},reuse=${REUSE_WEIGHT},domain=${TASK_DOMAIN_WEIGHT})`;
68
+ export const SELECTION_WEIGHTS_VERSION = `sel-7-idf-domain(${HEALTH_WEIGHTS_VERSION},conf=${CONFIDENCE_WEIGHT},memory=${MEMORY_GRAPH_WEIGHT},reuse=${REUSE_WEIGHT},domain=${TASK_DOMAIN_WEIGHT})`;
69
+ const SEMANTIC_CORPUS_LIMIT = 1_000;
70
+ const SEMANTIC_SIGNAL_LIMIT = 16;
71
+ const SEMANTIC_SIGNAL_CHARS = 96;
72
+ const SEMANTIC_GENE_ID_CHARS = 128;
73
+ const SEMANTIC_CATEGORY_CHARS = 96;
74
+ const SEMANTIC_SUMMARY_CHARS = 1_024;
75
+ const SEMANTIC_IDENTITY_CHARS = 256;
76
+ function legacyCandidateSemanticText(candidate) {
77
+ return [
78
+ ...withoutTaskDomainSignals(candidate.signalsMatch),
79
+ candidate.category ?? '',
80
+ candidate.summary ?? '',
81
+ ].join(' ');
82
+ }
83
+ function boundedSignals(signals) {
84
+ const out = [];
85
+ const genericSignals = withoutTaskDomainSignals(signals);
86
+ const limit = Math.min(genericSignals.length, SEMANTIC_SIGNAL_LIMIT);
87
+ for (let index = 0; index < limit; index += 1) {
88
+ const signal = genericSignals[index];
89
+ if (typeof signal === 'string')
90
+ out.push(signal.slice(0, SEMANTIC_SIGNAL_CHARS));
91
+ }
92
+ return out;
93
+ }
94
+ function semanticCandidateProjection(candidate) {
95
+ const signalsMatch = boundedSignals(candidate.signalsMatch);
96
+ const geneId = candidate.geneId.slice(0, SEMANTIC_GENE_ID_CHARS);
97
+ const category = candidate.category?.slice(0, SEMANTIC_CATEGORY_CHARS);
98
+ const summary = candidate.summary?.slice(0, SEMANTIC_SUMMARY_CHARS);
99
+ return {
100
+ tagInput: {
101
+ signalsMatch,
102
+ geneId,
103
+ ...(category ? { category } : {}),
104
+ ...(summary ? { summary } : {}),
105
+ },
106
+ text: [...signalsMatch, category ?? '', summary ?? ''].join(' '),
107
+ hasContent: signalsMatch.some((signal) => signal.trim().length > 0)
108
+ || Boolean(category?.trim())
109
+ || Boolean(summary?.trim()),
110
+ };
111
+ }
112
+ function buildSelectionSemanticProfile(corpus) {
113
+ const documents = [];
114
+ const identities = new Set();
115
+ for (const candidate of corpus) {
116
+ if (documents.length >= SEMANTIC_CORPUS_LIMIT)
117
+ break;
118
+ const identityValue = candidate.assetId ?? candidate.geneId;
119
+ const identity = `${candidate.assetId ? 'asset' : 'gene'}:${identityValue.slice(0, SEMANTIC_IDENTITY_CHARS)}`;
120
+ if (identities.has(identity))
121
+ continue;
122
+ identities.add(identity);
123
+ const projection = semanticCandidateProjection(candidate);
124
+ if (!projection.hasContent)
125
+ continue;
126
+ documents.push({ tags: geneTags(projection.tagInput), text: projection.text });
127
+ }
128
+ return buildSemanticIdfProfile(documents);
129
+ }
48
130
  /**
49
131
  * Match score with semantic signal expansion (ported from v1): literal coverage first, then fill the
50
132
  * recall gap with semantic tag overlap, then with bag-of-words cosine similarity. A perfect literal match
@@ -52,22 +134,43 @@ export const SELECTION_WEIGHTS_VERSION = `sel-4(${HEALTH_WEIGHTS_VERSION},conf=$
52
134
  * for 'repair'), and cosine catches lexical similarity the curated tags miss (e.g. a 'timeout_slow' signal
53
135
  * vs a gene summarized "fix the slow timeout"). Each layer only fills the gap the previous one left.
54
136
  */
55
- function expandedMatchScore(signals, c) {
137
+ function expandedMatchScore(signals, c, semanticProfile) {
56
138
  const literal = signalMatchScore(signals, c.signalsMatch);
57
139
  if (literal >= 1)
58
140
  return { score: 1, literal, tag: 0, cos: 0 };
59
- const tag = tagOverlapScore(signals, { signalsMatch: c.signalsMatch, geneId: c.geneId, category: c.category, summary: c.summary });
141
+ const projection = semanticProfile ? semanticCandidateProjection(c) : undefined;
142
+ const semanticSignals = semanticProfile ? boundedSignals(signals) : withoutTaskDomainSignals(signals);
143
+ const tag = semanticProfile
144
+ ? idfTagOverlapScore(semanticSignals, projection.tagInput, semanticProfile)
145
+ : tagOverlapScore(semanticSignals, {
146
+ signalsMatch: withoutTaskDomainSignals(c.signalsMatch),
147
+ geneId: c.geneId,
148
+ category: c.category,
149
+ summary: c.summary,
150
+ });
60
151
  const base = Math.min(1, literal + (1 - literal) * tag);
61
152
  if (base >= 1)
62
153
  return { score: 1, literal, tag, cos: 0 };
63
- const geneText = [...c.signalsMatch, c.category ?? '', c.summary ?? ''].join(' ');
64
- const cos = bagCosine(signals.join(' '), geneText);
154
+ const cos = semanticProfile
155
+ ? idfBagCosine(semanticSignals.join(' '), projection.text, semanticProfile)
156
+ : bagCosine(semanticSignals.join(' '), legacyCandidateSemanticText(c));
65
157
  return { score: Math.min(1, base + (1 - base) * cos * SEMANTIC_WEIGHT), literal, tag, cos };
66
158
  }
67
- /** Score one candidate under the engine-health weight vector (health 0.6 + 信号匹配 0.4 − epigenetic + confidence). */
68
- function scoreCandidate(signals, c) {
159
+ function taskDomainEvidence(queryDomain, candidateSignals) {
160
+ if (queryDomain.status !== 'resolved')
161
+ return 0;
162
+ const candidateDomain = resolveTaskDomainSignals(candidateSignals);
163
+ return candidateDomain.status === 'resolved' && candidateDomain.slug === queryDomain.slug
164
+ ? TASK_DOMAIN_SIGNAL_EVIDENCE
165
+ : 0;
166
+ }
167
+ /**
168
+ * Score one candidate under the engine-health weight vector, including bounded task-domain evidence.
169
+ * `kautoWeight` defaults to production KAUTO_WEIGHT; ablation passes λ ∈ {0,0.01,0.05,0.1}.
170
+ */
171
+ function scoreCandidate(signals, c, semanticProfile, queryDomain, kautoWeight = KAUTO_WEIGHT) {
69
172
  const health = geneHealthScore(c.view, { reuseCount: c.reuseCount, antiPatternCount: c.antiPatternCount });
70
- const m = expandedMatchScore(signals, c);
173
+ const m = expandedMatchScore(signals, c, semanticProfile);
71
174
  const epi = c.epigeneticPenalty ?? 0;
72
175
  // Confidence is the fourth (positive) factor: a [0,1] preferred-gene edge for this signal fingerprint.
73
176
  // Clamped defensively so an upstream bug cannot turn the soft nudge into a dominant term.
@@ -76,7 +179,15 @@ function scoreCandidate(signals, c) {
76
179
  // into a dominant (or unbounded) term. Absent → 0 → no effect (default-off until a caller injects it).
77
180
  const reuse = Math.max(-1, Math.min(1, c.reuseAdjust ?? 0));
78
181
  const memory = Math.max(-1, Math.min(1, c.memoryBoost ?? 0));
79
- const score = 0.6 * health.score + 0.4 * m.score - epi + CONFIDENCE_WEIGHT * conf + MEMORY_GRAPH_WEIGHT * memory + REUSE_WEIGHT * reuse;
182
+ const domainEvidence = taskDomainEvidence(queryDomain, c.signalsMatch);
183
+ const domainContribution = TASK_DOMAIN_WEIGHT * domainEvidence;
184
+ // Soft K_auto membership preference: binary, already-admitted candidates only.
185
+ const kauto = c.kautoMember === true ? 1 : 0;
186
+ const kautoContribution = kautoWeight * kauto;
187
+ // score_base is everything except the K_auto soft term — used by λ ablation.
188
+ const scoreBase = 0.6 * health.score + 0.4 * m.score - epi + CONFIDENCE_WEIGHT * conf
189
+ + MEMORY_GRAPH_WEIGHT * memory + REUSE_WEIGHT * reuse + domainContribution;
190
+ const score = scoreBase + kautoContribution;
80
191
  const breakdown = m.tag > 0 || m.cos > 0 ? `(literal=${m.literal.toFixed(2)}+tag=${m.tag.toFixed(2)}+cos=${m.cos.toFixed(2)})` : '';
81
192
  const reasons = [`health=${health.score.toFixed(3)}(succ=${health.successRate.toFixed(2)},reuse=${health.reuseCount})`, `信号匹配=${m.score.toFixed(2)}${breakdown}`];
82
193
  if (epi > 0)
@@ -87,7 +198,119 @@ function scoreCandidate(signals, c) {
87
198
  reasons.push(`scoped memory-graph outcome ${memory >= 0 ? '+' : ''}${(MEMORY_GRAPH_WEIGHT * memory).toFixed(3)} (boost=${memory.toFixed(2)})`);
88
199
  if (reuse !== 0)
89
200
  reasons.push(`cross-runtime reuse ${reuse >= 0 ? '+' : ''}${(REUSE_WEIGHT * reuse).toFixed(3)} (sentiment=${reuse.toFixed(2)})`);
90
- return { geneId: c.geneId, ...(c.assetId ? { assetId: c.assetId } : {}), score, health, reasons };
201
+ if (domainContribution > 0)
202
+ reasons.push(`task_domain match +${domainContribution.toFixed(3)} (evidence=${domainEvidence.toFixed(2)})`);
203
+ if (kautoContribution > 0)
204
+ reasons.push(`K_auto member +${kautoContribution.toFixed(3)}`);
205
+ // Keep scoreBase/kautoContribution off ScoredCandidate so decision.gene_selected
206
+ // root-event candidate payloads retain the pre-K_auto 4096B line budget.
207
+ return {
208
+ geneId: c.geneId,
209
+ ...(c.assetId ? { assetId: c.assetId } : {}),
210
+ score,
211
+ matchScore: m.score,
212
+ health,
213
+ reasons,
214
+ };
215
+ }
216
+ function candidateIdentity(c) {
217
+ return c.assetId ? `asset:${c.assetId}` : `gene:${c.geneId}`;
218
+ }
219
+ /**
220
+ * Offline λ ablation over an already-admitted candidate pool.
221
+ * Does not run hard gates / force / distilled fallback — those stay outside the soft preference.
222
+ * Pure ranking sensitivity for score_T2 = score_base + λ · 1[k_a ∈ K_auto].
223
+ */
224
+ export function ablateKautoLambda(input, lambdas = [0, 0.01, 0.05, 0.1], opts = {}) {
225
+ const topK = opts.topK ?? 3;
226
+ const floor = opts.floor ?? 0;
227
+ const semanticProfile = input.disableSemanticIdf || input.semanticCorpus === undefined
228
+ ? undefined
229
+ : buildSelectionSemanticProfile(input.semanticCorpus);
230
+ const queryDomain = resolveTaskDomainSignals(input.signals);
231
+ const pools = lambdas.map((lambda) => {
232
+ const scored = input.candidates
233
+ .map((c) => {
234
+ const s = scoreCandidate(input.signals, c, semanticProfile, queryDomain, lambda);
235
+ const kautoContribution = (c.kautoMember === true ? 1 : 0) * lambda;
236
+ return {
237
+ geneId: s.geneId,
238
+ ...(s.assetId ? { assetId: s.assetId } : {}),
239
+ score: s.score,
240
+ scoreBase: s.score - kautoContribution,
241
+ kautoContribution,
242
+ kautoMember: c.kautoMember === true,
243
+ };
244
+ })
245
+ .sort((a, b) => b.score - a.score || (a.assetId ?? a.geneId).localeCompare(b.assetId ?? b.geneId));
246
+ const eligible = scored.filter((s) => s.score > floor);
247
+ const chosen = eligible[0];
248
+ const bases = scored.map((s) => s.scoreBase);
249
+ const scoreBaseMin = bases.length ? Math.min(...bases) : 0;
250
+ const scoreBaseMax = bases.length ? Math.max(...bases) : 0;
251
+ const scoreBaseMean = bases.length ? bases.reduce((a, b) => a + b, 0) / bases.length : 0;
252
+ return {
253
+ lambda,
254
+ ranking: scored.map((s, index) => ({
255
+ rank: index + 1,
256
+ geneId: s.geneId,
257
+ ...(s.assetId ? { assetId: s.assetId } : {}),
258
+ score: s.score,
259
+ scoreBase: s.scoreBase,
260
+ kautoContribution: s.kautoContribution,
261
+ kautoMember: s.kautoMember,
262
+ })),
263
+ selectedGeneId: chosen?.geneId ?? null,
264
+ ...(chosen?.assetId ? { selectedAssetId: chosen.assetId } : {}),
265
+ scoreBaseMin,
266
+ scoreBaseMax,
267
+ scoreBaseMean,
268
+ };
269
+ });
270
+ const baseline = pools.find((p) => p.lambda === 0) ?? pools[0];
271
+ const baselineRank = new Map(baseline?.ranking.map((r) => [candidateIdentity(r), r.rank]) ?? []);
272
+ const baselineTopK = new Set((baseline?.ranking.slice(0, topK) ?? []).map((r) => candidateIdentity(r)));
273
+ const baselineSelected = baseline
274
+ ? candidateIdentity({
275
+ geneId: baseline.selectedGeneId ?? '',
276
+ ...(baseline.selectedAssetId ? { assetId: baseline.selectedAssetId } : {}),
277
+ })
278
+ : '';
279
+ const rankChangesByLambda = {};
280
+ const selectedChangesByLambda = {};
281
+ const topKChangesByLambda = {};
282
+ for (const pool of pools) {
283
+ const key = String(pool.lambda);
284
+ let rankChanges = 0;
285
+ for (const row of pool.ranking) {
286
+ const id = candidateIdentity(row);
287
+ if ((baselineRank.get(id) ?? -1) !== row.rank)
288
+ rankChanges += 1;
289
+ }
290
+ rankChangesByLambda[key] = rankChanges;
291
+ const selectedId = candidateIdentity({
292
+ geneId: pool.selectedGeneId ?? '',
293
+ ...(pool.selectedAssetId ? { assetId: pool.selectedAssetId } : {}),
294
+ });
295
+ selectedChangesByLambda[key] = selectedId !== baselineSelected;
296
+ const topSet = new Set(pool.ranking.slice(0, topK).map((r) => candidateIdentity(r)));
297
+ let topDiff = 0;
298
+ for (const id of topSet)
299
+ if (!baselineTopK.has(id))
300
+ topDiff += 1;
301
+ for (const id of baselineTopK)
302
+ if (!topSet.has(id))
303
+ topDiff += 1;
304
+ topKChangesByLambda[key] = topDiff;
305
+ }
306
+ return {
307
+ lambdas,
308
+ pools,
309
+ rankChangesByLambda,
310
+ selectedChangesByLambda,
311
+ topKChangesByLambda,
312
+ topK,
313
+ };
91
314
  }
92
315
  function matchesForcedGeneId(c, forcedGeneId) {
93
316
  return c.geneId === forcedGeneId || c.assetId === forcedGeneId;
@@ -101,6 +324,23 @@ function matchesScoredIdentity(a, b) {
101
324
  function withoutCandidateIdentity(scored, c) {
102
325
  return scored.filter((s) => !matchesCandidateIdentity(s, c));
103
326
  }
327
+ function candidateForScore(input, scored) {
328
+ // Eligibility is attached to one assembled candidate, not to every logical alias of the same content.
329
+ // Match the exact scored row so an untrusted alias cannot borrow a trusted candidate's UCB1 admission bit.
330
+ return input.candidates.find((candidate) => (candidate.geneId === scored.geneId && candidate.assetId === scored.assetId));
331
+ }
332
+ function compactMetric(value) {
333
+ return Math.round(value * 1_000_000) / 1_000_000;
334
+ }
335
+ function compactChoice(choice) {
336
+ return {
337
+ ...choice,
338
+ armId: choice.armId.slice(0, 160),
339
+ meanReward: compactMetric(choice.meanReward),
340
+ ...(choice.bonus !== null ? { bonus: compactMetric(choice.bonus) } : { bonus: null }),
341
+ ...(choice.index !== null ? { index: compactMetric(choice.index) } : { index: null }),
342
+ };
343
+ }
104
344
  function isReusableGenerationSource(source) {
105
345
  return source === 'distilled' || source === 'evolved';
106
346
  }
@@ -146,20 +386,150 @@ function forcedSelection(input, scored, scoreForcedFallback, floor) {
146
386
  forcedFallback.reasons.push(`forced gene selected despite floor ${floor}`);
147
387
  return { selectedGeneId: fallback.geneId, ...(fallback.assetId ? { selectedAssetId: fallback.assetId } : {}), scoredForChoice: scoredCandidatesOnly };
148
388
  }
389
+ /** Refs #626: identify selections whose relevance is absent or cannot discriminate during a plateau. */
390
+ export function assessSelectionGuard(scored, plateauActive) {
391
+ if (scored.length === 0) {
392
+ return { wouldAbstain: true, reason: 'no_match', maxMatch: 0, matchSpread: 0 };
393
+ }
394
+ const matches = scored.map((candidate) => candidate.matchScore ?? 0);
395
+ const maxMatch = Math.max(...matches);
396
+ const minMatch = Math.min(...matches);
397
+ const matchSpread = maxMatch - minMatch;
398
+ const metrics = {
399
+ maxMatch: compactMetric(maxMatch),
400
+ matchSpread: compactMetric(matchSpread),
401
+ };
402
+ if (maxMatch <= SELECTION_GUARD_MATCH_EPS) {
403
+ return { wouldAbstain: true, reason: 'no_match', ...metrics };
404
+ }
405
+ if (plateauActive && scored.length > 1 && matchSpread <= SELECTION_GUARD_MATCH_EPS) {
406
+ return { wouldAbstain: true, reason: 'plateau_flat_match', ...metrics };
407
+ }
408
+ return { wouldAbstain: false, ...metrics };
409
+ }
149
410
  /** 实现1: engine 健康分主导(health 0.6 + 信号匹配 0.4). */
150
411
  export const engineHealthSelection = {
151
412
  name: 'engine-health',
152
413
  version: '1',
153
414
  run(input, ctx) {
154
- const scored = input.candidates.map((c) => scoreCandidate(input.signals, c)).sort((a, b) => b.score - a.score);
415
+ const semanticProfile = input.disableSemanticIdf || input.semanticCorpus === undefined
416
+ ? undefined
417
+ : buildSelectionSemanticProfile(input.semanticCorpus);
418
+ const weightsVersion = semanticProfile ? SELECTION_WEIGHTS_VERSION : LEGACY_SELECTION_WEIGHTS_VERSION;
419
+ const semanticMetadata = semanticProfile
420
+ ? {
421
+ semanticProfileVersion: semanticProfile.version,
422
+ semanticDocumentCount: semanticProfile.documentCount,
423
+ }
424
+ : {};
425
+ const queryDomain = resolveTaskDomainSignals(input.signals);
426
+ const scoreForSelection = (candidate) => (scoreCandidate(input.signals, candidate, semanticProfile, queryDomain));
427
+ const scored = input.candidates.map(scoreForSelection).sort((a, b) => b.score - a.score);
155
428
  const floor = input.floor ?? 0;
156
- const forced = forcedSelection(input, scored, (c) => scoreCandidate(input.signals, c), floor);
429
+ const forced = forcedSelection(input, scored, scoreForSelection, floor);
157
430
  const eligible = forced.scoredForChoice.filter((s) => s.score > floor);
158
- // Deterministic top by default; exploration drift picks from the top-N to escape local optima.
159
- const drift = driftSelect(eligible.length, input.exploration, ctx?.rng ?? Math.random);
160
- const chosen = eligible[drift.index] ?? eligible[0];
431
+ const guardMode = input.selectionGuard ?? 'legacy';
432
+ const plateauActive = input.exploration?.plateau?.active === true;
433
+ let guardAssessment = assessSelectionGuard(eligible, plateauActive);
434
+ if (eligible.length === 0) {
435
+ const fullAssessment = assessSelectionGuard(forced.scoredForChoice, plateauActive);
436
+ if (fullAssessment.reason !== 'no_match') {
437
+ guardAssessment = {
438
+ wouldAbstain: false,
439
+ ...(fullAssessment.maxMatch !== undefined ? { maxMatch: fullAssessment.maxMatch } : {}),
440
+ ...(fullAssessment.matchSpread !== undefined ? { matchSpread: fullAssessment.matchSpread } : {}),
441
+ };
442
+ }
443
+ }
444
+ const policyEligible = guardMode === 'enforce' && guardAssessment.reason === 'no_match'
445
+ ? []
446
+ : eligible;
447
+ // Deterministic top by default. Plateau exploration either preserves legacy random drift or, when explicitly
448
+ // requested, evaluates UCB1 over the same adaptive top-N window. Forced selection always wins below.
449
+ const requestedPolicy = input.exploration?.policy ?? 'engine-health';
450
+ const ucb1Active = input.exploration?.plateau?.active === true;
451
+ let drift = { driftMode: 'deterministic', index: 0, intensity: 0 };
452
+ let chosen;
453
+ let selectionPolicy;
454
+ if (forced.selectedGeneId === undefined && ucb1Active && requestedPolicy !== 'engine-health') {
455
+ // Canonicalize ties before slicing the UCB window. The legacy/default ranked list intentionally keeps its
456
+ // stable input-order tie behavior; UCB1 must produce the same arm for any permutation of the same pool.
457
+ const ucbRanked = [...policyEligible].sort((left, right) => {
458
+ const leftScore = Number.isFinite(left.score) ? left.score : Number.NEGATIVE_INFINITY;
459
+ const rightScore = Number.isFinite(right.score) ? right.score : Number.NEGATIVE_INFINITY;
460
+ return rightScore !== leftScore
461
+ ? rightScore - leftScore
462
+ : (left.assetId ?? left.geneId).localeCompare(right.assetId ?? right.geneId);
463
+ });
464
+ const window = ucbRanked.slice(0, explorationWindowSize(ucbRanked.length, input.exploration));
465
+ const logicalIdCounts = new Map();
466
+ for (const scored of window) {
467
+ logicalIdCounts.set(scored.geneId, (logicalIdCounts.get(scored.geneId) ?? 0) + 1);
468
+ }
469
+ const history = input.exploration?.ucb1History;
470
+ const result = history
471
+ ? chooseUcb1Arm(window.map((scored) => {
472
+ const candidate = candidateForScore(input, scored);
473
+ const armId = scored.assetId ?? scored.geneId;
474
+ return {
475
+ armId,
476
+ baseScore: scored.score,
477
+ explorationEligible: candidate?.explorationEligible === true,
478
+ stats: candidate
479
+ ? ucb1StatsForCandidate(history, candidate.geneId, candidate.assetId, (logicalIdCounts.get(candidate.geneId) ?? 0) === 1)
480
+ : { armId, pulls: 0, completedPulls: 0, rewardSum: 0, meanReward: 0 },
481
+ };
482
+ }), history.totalPulls)
483
+ : { choice: null, fallbackReason: 'missing_history' };
484
+ const ucbChosen = result.choice
485
+ ? window.find((candidate) => (candidate.assetId ?? candidate.geneId) === result.choice.armId)
486
+ : undefined;
487
+ if (requestedPolicy === 'ucb1' && ucbChosen) {
488
+ chosen = ucbChosen;
489
+ const choice = compactChoice(result.choice);
490
+ chosen.reasons.push('ucb1(index=' + (choice.index ?? 'cold') + ',mean=' + choice.meanReward.toFixed(3) + ',pulls=' + choice.pulls + ')');
491
+ selectionPolicy = {
492
+ requested: requestedPolicy,
493
+ effective: 'ucb1',
494
+ selectionPolicyVersion: UCB1_SELECTION_POLICY_VERSION,
495
+ rewardPolicyVersion: UCB1_REWARD_POLICY_VERSION,
496
+ arm: choice,
497
+ };
498
+ }
499
+ else {
500
+ drift = driftSelect(eligible.length, input.exploration, ctx?.rng ?? Math.random);
501
+ chosen = eligible[drift.index] ?? eligible[0];
502
+ selectionPolicy = {
503
+ requested: requestedPolicy,
504
+ effective: 'engine-health',
505
+ selectionPolicyVersion: UCB1_SELECTION_POLICY_VERSION,
506
+ rewardPolicyVersion: UCB1_REWARD_POLICY_VERSION,
507
+ ...(result.choice ? {
508
+ arm: compactChoice(result.choice),
509
+ shadowArmId: result.choice.armId.slice(0, 160),
510
+ shadowDisagrees: Boolean(chosen && (chosen.assetId ?? chosen.geneId) !== result.choice.armId),
511
+ } : {}),
512
+ ...(result.fallbackReason ? { fallbackReason: result.fallbackReason } : {}),
513
+ };
514
+ }
515
+ }
516
+ else {
517
+ drift = driftSelect(eligible.length, input.exploration, ctx?.rng ?? Math.random);
518
+ chosen = eligible[drift.index] ?? eligible[0];
519
+ }
520
+ const forcedSelected = forced.selectedGeneId !== undefined;
521
+ const ucb1ResolvedPlateau = guardAssessment.reason === 'plateau_flat_match'
522
+ && selectionPolicy?.effective === 'ucb1';
523
+ const guardApplied = guardMode === 'enforce'
524
+ && !forcedSelected
525
+ && guardAssessment.wouldAbstain
526
+ && !ucb1ResolvedPlateau;
527
+ if (guardApplied) {
528
+ chosen = undefined;
529
+ }
161
530
  let selectedGeneId = forced.selectedGeneId ?? (chosen ? chosen.geneId : null);
162
531
  let selectedAssetId = forced.selectedAssetId ?? chosen?.assetId;
532
+ let fallbackSelected = false;
163
533
  if (!selectedGeneId) {
164
534
  // Issue #97 (ported from v1 selector.js): no candidate matched any live signal (every gene scored <= 0). Rather
165
535
  // than fall straight through to a blind innovate, reuse a *distilled* (or evolved) gene if one is available.
@@ -176,11 +546,14 @@ export const engineHealthSelection = {
176
546
  // scored above zero, NOT merely when nothing cleared a positive `floor`. Otherwise a real but weak signal match
177
547
  // (0 < score <= floor) would be preempted by an unrelated zero-evidence distilled gene; V1 (which has no floor)
178
548
  // keeps such a match. When candidates scored > 0 but below floor, defer to V2's floor policy (innovate).
179
- const noSignalMatch = !forced.forceRejected && forced.scoredForChoice.every((s) => s.score <= 0);
549
+ const normalPoolHasMatch = forced.scoredForChoice.some((candidate) => (candidate.matchScore ?? 0) > SELECTION_GUARD_MATCH_EPS);
550
+ const noSignalMatch = !forced.forceRejected
551
+ && ((guardApplied && guardAssessment.reason === 'no_match' && !normalPoolHasMatch)
552
+ || forced.scoredForChoice.every((candidate) => candidate.score <= 0));
180
553
  const fallback = noSignalMatch
181
554
  ? (input.distilledFallback ?? [])
182
555
  .filter((candidate) => isReusableFallbackCandidate(candidate) && (candidate.epigeneticPenalty ?? 0) === 0)
183
- .map((candidate) => ({ candidate, scored: scoreCandidate(input.signals, candidate) }))
556
+ .map((candidate) => ({ candidate, scored: scoreForSelection(candidate) }))
184
557
  .sort((left, right) => right.scored.score - left.scored.score || left.candidate.geneId.localeCompare(right.candidate.geneId))[0]
185
558
  : undefined;
186
559
  if (fallback) {
@@ -189,15 +562,48 @@ export const engineHealthSelection = {
189
562
  scored.push(fbScored);
190
563
  selectedGeneId = fb.geneId;
191
564
  selectedAssetId = fb.assetId;
565
+ fallbackSelected = true;
192
566
  }
193
- else {
194
- forced.scoredForChoice.forEach((s) => s.reasons.push(`< floor ${floor} → innovate`));
567
+ else if (!guardApplied) {
568
+ forced.scoredForChoice.forEach((candidate) => candidate.reasons.push(`< floor ${floor} → innovate`));
195
569
  }
196
570
  }
197
- else if (forced.selectedGeneId === undefined && drift.driftMode !== 'deterministic') {
571
+ else if (forced.selectedGeneId === undefined && selectionPolicy?.effective !== 'ucb1' && drift.driftMode !== 'deterministic') {
198
572
  scored.find((s) => s.geneId === selectedGeneId)?.reasons.push(`drift(${drift.driftMode},intensity=${drift.intensity.toFixed(2)})`);
199
573
  }
200
- return decisionWithWarnings(input, { selectedGeneId, ...(selectedAssetId ? { selectedAssetId } : {}), candidates: scored, weightsVersion: SELECTION_WEIGHTS_VERSION, strategyName: 'engine-health' });
574
+ if (selectionPolicy?.requested === 'ucb1-shadow' && selectionPolicy.shadowArmId) {
575
+ selectionPolicy.shadowDisagrees = (selectedAssetId ?? selectedGeneId) !== selectionPolicy.shadowArmId;
576
+ }
577
+ const selectionGuard = guardMode === 'legacy'
578
+ ? undefined
579
+ : {
580
+ mode: guardMode,
581
+ version: SELECTION_GUARD_VERSION,
582
+ status: forcedSelected
583
+ ? 'forced'
584
+ : ucb1ResolvedPlateau
585
+ ? 'ucb1'
586
+ : fallbackSelected
587
+ ? 'fallback'
588
+ : guardApplied
589
+ ? 'innovate'
590
+ : guardAssessment.wouldAbstain
591
+ ? 'shadow'
592
+ : 'allowed',
593
+ ...(guardAssessment.wouldAbstain && guardAssessment.reason ? { reason: guardAssessment.reason } : {}),
594
+ ...(guardAssessment.maxMatch !== undefined ? { maxMatch: guardAssessment.maxMatch } : {}),
595
+ ...(guardAssessment.matchSpread !== undefined ? { matchSpread: guardAssessment.matchSpread } : {}),
596
+ };
597
+ return decisionWithWarnings(input, {
598
+ selectedGeneId,
599
+ ...(selectedAssetId ? { selectedAssetId } : {}),
600
+ candidates: scored,
601
+ weightsVersion,
602
+ ...semanticMetadata,
603
+ strategyName: 'engine-health',
604
+ ...(selectionPolicy ? { selectionPolicy } : {}),
605
+ ...(selectionGuard ? { selectionGuard } : {}),
606
+ });
201
607
  },
202
608
  };
203
609
  /** 实现2: 纯信号匹配采样(忽略 health, 对照基线 — 经验主义要可对比). */
@@ -229,14 +635,35 @@ export function agentLedSelection(pick) {
229
635
  run(input, ctx) {
230
636
  const baseDecision = engineHealthSelection.run(input, ctx);
231
637
  const base = baseDecision.candidates;
638
+ const decisionMetadata = {
639
+ weightsVersion: baseDecision.weightsVersion,
640
+ ...(baseDecision.semanticProfileVersion
641
+ ? { semanticProfileVersion: baseDecision.semanticProfileVersion }
642
+ : {}),
643
+ ...(baseDecision.semanticDocumentCount !== undefined
644
+ ? { semanticDocumentCount: baseDecision.semanticDocumentCount }
645
+ : {}),
646
+ };
232
647
  const forced = input.forcedGeneId ? base.find((s) => s.reasons.some((r) => r.includes('forced gene'))) : undefined;
233
648
  if (forced?.reasons.some((r) => r.includes('forced gene selected'))) {
234
- return decisionWithWarnings(input, { selectedGeneId: forced.geneId, ...(forced.assetId ? { selectedAssetId: forced.assetId } : {}), candidates: base, weightsVersion: SELECTION_WEIGHTS_VERSION, strategyName: 'agent-led' });
649
+ return decisionWithWarnings(input, {
650
+ selectedGeneId: forced.geneId,
651
+ ...(forced.assetId ? { selectedAssetId: forced.assetId } : {}),
652
+ candidates: base,
653
+ ...decisionMetadata,
654
+ strategyName: 'agent-led',
655
+ });
235
656
  }
236
657
  const candidatesForPick = forced?.reasons.some((r) => r.includes('forced gene rejected')) ? base.filter((s) => !matchesScoredIdentity(s, forced)) : base;
237
658
  const choice = pick(candidatesForPick, input);
238
659
  const selected = candidatesForPick.find((s) => s.geneId === choice) ?? base.find((s) => s.geneId === choice);
239
- return decisionWithWarnings(input, { selectedGeneId: choice, ...(selected?.assetId ? { selectedAssetId: selected.assetId } : {}), candidates: base, weightsVersion: SELECTION_WEIGHTS_VERSION, strategyName: 'agent-led' });
660
+ return decisionWithWarnings(input, {
661
+ selectedGeneId: choice,
662
+ ...(selected?.assetId ? { selectedAssetId: selected.assetId } : {}),
663
+ candidates: base,
664
+ ...decisionMetadata,
665
+ strategyName: 'agent-led',
666
+ });
240
667
  },
241
668
  };
242
669
  }
@@ -1,5 +1,6 @@
1
1
  export * from './geneHealth.js';
2
2
  export * from './exploration.js';
3
+ export * from './ucb1.js';
3
4
  export * from './epigenetics.js';
4
5
  export * from './confidence.js';
5
6
  export * from './memoryGraph.js';
@@ -18,4 +19,6 @@ export * from './solidify.js';
18
19
  export * from './evolutionEvent.js';
19
20
  export * from './cycleEngine.js';
20
21
  export * from './strategyPresets.js';
21
- export * from './cycleFailureClassifier.js';
22
+ export * from './cycleFailureClassifier.js';
23
+ export * from './kautoValidator.js';
24
+ export * from './kautoProjection.js';
@@ -1,5 +1,6 @@
1
1
  export * from './geneHealth.js';
2
2
  export * from './exploration.js';
3
+ export * from './ucb1.js';
3
4
  export * from './epigenetics.js';
4
5
  export * from './confidence.js';
5
6
  export * from './memoryGraph.js';
@@ -18,4 +19,6 @@ export * from './solidify.js';
18
19
  export * from './evolutionEvent.js';
19
20
  export * from './cycleEngine.js';
20
21
  export * from './strategyPresets.js';
21
- export * from './cycleFailureClassifier.js';
22
+ export * from './cycleFailureClassifier.js';
23
+ export * from './kautoValidator.js';
24
+ export * from './kautoProjection.js';