@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
@@ -21,4 +21,37 @@ export declare const DEFAULT_HEALTH_WEIGHTS: GeneHealthWeights;
21
21
  export declare function geneHealthScore(view: GeneLearningView, opts?: {
22
22
  reuseCount?: number;
23
23
  antiPatternCount?: number;
24
- }, w?: GeneHealthWeights): GeneHealth;
24
+ }, w?: GeneHealthWeights): GeneHealth;
25
+ /**
26
+ * Whether a gene has any DECISIVE evidence behind its health score.
27
+ *
28
+ * `aggregateLearningHistory` divides successes by `success + failed`, excluding inert runs from both
29
+ * sides, so a gene whose capsules are all inert — or which has never run — yields `successRate = 0` and
30
+ * therefore `score = 0`. That zero means "nothing is known", not "known to be bad", and the two must not
31
+ * render the same: showing 0% for an unproven gene is the same class of lie as showing a self-reported
32
+ * 98%. A surface reporting a score must check this first and say "not assessed" when it is false.
33
+ */
34
+ export declare function isGeneHealthAssessable(view: Pick<GeneLearningView, 'success' | 'failed'>): boolean;
35
+ /**
36
+ * The best score {@link geneHealthScore} can return under `w`: a gene with a perfect success rate, reuse
37
+ * saturated, and no anti-patterns. Derived from the weights rather than hard-coded, so retuning them
38
+ * cannot silently leave the display scale calibrated to the old ceiling.
39
+ */
40
+ export declare function geneHealthScoreCeiling(w?: GeneHealthWeights): number;
41
+ /**
42
+ * Map a raw health score onto [0, 1] for DISPLAY only.
43
+ *
44
+ * `geneHealthScore` is not a 0–1 quantity: under the default weights it tops out at 0.7 and floors at
45
+ * -0.4 (the anti-pattern penalty). Rendering the raw number as a percentage would report a flawless gene
46
+ * as 70%, so any surface showing a percentage has to rescale — and doing that at each surface is how two
47
+ * surfaces end up disagreeing.
48
+ *
49
+ * Negative scores clamp to 0 rather than mapping the full [-ceiling_penalty, ceiling] range onto [0, 1]:
50
+ * a linear map would place "no evidence at all" (raw 0) near the middle of the bar, which reads as a
51
+ * passing grade for a gene that has proven nothing. The penalty is not lost, it is just not separately
52
+ * legible below zero — callers that need it read `GeneHealth.score` / `antiPatternPenalty` directly.
53
+ *
54
+ * Never feed this back into selection: ranking consumes the raw score, and clamping there would make
55
+ * every penalized gene tie at 0.
56
+ */
57
+ export declare function normalizeGeneHealthScore(score: number, w?: GeneHealthWeights): number;
@@ -14,4 +14,46 @@ export function geneHealthScore(view, opts = {}, w = DEFAULT_HEALTH_WEIGHTS) {
14
14
  const confidenceAdjustedSuccess = view.successRate * (w.successRate + w.reuse * reuseScore(reuseCount));
15
15
  const score = confidenceAdjustedSuccess - w.antiPattern * antiPatternPenalty;
16
16
  return { geneId: view.geneId, successRate: view.successRate, reuseCount, antiPatternPenalty, score };
17
+ }
18
+ /**
19
+ * Whether a gene has any DECISIVE evidence behind its health score.
20
+ *
21
+ * `aggregateLearningHistory` divides successes by `success + failed`, excluding inert runs from both
22
+ * sides, so a gene whose capsules are all inert — or which has never run — yields `successRate = 0` and
23
+ * therefore `score = 0`. That zero means "nothing is known", not "known to be bad", and the two must not
24
+ * render the same: showing 0% for an unproven gene is the same class of lie as showing a self-reported
25
+ * 98%. A surface reporting a score must check this first and say "not assessed" when it is false.
26
+ */
27
+ export function isGeneHealthAssessable(view) {
28
+ return view.success + view.failed >= 1;
29
+ }
30
+ /**
31
+ * The best score {@link geneHealthScore} can return under `w`: a gene with a perfect success rate, reuse
32
+ * saturated, and no anti-patterns. Derived from the weights rather than hard-coded, so retuning them
33
+ * cannot silently leave the display scale calibrated to the old ceiling.
34
+ */
35
+ export function geneHealthScoreCeiling(w = DEFAULT_HEALTH_WEIGHTS) {
36
+ return w.successRate + w.reuse;
37
+ }
38
+ /**
39
+ * Map a raw health score onto [0, 1] for DISPLAY only.
40
+ *
41
+ * `geneHealthScore` is not a 0–1 quantity: under the default weights it tops out at 0.7 and floors at
42
+ * -0.4 (the anti-pattern penalty). Rendering the raw number as a percentage would report a flawless gene
43
+ * as 70%, so any surface showing a percentage has to rescale — and doing that at each surface is how two
44
+ * surfaces end up disagreeing.
45
+ *
46
+ * Negative scores clamp to 0 rather than mapping the full [-ceiling_penalty, ceiling] range onto [0, 1]:
47
+ * a linear map would place "no evidence at all" (raw 0) near the middle of the bar, which reads as a
48
+ * passing grade for a gene that has proven nothing. The penalty is not lost, it is just not separately
49
+ * legible below zero — callers that need it read `GeneHealth.score` / `antiPatternPenalty` directly.
50
+ *
51
+ * Never feed this back into selection: ranking consumes the raw score, and clamping there would make
52
+ * every penalized gene tie at 0.
53
+ */
54
+ export function normalizeGeneHealthScore(score, w = DEFAULT_HEALTH_WEIGHTS) {
55
+ const ceiling = geneHealthScoreCeiling(w);
56
+ if (!Number.isFinite(score) || ceiling <= 0)
57
+ return 0;
58
+ return Math.min(1, Math.max(0, score / ceiling));
17
59
  }
@@ -1,4 +1,4 @@
1
- import { type Gene, type GenerationSource } from '../wire/index.js';
1
+ import { type EnvClass, type Gene, type GeneClaim, type GeneRuntimeProfile, type GeneScope, type GeneVerifierProfile, type GenerationSource } from '../wire/index.js';
2
2
  /**
3
3
  * True when a gene id is in the `gene_distilled_` namespace. NB this prefix is now a NAMESPACE marker, NOT a
4
4
  * provenance tag: v1 used it to mean "skill-derived" (auto-evolved genes were `gene_auto_`), but v2's intakeGene
@@ -26,9 +26,27 @@ export interface GeneCandidate {
26
26
  forbidden_paths?: readonly string[];
27
27
  };
28
28
  validation?: readonly string[];
29
+ /**
30
+ * Runtime profile: which LLM produced this gene. This is the \(K_{auto}\) runtime coordinate, and it was the
31
+ * binding constraint on automatic-governance eligibility in production (32.9% populated vs 99%+ for the
32
+ * retrieval coordinates, bench/thesis/result-kauto-coverage.json) — because intake never populated it, so it
33
+ * only appeared when some other writer happened to set it. Intake now fills it from the SAME producer the
34
+ * environment fingerprint already uses (detectModelName), which resolves an explicit EVOLVER_MODEL_NAME or a
35
+ * host CLI's model env var. Callers may pass it explicitly to override detection.
36
+ *
37
+ * The literal 'unknown' is what detectModelName returns when nothing is discoverable, and it is deliberately
38
+ * NOT written: an undetectable runtime is not a machine-decidable coordinate, and recording 'unknown' would
39
+ * inflate K_auto coverage with rows that cannot actually be governed by runtime. Absent therefore keeps its
40
+ * existing meaning (not recorded), and a present value always names a real runtime.
41
+ */
42
+ model_name?: string;
29
43
  routing_hint?: unknown;
30
44
  tool_policy?: unknown;
31
45
  generation_meta?: unknown;
46
+ claims?: unknown;
47
+ scope?: unknown;
48
+ runtime_profile?: unknown;
49
+ verifier_profile?: unknown;
32
50
  }
33
51
  /** Minimal shape of an existing gene needed for dedup. */
34
52
  export interface ExistingGeneRef {
@@ -40,6 +58,33 @@ export interface GeneIntakeResult {
40
58
  gene?: Gene;
41
59
  errors: string[];
42
60
  }
61
+ /**
62
+ * Map free-text retrieval signals into the closed constraint language so scope containment is decidable.
63
+ * Already-namespaced terms and version intervals pass through; bare tokens become `capability:<token>`.
64
+ * Hard facets (`required:…`) keep the marker around the namespaced term.
65
+ */
66
+ export declare function namespaceScopeSignals(signals: readonly string[]): string[];
67
+ /** Detect a coarse env_class for runtime_profile from process env (CI → ci, else local). */
68
+ export declare function detectEnvClass(env?: NodeJS.ProcessEnv): EnvClass;
69
+ /**
70
+ * Honest defaults for K_auto coordinates the producer did not state. Never invents a coordinate from
71
+ * nothing: each default is grounded in fields the gene already carries (signals, model, validation).
72
+ */
73
+ export declare function deriveDefaultKautoCoordinates(input: {
74
+ signals: readonly string[];
75
+ modelName?: string;
76
+ validation: readonly string[];
77
+ claims?: GeneClaim[] | null;
78
+ scope?: GeneScope | null;
79
+ runtime_profile?: GeneRuntimeProfile | null;
80
+ verifier_profile?: GeneVerifierProfile | null;
81
+ env?: NodeJS.ProcessEnv;
82
+ }): {
83
+ claims: GeneClaim[] | null;
84
+ scope: GeneScope | null;
85
+ runtime_profile: GeneRuntimeProfile | null;
86
+ verifier_profile: GeneVerifierProfile | null;
87
+ };
43
88
  /**
44
89
  * Validate + normalize a distilled/proposed gene for pool insertion. On success returns the canonical Gene
45
90
  * (defaults filled, asset_id computed); otherwise the structural / dedup / schema errors.
@@ -5,12 +5,14 @@
5
5
  // signals fully overlap an existing one (dedup, so the pool does not grow redundant). The gene's CONTENT
6
6
  // is generated upstream (the agent runtime / a distillation prompt — generative, out of core scope); this
7
7
  // slice is the structural gate. Pairs with capabilityCandidates (which proposes what to distill).
8
- import { computeAssetId, validateWire, SCHEMA_VERSION, normalizeRoutingHint, normalizeToolPolicy, normalizeGenerationMeta, stripGeneHints, } from '../wire/index.js';
8
+ import { detectModelName } from '../bootstrap/envFingerprint.js';
9
+ import { computeAssetId, validateWire, SCHEMA_VERSION, normalizeRoutingHint, normalizeToolPolicy, normalizeGenerationMeta, normalizeClaims, normalizeGeneScope, normalizeRuntimeProfile, normalizeVerifierProfile, stripGeneHints, ENV_CLASSES, } from '../wire/index.js';
9
10
  const VALID_CATEGORIES = ['repair', 'optimize', 'innovate', 'explore'];
10
11
  const DEFAULT_FORBIDDEN_PATHS = ['.git', 'node_modules'];
11
12
  const DEFAULT_MAX_FILES = 12;
12
13
  /** Id prefix every distilled (skill-derived) gene carries. Recognised via {@link isDistilledGeneId}. */
13
14
  const DISTILLED_ID_PREFIX = 'gene_distilled_';
15
+ const LEGACY_CONVERSATION_ID_PREFIX = 'gene_conversation_';
14
16
  /**
15
17
  * True when a gene id is in the `gene_distilled_` namespace. NB this prefix is now a NAMESPACE marker, NOT a
16
18
  * provenance tag: v1 used it to mean "skill-derived" (auto-evolved genes were `gene_auto_`), but v2's intakeGene
@@ -51,7 +53,84 @@ function fnv1a(s) {
51
53
  }
52
54
  return (h >>> 0).toString(16).padStart(8, '0');
53
55
  }
56
+ const nonEmptyModel = (v) => {
57
+ const t = (v ?? '').trim();
58
+ return t.length > 0 ? t.slice(0, 100) : undefined;
59
+ };
54
60
  const clean = (xs) => (xs ?? []).map((s) => String(s).trim()).filter((s) => s.length > 0);
61
+ /** Already-namespaced closed-constraint scope terms (kautoValidator.SCOPE_TERM prefixes). */
62
+ const NAMESPACED_SCOPE = /^(?:repo|lineage|pkg|lang|framework|capability|tool|model|time|env):[A-Za-z0-9._@/:-]+$/;
63
+ const VERSION_INTERVAL_SCOPE = /^[A-Za-z0-9._-]+@(?:[><=^~]{1,2})?\d[\w.*-]*(?:\s*-\s*\d[\w.*-]*)?$/;
64
+ const PLACEHOLDER_VERIFIER = /^(?:node|python3?|ruby|deno|bun|go|java|npm|pnpm|yarn)\s+(?:--?v(?:ersion)?|-V)\s*$/i;
65
+ const TRIVIAL_VERIFIER = /^(?:true|:|echo\b.*|exit\s+0)\s*$/i;
66
+ /**
67
+ * Map free-text retrieval signals into the closed constraint language so scope containment is decidable.
68
+ * Already-namespaced terms and version intervals pass through; bare tokens become `capability:<token>`.
69
+ * Hard facets (`required:…`) keep the marker around the namespaced term.
70
+ */
71
+ export function namespaceScopeSignals(signals) {
72
+ const out = [];
73
+ for (const raw of signals) {
74
+ const t = String(raw ?? '').trim();
75
+ if (!t)
76
+ continue;
77
+ const required = t.startsWith('required:');
78
+ const bare = required ? t.slice('required:'.length).trim() : t;
79
+ if (!bare)
80
+ continue;
81
+ const namespaced = (NAMESPACED_SCOPE.test(bare) || VERSION_INTERVAL_SCOPE.test(bare))
82
+ ? bare
83
+ : `capability:${bare.replace(/[^A-Za-z0-9._@/-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 80)}`;
84
+ if (!namespaced || namespaced === 'capability:')
85
+ continue;
86
+ out.push(required ? `required:${namespaced}` : namespaced);
87
+ }
88
+ return out;
89
+ }
90
+ /** Detect a coarse env_class for runtime_profile from process env (CI → ci, else local). */
91
+ export function detectEnvClass(env = process.env) {
92
+ const truthy = (v) => {
93
+ const t = (v ?? '').trim().toLowerCase();
94
+ return t !== '' && t !== '0' && t !== 'false' && t !== 'no';
95
+ };
96
+ if (truthy(env['CI']) || truthy(env['GITHUB_ACTIONS']) || truthy(env['GITLAB_CI'])
97
+ || truthy(env['BUILDKITE']) || truthy(env['CIRCLECI']) || truthy(env['TRAVIS'])) {
98
+ return 'ci';
99
+ }
100
+ if (truthy(env['EVOLVER_ENV_CLASS'])) {
101
+ const v = String(env['EVOLVER_ENV_CLASS']).trim().toLowerCase();
102
+ if (ENV_CLASSES.includes(v))
103
+ return v;
104
+ }
105
+ return 'local';
106
+ }
107
+ /**
108
+ * Honest defaults for K_auto coordinates the producer did not state. Never invents a coordinate from
109
+ * nothing: each default is grounded in fields the gene already carries (signals, model, validation).
110
+ */
111
+ export function deriveDefaultKautoCoordinates(input) {
112
+ const claims = input.claims ?? (input.signals.length > 0
113
+ ? [{ predicate: 'output_contract', kind: 'behavioral' }]
114
+ : null);
115
+ const scope = input.scope ?? (() => {
116
+ const namespaced = namespaceScopeSignals(input.signals);
117
+ return namespaced.length > 0 ? { signals: namespaced } : null;
118
+ })();
119
+ const runtime_profile = input.runtime_profile ?? (input.modelName
120
+ ? { runtime: input.modelName, env_class: detectEnvClass(input.env) }
121
+ : null);
122
+ const verifier_profile = input.verifier_profile ?? (() => {
123
+ const cmds = input.validation.map((c) => String(c).trim()).filter(Boolean);
124
+ if (cmds.length === 0)
125
+ return null;
126
+ const substantive = cmds.find((c) => !PLACEHOLDER_VERIFIER.test(c) && !TRIVIAL_VERIFIER.test(c));
127
+ // Light distill validation deliberately keeps `node --version` for sandbox safety; the gene still
128
+ // passed through evolver's sandboxed validation gate, which is a real verifier identity (not a
129
+ // shell placeholder). Prefer a substantive command when present; else name the gate.
130
+ return { verifier: substantive ?? 'evolver-sandboxed-validation', decision: 'pass' };
131
+ })();
132
+ return { claims, scope, runtime_profile, verifier_profile };
133
+ }
55
134
  /** Returns the id of an existing gene whose signals are a superset of `signals` (i.e. the candidate is redundant), else null. */
56
135
  function fullyOverlaps(signals, existing) {
57
136
  const newSet = signals.map((s) => s.toLowerCase());
@@ -84,14 +163,42 @@ export function intakeGene(candidate, existing = []) {
84
163
  if (errors.length > 0)
85
164
  return { ok: false, errors };
86
165
  const category = VALID_CATEGORIES.includes(candidate.category) ? candidate.category : 'optimize';
87
- const id = candidate.id && candidate.id.startsWith(DISTILLED_ID_PREFIX) ? candidate.id : `${DISTILLED_ID_PREFIX}${fnv1a(signals.join('|'))}`;
88
- // v2-delta EvoX hints (v1 PR #93): normalized so a candidate carrying them lands canonical, dropped to
166
+ // First-class EvoX hints (v1 PR #93): normalized so a candidate carrying them lands canonical, not dropped, and
89
167
  // absent when malformed/empty. The router / tool-gate reads absent === "no opinion".
90
168
  const routingHint = normalizeRoutingHint(candidate.routing_hint);
91
169
  const toolPolicy = normalizeToolPolicy(candidate.tool_policy);
92
- // v2-delta provenance + quality metadata (v1 #302): normalized so a candidate carrying it lands canonical,
170
+ // Local provenance + quality metadata (v1 #302): normalized so a candidate carrying it lands canonical,
93
171
  // dropped to absent when malformed/no-recognized-source.
94
172
  const generationMeta = normalizeGenerationMeta(candidate.generation_meta);
173
+ // 只有调用方明确证明记录来自蒸馏时,才保留 intake 之前的会话命名空间。
174
+ // 这样既兼容重试和下游逻辑 ID 查询,也不允许任意调用方 ID 绕过 intake 的命名空间所有权规则。
175
+ const preservesLegacyConversationId = generationMeta?.source === 'distilled'
176
+ && typeof candidate.id === 'string'
177
+ && candidate.id.startsWith(LEGACY_CONVERSATION_ID_PREFIX);
178
+ const id = candidate.id && (candidate.id.startsWith(DISTILLED_ID_PREFIX) || preservesLegacyConversationId)
179
+ ? candidate.id : `${DISTILLED_ID_PREFIX}${fnv1a(signals.join('|'))}`;
180
+ // K_auto runtime coordinate. Explicit caller value wins; otherwise detect from the environment using the same
181
+ // producer the env fingerprint uses. 'unknown' means undetectable, so it is dropped rather than recorded (see
182
+ // GeneCandidate.model_name) — absent keeps meaning "not recorded", never "runtime is literally unknown".
183
+ const detectedModel = nonEmptyModel(candidate.model_name) ?? nonEmptyModel(detectModelName());
184
+ const modelName = detectedModel === 'unknown' ? undefined : detectedModel;
185
+ // First-class K_auto projection-key coordinates. Producer-supplied values win when they normalize; otherwise
186
+ // derive honest defaults grounded in signals/model/validation so forward intake can clear strict K_auto
187
+ // without fabricating coordinates from nothing. Still ride along in asset_id and strip before validateWire.
188
+ const validation = candidate.validation ? [...candidate.validation] : [];
189
+ const derived = deriveDefaultKautoCoordinates({
190
+ signals,
191
+ ...(modelName ? { modelName } : {}),
192
+ validation,
193
+ claims: normalizeClaims(candidate.claims),
194
+ scope: normalizeGeneScope(candidate.scope),
195
+ runtime_profile: normalizeRuntimeProfile(candidate.runtime_profile),
196
+ verifier_profile: normalizeVerifierProfile(candidate.verifier_profile),
197
+ });
198
+ const claims = derived.claims;
199
+ const scope = derived.scope;
200
+ const runtimeProfile = derived.runtime_profile;
201
+ const verifierProfile = derived.verifier_profile;
95
202
  const gene = {
96
203
  type: 'Gene',
97
204
  schema_version: SCHEMA_VERSION,
@@ -103,12 +210,17 @@ export function intakeGene(candidate, existing = []) {
103
210
  max_files: candidate.constraints?.max_files ?? DEFAULT_MAX_FILES,
104
211
  forbidden_paths: [...(candidate.constraints?.forbidden_paths ?? DEFAULT_FORBIDDEN_PATHS)],
105
212
  },
106
- validation: candidate.validation ? [...candidate.validation] : [],
213
+ validation,
107
214
  summary: candidate.summary ?? `Strategy for: ${signals.slice(0, 3).join(', ')}`,
215
+ ...(modelName ? { model_name: modelName } : {}),
108
216
  ...(candidate.preconditions ? { preconditions: [...candidate.preconditions] } : {}),
109
217
  ...(routingHint ? { routing_hint: routingHint } : {}),
110
218
  ...(toolPolicy ? { tool_policy: toolPolicy } : {}),
111
219
  ...(generationMeta ? { generation_meta: generationMeta } : {}),
220
+ ...(claims ? { claims } : {}),
221
+ ...(scope ? { scope } : {}),
222
+ ...(runtimeProfile ? { runtime_profile: runtimeProfile } : {}),
223
+ ...(verifierProfile ? { verifier_profile: verifierProfile } : {}),
112
224
  asset_id: '',
113
225
  };
114
226
  // asset_id folds in the hints + generation_meta (intake's own canonical shape — gep-sdk canonicalize hashes every
@@ -116,11 +228,10 @@ export function intakeGene(candidate, existing = []) {
116
228
  // routing_hint/tool_policy:null plus empty epigenetic_marks/learning_history/anti_patterns/preconditions that
117
229
  // v2 intake omits, so the canonical shapes differ. Self-consistent here; not a cross-impl parity guarantee.
118
230
  gene.asset_id = computeAssetId(gene) ?? '';
119
- // Structural gate validates the gep-sdk-known CORE. routing_hint / tool_policy / generation_meta are v2-delta
120
- // (pending a gene-schema bump), so strip them before the check — else additionalProperties:false rejects the
121
- // gene at intake. The full hinted gene stays schema-invalid until the SDK catches up — surfaced by the advisory
122
- // validateWire preview (evolver_gep_build) and enforced by the hub on receipt, NOT by a local egress gate
123
- // (the publish/egress path is sanitize-only). Same v2-delta contract Capsule's proof_of_work had pre-1.11.0.
231
+ // Structural gate validates the gep-sdk-known Gene shape. gep-sdk 1.13.0 made routing_hint/tool_policy plus the
232
+ // K_auto coordinates first-class, so stripGeneHints removes only local annotations (generation_meta/model_name)
233
+ // before the check. That keeps SDK constraints active for every first-class field while preserving local-only
234
+ // metadata in asset_id and in the returned gene.
124
235
  const v = validateWire(stripGeneHints(gene));
125
236
  if (!v.ok)
126
237
  return { ok: false, errors: v.errors };
@@ -6,6 +6,7 @@ import type { AssetRecord } from '../assetstore/provider.js';
6
6
  import { type ExplorationInput } from './exploration.js';
7
7
  import type { GenerationSource } from '../wire/index.js';
8
8
  import type { MemoryGraphGeneEvidence } from './memoryGraph.js';
9
+ import { UCB1_REWARD_POLICY_VERSION, UCB1_SELECTION_POLICY_VERSION, type SelectionPolicy, type Ucb1Choice, type Ucb1FallbackReason } from './ucb1.js';
9
10
  /** 一个候选 gene 的选择期素材. */
10
11
  export interface GeneCandidateInput {
11
12
  geneId: string;
@@ -47,17 +48,35 @@ export interface GeneCandidateInput {
47
48
  * effect, so the factor is dormant until a caller injects the signal (default-off).
48
49
  */
49
50
  reuseAdjust?: number;
51
+ /**
52
+ * Soft preference for genes whose five K_auto coordinates are machine-decidable (decideKauto.inKauto).
53
+ * Assembly stamps this when the source record clears the strict predicate. SOFT only: it re-orders near-ties
54
+ * among already-admitted candidates and can NEVER resurrect a gene excluded by trust/review/ban. Absent/false
55
+ * → 0 contribution, so the historical catalogue (strict membership 0%) is not zero-scored.
56
+ */
57
+ kautoMember?: boolean;
50
58
  /**
51
59
  * Authoritative provenance tag from Gene.generation_meta.source. The legacy `gene_distilled_` prefix remains a
52
60
  * fallback only for old candidates that do not carry this field.
53
61
  */
54
62
  generationSource?: GenerationSource;
63
+ /**
64
+ * Assembly-owned UCB1 eligibility. Only trusted approved/legacy-local candidates may set true. Injected callers
65
+ * must not self-assert this bit; candidateAssembly overwrites Hub candidates to false.
66
+ */
67
+ explorationEligible?: boolean;
55
68
  }
56
69
  export interface SelectionInput {
57
70
  signals: readonly string[];
58
71
  candidates: readonly GeneCandidateInput[];
72
+ /** Trust-filtered library corpus captured before relevance admission. Omit for direct strategy callers. */
73
+ semanticCorpus?: readonly GeneCandidateInput[];
74
+ /** Emergency rollback: restore the pre-IDF semantic scorer and skip profile construction. */
75
+ disableSemanticIdf?: boolean;
59
76
  /** 低于此分则不选(→ 走 innovate 新基因), 默认 0. */
60
77
  floor?: number;
78
+ /** Relevance guard rollout. Omit for legacy selector behavior in direct/core callers. */
79
+ selectionGuard?: SelectionGuardMode;
61
80
  /**
62
81
  * Explicit gene requested by GEP / an external runtime. This is a hard selection only within the already
63
82
  * assembled candidate/fallback pools: it cannot resurrect a gene filtered by trust/review/ban upstream, and it is
@@ -70,8 +89,9 @@ export interface SelectionInput {
70
89
  /**
71
90
  * Distilled-gene fallback pool (ported from v1 #97): broadly-applicable distilled genes that do NOT match the
72
91
  * live signals, supplied by the assembly layer (already trust/review/ban-filtered) so they never compete in the
73
- * normal scored set. Used ONLY as a last resort when no candidate clears the floor: instead of falling straight
74
- * through to a blind innovate, selection reuses a known distilled strategy. Epigenetically-suppressed entries
92
+ * normal scored set. Used ONLY after normal selection has no reusable positive choice (the legacy non-positive
93
+ * pool or an enforced no-match guard): instead of falling through to a blind innovate, selection reuses a known
94
+ * distilled strategy. Epigenetically-suppressed entries
75
95
  * (epigeneticPenalty > 0) are skipped — v2's event-log-derived epigeneticPenalty is the analog of v1's asset-mark
76
96
  * hard suppression (a related band, not the identical predicate).
77
97
  */
@@ -88,6 +108,8 @@ export interface ScoredCandidate {
88
108
  geneId: string;
89
109
  assetId?: string;
90
110
  score: number;
111
+ /** Internal expanded match in [0,1]; omitted from root-event candidate payloads. */
112
+ matchScore?: number;
91
113
  reasons: string[];
92
114
  health?: GeneHealth;
93
115
  }
@@ -114,6 +136,36 @@ export interface GeneDecision {
114
136
  selectedReason?: string;
115
137
  /** Bounded structured outcome evidence for prompt enrichment. */
116
138
  memoryEvidence?: MemoryGraphGeneEvidence[];
139
+ /** Deterministic identity of the bounded IDF profile used for this decision. */
140
+ semanticProfileVersion?: string;
141
+ /** Number of trusted semantic documents represented by the profile. */
142
+ semanticDocumentCount?: number;
143
+ /** Compact policy trace; omitted for the default engine-health behavior. */
144
+ selectionPolicy?: SelectionPolicyTrace;
145
+ /** Versioned relevance guard trace; omitted only for explicit legacy rollback. */
146
+ selectionGuard?: SelectionGuardTrace;
147
+ }
148
+ export interface SelectionPolicyTrace {
149
+ requested: Exclude<SelectionPolicy, 'engine-health'>;
150
+ effective: 'engine-health' | 'ucb1';
151
+ selectionPolicyVersion: typeof UCB1_SELECTION_POLICY_VERSION;
152
+ rewardPolicyVersion: typeof UCB1_REWARD_POLICY_VERSION;
153
+ arm?: Ucb1Choice;
154
+ shadowArmId?: string;
155
+ shadowDisagrees?: boolean;
156
+ fallbackReason?: Ucb1FallbackReason;
157
+ }
158
+ export type SelectionGuardMode = 'legacy' | 'shadow' | 'enforce';
159
+ export declare const SELECTION_GUARD_VERSION = "relevance-guard-v1";
160
+ export type SelectionGuardReason = 'no_match' | 'plateau_flat_match';
161
+ export type SelectionGuardStatus = 'allowed' | 'shadow' | 'forced' | 'ucb1' | 'fallback' | 'innovate';
162
+ export interface SelectionGuardTrace {
163
+ mode: Exclude<SelectionGuardMode, 'legacy'>;
164
+ version: typeof SELECTION_GUARD_VERSION;
165
+ status: SelectionGuardStatus;
166
+ reason?: SelectionGuardReason;
167
+ maxMatch?: number;
168
+ matchSpread?: number;
117
169
  }
118
170
  /**
119
171
  * Weight of the preferred-gene confidence factor (fourth factor, positive cross-cycle learning). Kept small so
@@ -129,12 +181,68 @@ export declare const CONFIDENCE_WEIGHT = 0.15;
129
181
  export declare const REUSE_WEIGHT = 0.1;
130
182
  /** Weight of scoped local MemoryGraph outcome evidence. */
131
183
  export declare const MEMORY_GRAPH_WEIGHT = 0.12;
184
+ /** Bounded weight for a canonical task-domain signal match (#628). */
185
+ export declare const TASK_DOMAIN_WEIGHT = 0.08;
186
+ /** signals_match is weak domain evidence; its maximum score contribution is 0.08 * 0.5 = 0.04. */
187
+ export declare const TASK_DOMAIN_SIGNAL_EVIDENCE = 0.5;
188
+ /**
189
+ * Soft boost for strict K_auto members. Smaller than CONFIDENCE_WEIGHT: membership is a writer-side property,
190
+ * not verified cycle history, so it only breaks near-ties between already-eligible candidates.
191
+ */
192
+ export declare const KAUTO_WEIGHT = 0.05;
132
193
  /**
133
194
  * Version of the full engine-health weight vector (health 0.6 + signal-match 0.4 − epigenetic penalty
134
- * + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment). Bumped whenever a factor is added so golden
135
- * weight snapshots track the change. Composed from the health-weights version so a change to either layer shows.
195
+ * + CONFIDENCE_WEIGHT × confidence + REUSE_WEIGHT × reuse-sentiment + KAUTO_WEIGHT × kauto-member).
196
+ * Bumped whenever a factor is added so golden weight snapshots track the change. Composed from the
197
+ * health-weights version so a change to either layer shows.
136
198
  */
137
- export declare const SELECTION_WEIGHTS_VERSION = "sel-4(gh-2,conf=0.15,memory=0.12,reuse=0.1)";
199
+ export declare const LEGACY_SELECTION_WEIGHTS_VERSION = "sel-6-domain(gh-2,conf=0.15,memory=0.12,reuse=0.1,domain=0.08)";
200
+ export declare const SELECTION_WEIGHTS_VERSION = "sel-7-idf-domain(gh-2,conf=0.15,memory=0.12,reuse=0.1,domain=0.08)";
201
+ export interface KautoAblationPoolResult {
202
+ lambda: number;
203
+ ranking: Array<{
204
+ rank: number;
205
+ geneId: string;
206
+ assetId?: string;
207
+ score: number;
208
+ scoreBase: number;
209
+ kautoContribution: number;
210
+ kautoMember: boolean;
211
+ }>;
212
+ selectedGeneId: string | null;
213
+ selectedAssetId?: string;
214
+ scoreBaseMin: number;
215
+ scoreBaseMax: number;
216
+ scoreBaseMean: number;
217
+ }
218
+ export interface KautoAblationCompare {
219
+ lambdas: readonly number[];
220
+ pools: KautoAblationPoolResult[];
221
+ /** Pairs (baseline λ=0 vs each λ>0) where any gene's rank changed. */
222
+ rankChangesByLambda: Record<string, number>;
223
+ /** Pairs where the selected (top) gene changed vs λ=0. */
224
+ selectedChangesByLambda: Record<string, boolean>;
225
+ /** Pairs where the top-k set changed vs λ=0. */
226
+ topKChangesByLambda: Record<string, number>;
227
+ topK: number;
228
+ }
229
+ /**
230
+ * Offline λ ablation over an already-admitted candidate pool.
231
+ * Does not run hard gates / force / distilled fallback — those stay outside the soft preference.
232
+ * Pure ranking sensitivity for score_T2 = score_base + λ · 1[k_a ∈ K_auto].
233
+ */
234
+ export declare function ablateKautoLambda(input: Pick<SelectionInput, 'signals' | 'candidates' | 'semanticCorpus' | 'disableSemanticIdf'>, lambdas?: readonly number[], opts?: {
235
+ topK?: number;
236
+ floor?: number;
237
+ }): KautoAblationCompare;
238
+ interface SelectionGuardAssessment {
239
+ wouldAbstain: boolean;
240
+ reason?: SelectionGuardReason;
241
+ maxMatch?: number;
242
+ matchSpread?: number;
243
+ }
244
+ /** Refs #626: identify selections whose relevance is absent or cannot discriminate during a plateau. */
245
+ export declare function assessSelectionGuard(scored: readonly ScoredCandidate[], plateauActive: boolean): SelectionGuardAssessment;
138
246
  /** 实现1: engine 健康分主导(health 0.6 + 信号匹配 0.4). */
139
247
  export declare const engineHealthSelection: Strategy<SelectionInput, GeneDecision>;
140
248
  /** 实现2: 纯信号匹配采样(忽略 health, 对照基线 — 经验主义要可对比). */
@@ -142,4 +250,5 @@ export declare const signalMatchSelection: Strategy<SelectionInput, GeneDecision
142
250
  /** 实现3: agent 主导(注入决策回调; engine 只给候选+分, agent 拍板, D26 agent 一等公民). */
143
251
  export declare function agentLedSelection(pick: (scored: ScoredCandidate[], input: SelectionInput) => string | null): Strategy<SelectionInput, GeneDecision>;
144
252
  /** 选 gene StrategyPoint: 默认 engine-health, 备选 signal-match(+ 可注册 agent-led). */
145
- export declare function makeGeneSelectionPoint(): StrategyPoint<SelectionInput, GeneDecision>;
253
+ export declare function makeGeneSelectionPoint(): StrategyPoint<SelectionInput, GeneDecision>;
254
+ export {};