@cogitator-ai/self-modifying 17.0.17 → 18.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/README.md +112 -48
  2. package/dist/architecture-evolution/capability-analyzer.d.ts.map +1 -1
  3. package/dist/architecture-evolution/capability-analyzer.js +44 -40
  4. package/dist/architecture-evolution/capability-analyzer.js.map +1 -1
  5. package/dist/architecture-evolution/evolution-strategy.d.ts.map +1 -1
  6. package/dist/architecture-evolution/evolution-strategy.js +4 -2
  7. package/dist/architecture-evolution/evolution-strategy.js.map +1 -1
  8. package/dist/architecture-evolution/index.d.ts +2 -2
  9. package/dist/architecture-evolution/index.d.ts.map +1 -1
  10. package/dist/architecture-evolution/index.js +1 -1
  11. package/dist/architecture-evolution/index.js.map +1 -1
  12. package/dist/architecture-evolution/parameter-optimizer.d.ts +5 -4
  13. package/dist/architecture-evolution/parameter-optimizer.d.ts.map +1 -1
  14. package/dist/architecture-evolution/parameter-optimizer.js +62 -34
  15. package/dist/architecture-evolution/parameter-optimizer.js.map +1 -1
  16. package/dist/architecture-evolution/prompts.d.ts +21 -2
  17. package/dist/architecture-evolution/prompts.d.ts.map +1 -1
  18. package/dist/architecture-evolution/prompts.js +189 -37
  19. package/dist/architecture-evolution/prompts.js.map +1 -1
  20. package/dist/constraints/modification-validator.d.ts +1 -0
  21. package/dist/constraints/modification-validator.d.ts.map +1 -1
  22. package/dist/constraints/modification-validator.js +45 -18
  23. package/dist/constraints/modification-validator.js.map +1 -1
  24. package/dist/constraints/rollback-manager.d.ts +1 -0
  25. package/dist/constraints/rollback-manager.d.ts.map +1 -1
  26. package/dist/constraints/rollback-manager.js +25 -1
  27. package/dist/constraints/rollback-manager.js.map +1 -1
  28. package/dist/events/event-emitter.d.ts +0 -1
  29. package/dist/events/event-emitter.d.ts.map +1 -1
  30. package/dist/events/event-emitter.js +6 -10
  31. package/dist/events/event-emitter.js.map +1 -1
  32. package/dist/index.d.ts +4 -4
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +3 -3
  35. package/dist/index.js.map +1 -1
  36. package/dist/meta-reasoning/meta-reasoner.d.ts +4 -1
  37. package/dist/meta-reasoning/meta-reasoner.d.ts.map +1 -1
  38. package/dist/meta-reasoning/meta-reasoner.js +120 -43
  39. package/dist/meta-reasoning/meta-reasoner.js.map +1 -1
  40. package/dist/meta-reasoning/observation-collector.d.ts.map +1 -1
  41. package/dist/meta-reasoning/observation-collector.js +6 -2
  42. package/dist/meta-reasoning/observation-collector.js.map +1 -1
  43. package/dist/meta-reasoning/prompts.d.ts +10 -14
  44. package/dist/meta-reasoning/prompts.d.ts.map +1 -1
  45. package/dist/meta-reasoning/prompts.js +108 -33
  46. package/dist/meta-reasoning/prompts.js.map +1 -1
  47. package/dist/meta-reasoning/strategy-selector.d.ts.map +1 -1
  48. package/dist/meta-reasoning/strategy-selector.js +6 -0
  49. package/dist/meta-reasoning/strategy-selector.js.map +1 -1
  50. package/dist/self-modifying-agent.d.ts +88 -15
  51. package/dist/self-modifying-agent.d.ts.map +1 -1
  52. package/dist/self-modifying-agent.js +583 -376
  53. package/dist/self-modifying-agent.js.map +1 -1
  54. package/dist/tool-generation/gap-analyzer.d.ts +0 -1
  55. package/dist/tool-generation/gap-analyzer.d.ts.map +1 -1
  56. package/dist/tool-generation/gap-analyzer.js +16 -15
  57. package/dist/tool-generation/gap-analyzer.js.map +1 -1
  58. package/dist/tool-generation/generated-tool-store.d.ts.map +1 -1
  59. package/dist/tool-generation/generated-tool-store.js +90 -10
  60. package/dist/tool-generation/generated-tool-store.js.map +1 -1
  61. package/dist/tool-generation/index.d.ts +3 -3
  62. package/dist/tool-generation/index.d.ts.map +1 -1
  63. package/dist/tool-generation/index.js +2 -2
  64. package/dist/tool-generation/index.js.map +1 -1
  65. package/dist/tool-generation/prompts.d.ts +4 -3
  66. package/dist/tool-generation/prompts.d.ts.map +1 -1
  67. package/dist/tool-generation/prompts.js +96 -18
  68. package/dist/tool-generation/prompts.js.map +1 -1
  69. package/dist/tool-generation/tool-generator.d.ts +6 -6
  70. package/dist/tool-generation/tool-generator.d.ts.map +1 -1
  71. package/dist/tool-generation/tool-generator.js +88 -23
  72. package/dist/tool-generation/tool-generator.js.map +1 -1
  73. package/dist/tool-generation/tool-sandbox.d.ts +10 -11
  74. package/dist/tool-generation/tool-sandbox.d.ts.map +1 -1
  75. package/dist/tool-generation/tool-sandbox.js +346 -156
  76. package/dist/tool-generation/tool-sandbox.js.map +1 -1
  77. package/dist/tool-generation/tool-validator.d.ts +4 -8
  78. package/dist/tool-generation/tool-validator.d.ts.map +1 -1
  79. package/dist/tool-generation/tool-validator.js +74 -28
  80. package/dist/tool-generation/tool-validator.js.map +1 -1
  81. package/dist/utils/index.d.ts.map +1 -1
  82. package/dist/utils/index.js +30 -5
  83. package/dist/utils/index.js.map +1 -1
  84. package/dist/utils/llm-helper.d.ts.map +1 -1
  85. package/dist/utils/llm-helper.js +1 -0
  86. package/dist/utils/llm-helper.js.map +1 -1
  87. package/package.json +3 -12
  88. package/src/__tests__/component-regressions.test.ts +599 -0
  89. package/src/__tests__/meta-reasoning.test.ts +5 -1
  90. package/src/__tests__/sandbox-security.test.ts +186 -0
  91. package/src/__tests__/self-modifying-agent.test.ts +405 -11
  92. package/src/architecture-evolution/capability-analyzer.ts +63 -44
  93. package/src/architecture-evolution/evolution-strategy.ts +5 -2
  94. package/src/architecture-evolution/index.ts +2 -0
  95. package/src/architecture-evolution/parameter-optimizer.ts +82 -40
  96. package/src/architecture-evolution/prompts.ts +224 -42
  97. package/src/constraints/modification-validator.ts +48 -21
  98. package/src/constraints/rollback-manager.ts +20 -1
  99. package/src/events/event-emitter.ts +6 -15
  100. package/src/index.ts +14 -0
  101. package/src/meta-reasoning/meta-reasoner.ts +124 -52
  102. package/src/meta-reasoning/observation-collector.ts +6 -2
  103. package/src/meta-reasoning/prompts.ts +131 -56
  104. package/src/meta-reasoning/strategy-selector.ts +6 -0
  105. package/src/self-modifying-agent.ts +797 -413
  106. package/src/tool-generation/gap-analyzer.ts +21 -19
  107. package/src/tool-generation/generated-tool-store.ts +97 -10
  108. package/src/tool-generation/index.ts +13 -3
  109. package/src/tool-generation/prompts.ts +120 -18
  110. package/src/tool-generation/tool-generator.ts +120 -26
  111. package/src/tool-generation/tool-sandbox.ts +408 -206
  112. package/src/tool-generation/tool-validator.ts +90 -37
  113. package/src/utils/index.ts +29 -5
  114. package/src/utils/llm-helper.ts +1 -0
@@ -1,5 +1,21 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import type { Tool, LLMBackend, TaskProfile } from '@cogitator-ai/types';
2
3
  import { buildTaskProfilePrompt, parseTaskProfileResponse } from './prompts';
4
+ import { llmChat } from '../utils/llm-helper';
5
+
6
+ function escapeRegExp(value: string): string {
7
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
8
+ }
9
+
10
+ function containsPhrase(text: string, phrase: string): boolean {
11
+ return new RegExp(`(?:^|[^a-z0-9])${escapeRegExp(phrase)}(?:s|es|ed|ing)?(?:$|[^a-z0-9])`).test(
12
+ text
13
+ );
14
+ }
15
+
16
+ function tokenize(text: string): string[] {
17
+ return text.split(/[^a-z0-9-]+/).filter(Boolean);
18
+ }
3
19
 
4
20
  export interface CapabilityAnalyzerOptions {
5
21
  llm?: LLMBackend;
@@ -121,6 +137,7 @@ const COMPLEXITY_INDICATORS = {
121
137
  simple: ['explain', 'define', 'list', 'describe', 'show'],
122
138
  moderate: ['compare', 'analyze', 'summarize', 'evaluate', 'implement'],
123
139
  complex: ['design', 'architect', 'optimize', 'comprehensive', 'detailed'],
140
+ expert: ['expert', 'advanced', 'specialized', 'intricate', 'sophisticated'],
124
141
  extreme: ['novel', 'research', 'breakthrough', 'state-of-the-art', 'cutting-edge'],
125
142
  };
126
143
 
@@ -145,7 +162,7 @@ export class CapabilityAnalyzer {
145
162
  constraints?: { maxCost?: number; maxLatency?: number };
146
163
  }
147
164
  ): Promise<TaskProfile> {
148
- const cacheKey = this.buildCacheKey(taskDescription);
165
+ const cacheKey = this.buildCacheKey(taskDescription, context);
149
166
  const cached = this.profileCache.get(cacheKey);
150
167
  if (cached && Date.now() - cached.timestamp < this.cacheTTL) {
151
168
  return cached.profile;
@@ -189,32 +206,20 @@ export class CapabilityAnalyzer {
189
206
  constraints: context?.constraints,
190
207
  });
191
208
 
192
- const response = llm.complete
193
- ? await llm.complete({
194
- messages: [
195
- {
196
- role: 'system',
197
- content:
198
- 'You are a task analysis expert. Analyze tasks and determine their characteristics.',
199
- },
200
- { role: 'user', content: prompt },
201
- ],
202
- temperature: 0.2,
203
- })
204
- : await llm.chat({
205
- model: this.model,
206
- messages: [
207
- {
208
- role: 'system',
209
- content:
210
- 'You are a task analysis expert. Analyze tasks and determine their characteristics.',
211
- },
212
- { role: 'user', content: prompt },
213
- ],
214
- temperature: 0.2,
215
- });
216
-
217
- const parsed = parseTaskProfileResponse(response.content);
209
+ const content = await llmChat(
210
+ llm,
211
+ [
212
+ {
213
+ role: 'system',
214
+ content:
215
+ 'You are a task analysis expert. Analyze tasks and determine their characteristics.',
216
+ },
217
+ { role: 'user', content: prompt },
218
+ ],
219
+ { model: this.model, temperature: 0.2 }
220
+ );
221
+
222
+ const parsed = parseTaskProfileResponse(content);
218
223
  if (parsed) {
219
224
  return parsed;
220
225
  }
@@ -225,8 +230,7 @@ export class CapabilityAnalyzer {
225
230
 
226
231
  private analyzeHeuristically(taskDescription: string, availableTools?: Tool[]): TaskProfile {
227
232
  const lowerTask = taskDescription.toLowerCase();
228
- const words = lowerTask.split(/\s+/);
229
- const wordCount = words.length;
233
+ const wordCount = tokenize(lowerTask).length;
230
234
 
231
235
  const domain = this.detectDomain(lowerTask);
232
236
  const complexity = this.detectComplexity(lowerTask, wordCount);
@@ -237,6 +241,8 @@ export class CapabilityAnalyzer {
237
241
  domain,
238
242
  estimatedTokens: this.estimateTokens(wordCount, complexity),
239
243
  requiresTools: toolAnalysis.requiresTools,
244
+ requiresReasoning: domain === 'reasoning' || domain === 'coding',
245
+ requiresCreativity: domain === 'creative',
240
246
  toolIntensity: toolAnalysis.intensity,
241
247
  reasoningDepth: this.estimateReasoningDepth(complexity, domain),
242
248
  creativityLevel:
@@ -257,7 +263,7 @@ export class CapabilityAnalyzer {
257
263
 
258
264
  for (const [domain, keywords] of Object.entries(DOMAIN_KEYWORDS)) {
259
265
  for (const keyword of keywords) {
260
- if (task.includes(keyword)) {
266
+ if (containsPhrase(task, keyword)) {
261
267
  scores[domain] += keyword.length > 5 ? 2 : 1;
262
268
  }
263
269
  }
@@ -269,7 +275,7 @@ export class CapabilityAnalyzer {
269
275
  }
270
276
 
271
277
  private detectComplexity(task: string, wordCount: number): TaskProfile['complexity'] {
272
- const taskWords = new Set(task.split(/\s+/));
278
+ const taskWords = new Set(tokenize(task));
273
279
 
274
280
  for (const [level, indicators] of Object.entries(COMPLEXITY_INDICATORS).reverse()) {
275
281
  for (const indicator of indicators) {
@@ -306,14 +312,14 @@ export class CapabilityAnalyzer {
306
312
 
307
313
  let toolMentions = 0;
308
314
  for (const indicator of toolIndicators) {
309
- if (task.includes(indicator)) {
315
+ if (containsPhrase(task, indicator)) {
310
316
  toolMentions++;
311
317
  }
312
318
  }
313
319
 
314
320
  if (availableTools?.length) {
315
321
  for (const tool of availableTools) {
316
- if (task.includes(tool.name.toLowerCase())) {
322
+ if (containsPhrase(task, tool.name.toLowerCase())) {
317
323
  toolMentions += 2;
318
324
  }
319
325
  }
@@ -375,20 +381,20 @@ export class CapabilityAnalyzer {
375
381
  domain: TaskProfile['domain'],
376
382
  task: string
377
383
  ): TaskProfile['accuracyRequirement'] {
378
- if (domain === 'coding' || domain === 'factual') {
379
- return 'high';
380
- }
381
-
382
384
  const criticalIndicators = ['critical', 'exact', 'precise', 'must be correct', 'no errors'];
383
385
  for (const indicator of criticalIndicators) {
384
- if (task.includes(indicator)) {
386
+ if (containsPhrase(task, indicator)) {
385
387
  return 'critical';
386
388
  }
387
389
  }
388
390
 
391
+ if (domain === 'coding' || domain === 'factual') {
392
+ return 'high';
393
+ }
394
+
389
395
  const approximateIndicators = ['rough', 'approximate', 'estimate', 'about', 'roughly'];
390
396
  for (const indicator of approximateIndicators) {
391
- if (task.includes(indicator)) {
397
+ if (containsPhrase(task, indicator)) {
392
398
  return 'approximate';
393
399
  }
394
400
  }
@@ -402,20 +408,33 @@ export class CapabilityAnalyzer {
402
408
  const relaxedIndicators = ['when possible', 'eventually', 'no rush'];
403
409
 
404
410
  for (const indicator of strictIndicators) {
405
- if (task.includes(indicator)) return 'strict';
411
+ if (containsPhrase(task, indicator)) return 'strict';
406
412
  }
407
413
  for (const indicator of moderateIndicators) {
408
- if (task.includes(indicator)) return 'moderate';
414
+ if (containsPhrase(task, indicator)) return 'moderate';
409
415
  }
410
416
  for (const indicator of relaxedIndicators) {
411
- if (task.includes(indicator)) return 'relaxed';
417
+ if (containsPhrase(task, indicator)) return 'relaxed';
412
418
  }
413
419
 
414
420
  return 'none';
415
421
  }
416
422
 
417
- private buildCacheKey(task: string): string {
418
- return task.slice(0, 200).toLowerCase().replace(/\s+/g, ' ').trim();
423
+ private buildCacheKey(
424
+ task: string,
425
+ context?: {
426
+ availableTools?: Tool[];
427
+ previousTasks?: string[];
428
+ constraints?: { maxCost?: number; maxLatency?: number };
429
+ }
430
+ ): string {
431
+ const raw = JSON.stringify([
432
+ task.toLowerCase().replace(/\s+/g, ' ').trim(),
433
+ (context?.availableTools ?? []).map((t) => t.name).sort(),
434
+ context?.previousTasks ?? [],
435
+ context?.constraints ?? {},
436
+ ]);
437
+ return createHash('sha256').update(raw).digest('hex');
419
438
  }
420
439
 
421
440
  clearCache(): void {
@@ -152,8 +152,9 @@ export class EvolutionStrategy {
152
152
 
153
153
  const d = shape - 1 / 3;
154
154
  const c = 1 / Math.sqrt(9 * d);
155
+ const maxIterations = 1000;
155
156
 
156
- while (true) {
157
+ for (let i = 0; i < maxIterations; i++) {
157
158
  let x: number;
158
159
  let v: number;
159
160
 
@@ -173,6 +174,8 @@ export class EvolutionStrategy {
173
174
  return d * v;
174
175
  }
175
176
  }
177
+
178
+ return d;
176
179
  }
177
180
 
178
181
  private sampleNormal(): number {
@@ -202,7 +205,7 @@ export class EvolutionStrategy {
202
205
  (this.strategy.explorationConstant ?? 2) / Math.sqrt(this.totalSelections)
203
206
  );
204
207
  case 'thompson_sampling':
205
- return 0.5;
208
+ return Math.max(0.05, 1 / Math.sqrt(this.totalSelections + 1));
206
209
  default:
207
210
  return 0.1;
208
211
  }
@@ -10,6 +10,7 @@ export {
10
10
  ParameterOptimizer,
11
11
  type ParameterOptimizerOptions,
12
12
  type OptimizationResult,
13
+ type HistoricalRecord,
13
14
  } from './parameter-optimizer';
14
15
 
15
16
  export {
@@ -19,5 +20,6 @@ export {
19
20
  parseTaskProfileResponse,
20
21
  parseCandidateGenerationResponse,
21
22
  parsePerformanceAnalysisResponse,
23
+ sanitizeCandidateConfig,
22
24
  ARCHITECTURE_ANALYSIS_SYSTEM_PROMPT,
23
25
  } from './prompts';
@@ -13,17 +13,23 @@ import {
13
13
  buildPerformanceAnalysisPrompt,
14
14
  parseCandidateGenerationResponse,
15
15
  parsePerformanceAnalysisResponse,
16
+ MAX_TOKENS_RANGE,
17
+ REFLECTION_DEPTH_RANGE,
18
+ TEMPERATURE_RANGE,
16
19
  } from './prompts';
20
+ import { llmChat } from '../utils/llm-helper';
17
21
 
18
22
  export interface ParameterOptimizerOptions {
19
23
  llm: LLMBackend;
20
24
  config: ArchitectureEvolutionConfig;
21
25
  baseConfig: ArchitectureConfig;
22
26
  model?: string;
27
+ availableModels?: string[];
23
28
  }
24
29
 
25
30
  export interface OptimizationResult {
26
31
  recommendedConfig: ArchitectureConfig;
32
+ taskProfile: TaskProfile;
27
33
  candidate: EvolutionCandidate | null;
28
34
  metrics: EvolutionMetrics;
29
35
  shouldAdopt: boolean;
@@ -31,7 +37,7 @@ export interface OptimizationResult {
31
37
  reasoning: string;
32
38
  }
33
39
 
34
- interface HistoricalRecord {
40
+ export interface HistoricalRecord {
35
41
  taskProfile: TaskProfile;
36
42
  config: Partial<ArchitectureConfig>;
37
43
  score: number;
@@ -44,6 +50,7 @@ export class ParameterOptimizer {
44
50
  private readonly config: ArchitectureEvolutionConfig;
45
51
  private readonly model: string;
46
52
  private readonly baseConfig: ArchitectureConfig;
53
+ private readonly availableModels: string[];
47
54
  private readonly capabilityAnalyzer: CapabilityAnalyzer;
48
55
  private readonly evolutionStrategy: EvolutionStrategy;
49
56
 
@@ -58,6 +65,7 @@ export class ParameterOptimizer {
58
65
  this.config = options.config;
59
66
  this.model = options.model ?? 'default';
60
67
  this.baseConfig = options.baseConfig;
68
+ this.availableModels = options.availableModels ?? [];
61
69
 
62
70
  this.capabilityAnalyzer = new CapabilityAnalyzer({
63
71
  llm: options.llm,
@@ -79,15 +87,28 @@ export class ParameterOptimizer {
79
87
  }
80
88
 
81
89
  const selection = this.evolutionStrategy.select(this.candidates);
90
+ const candidate = selection.candidate;
91
+
92
+ const mergedConfig = this.mergeConfigs(this.baseConfig, candidate.config);
82
93
 
83
- const mergedConfig = this.mergeConfigs(this.baseConfig, selection.candidate.config);
94
+ const bestEvaluated = this.candidates
95
+ .filter((c) => c.evaluationCount > 0)
96
+ .sort((a, b) => b.score - a.score)[0];
97
+
98
+ const threshold = this.config.adaptationThreshold ?? 0.1;
99
+ const shouldAdopt =
100
+ candidate.evaluationCount === 0 ||
101
+ !bestEvaluated ||
102
+ candidate.id === bestEvaluated.id ||
103
+ candidate.score >= bestEvaluated.score - threshold;
84
104
 
85
105
  return {
86
106
  recommendedConfig: mergedConfig,
87
- candidate: selection.candidate,
107
+ taskProfile: profile,
108
+ candidate,
88
109
  metrics: this.calculateMetrics(),
89
- shouldAdopt: true,
90
- confidence: selection.score,
110
+ shouldAdopt,
111
+ confidence: candidate.evaluationCount > 0 ? candidate.score : 0.5,
91
112
  reasoning: selection.reasoning,
92
113
  };
93
114
  }
@@ -134,11 +155,13 @@ export class ParameterOptimizer {
134
155
  const prompt = buildCandidateGenerationPrompt(
135
156
  profile,
136
157
  this.baseConfig,
137
- this.getRelevantHistory(profile)
158
+ this.getRelevantHistory(profile),
159
+ { availableModels: this.availableModels }
138
160
  );
139
161
 
140
162
  try {
141
- const response = await this.callLLM(
163
+ const content = await llmChat(
164
+ this.llm,
142
165
  [
143
166
  {
144
167
  role: 'system',
@@ -146,14 +169,17 @@ export class ParameterOptimizer {
146
169
  },
147
170
  { role: 'user', content: prompt },
148
171
  ],
149
- 0.5
172
+ { model: this.model, temperature: 0.5 }
150
173
  );
151
174
 
152
- const generated = parseCandidateGenerationResponse(response.content);
175
+ const generated = parseCandidateGenerationResponse(content, {
176
+ availableModels: this.availableModels,
177
+ }).filter((c) => c.risk !== 'high');
153
178
 
179
+ const maxCandidates = this.config.maxCandidates ?? 10;
154
180
  this.candidates = [
155
181
  this.createBaselineCandidate(),
156
- ...generated.map((c) => ({
182
+ ...generated.slice(0, Math.max(0, maxCandidates - 1)).map((c) => ({
157
183
  ...c,
158
184
  generation: this.currentGeneration,
159
185
  })),
@@ -247,22 +273,28 @@ export class ParameterOptimizer {
247
273
  const mutated = { ...config };
248
274
  const mutationRate = 0.3;
249
275
 
250
- if (Math.random() < mutationRate && mutated.temperature !== undefined) {
276
+ if (Math.random() < mutationRate) {
277
+ const base = mutated.temperature ?? this.baseConfig.temperature;
251
278
  mutated.temperature = Math.max(
252
- 0,
253
- Math.min(2, mutated.temperature + (Math.random() - 0.5) * 0.3)
279
+ TEMPERATURE_RANGE.min,
280
+ Math.min(TEMPERATURE_RANGE.max, base + (Math.random() - 0.5) * 0.3)
254
281
  );
255
282
  }
256
283
 
257
- if (Math.random() < mutationRate && mutated.maxTokens !== undefined) {
284
+ if (Math.random() < mutationRate) {
285
+ const base = mutated.maxTokens ?? this.baseConfig.maxTokens;
258
286
  const delta = Math.floor((Math.random() - 0.5) * 1000);
259
- mutated.maxTokens = Math.max(100, Math.min(32000, mutated.maxTokens + delta));
287
+ mutated.maxTokens = Math.max(
288
+ MAX_TOKENS_RANGE.min,
289
+ Math.min(MAX_TOKENS_RANGE.max, base + delta)
290
+ );
260
291
  }
261
292
 
262
- if (Math.random() < mutationRate && mutated.reflectionDepth !== undefined) {
293
+ if (Math.random() < mutationRate) {
294
+ const base = mutated.reflectionDepth ?? this.baseConfig.reflectionDepth;
263
295
  mutated.reflectionDepth = Math.max(
264
- 0,
265
- Math.min(5, mutated.reflectionDepth + Math.round(Math.random() * 2 - 1))
296
+ REFLECTION_DEPTH_RANGE.min,
297
+ Math.min(REFLECTION_DEPTH_RANGE.max, base + Math.round(Math.random() * 2 - 1))
266
298
  );
267
299
  }
268
300
 
@@ -316,22 +348,26 @@ export class ParameterOptimizer {
316
348
  }
317
349
 
318
350
  private shouldEvolve(): boolean {
319
- const minEvaluations = this.candidates.reduce(
351
+ const evaluatedCandidates = this.candidates.filter((c) => c.evaluationCount > 0);
352
+ if (evaluatedCandidates.length === 0) return false;
353
+
354
+ const minEvaluations = evaluatedCandidates.reduce(
320
355
  (min, c) => Math.min(min, c.evaluationCount),
321
356
  Infinity
322
357
  );
323
358
 
324
- if (minEvaluations < 3) return false;
359
+ const minRequired = this.config.minEvaluationsBeforeEvolution ?? 3;
360
+ if (minEvaluations < minRequired) return false;
325
361
 
326
362
  const evaluationsSinceEvolution = this.history.filter(
327
- (h) => h.timestamp > this.getLastEvolutionTime()
363
+ (h) => h.timestamp > this.lastEvolutionTimestamp
328
364
  ).length;
329
365
 
330
- return evaluationsSinceEvolution >= 5;
366
+ return evaluationsSinceEvolution >= this.getEvaluationWindow();
331
367
  }
332
368
 
333
- private getLastEvolutionTime(): number {
334
- return this.lastEvolutionTimestamp;
369
+ private getEvaluationWindow(): number {
370
+ return Math.max(1, this.config.evaluationWindow ?? 10);
335
371
  }
336
372
 
337
373
  private getRelevantHistory(profile: TaskProfile): HistoricalRecord[] {
@@ -340,7 +376,7 @@ export class ParameterOptimizer {
340
376
  (h) =>
341
377
  h.taskProfile.domain === profile.domain || h.taskProfile.complexity === profile.complexity
342
378
  )
343
- .slice(-10);
379
+ .slice(-this.getEvaluationWindow());
344
380
  }
345
381
 
346
382
  private mergeConfigs(
@@ -418,13 +454,28 @@ export class ParameterOptimizer {
418
454
  fields++;
419
455
  }
420
456
 
457
+ if (
458
+ a.toolStrategy !== undefined &&
459
+ b.toolStrategy !== undefined &&
460
+ a.toolStrategy !== b.toolStrategy
461
+ ) {
462
+ distance += 1;
463
+ fields++;
464
+ }
465
+
466
+ if (a.reflectionDepth !== undefined && b.reflectionDepth !== undefined) {
467
+ distance += Math.abs(a.reflectionDepth - b.reflectionDepth) / 5;
468
+ fields++;
469
+ }
470
+
421
471
  return fields > 0 ? distance / fields : 0;
422
472
  }
423
473
 
424
474
  private calculateConvergenceRate(): number {
425
- if (this.history.length < 10) return 0;
475
+ const window = this.getEvaluationWindow();
476
+ if (this.history.length < window) return 0;
426
477
 
427
- const recentScores = this.history.slice(-10).map((h) => h.score);
478
+ const recentScores = this.history.slice(-window).map((h) => h.score);
428
479
  const variance = this.calculateVariance(recentScores);
429
480
 
430
481
  return Math.max(0, 1 - variance * 4);
@@ -471,30 +522,21 @@ export class ParameterOptimizer {
471
522
  const prompt = buildPerformanceAnalysisPrompt(evaluatedCandidates, results);
472
523
 
473
524
  try {
474
- const response = await this.callLLM(
525
+ const content = await llmChat(
526
+ this.llm,
475
527
  [
476
528
  { role: 'system', content: 'You are an AI performance analyst.' },
477
529
  { role: 'user', content: prompt },
478
530
  ],
479
- 0.2
531
+ { model: this.model, temperature: 0.2 }
480
532
  );
481
533
 
482
- return parsePerformanceAnalysisResponse(response.content);
534
+ return parsePerformanceAnalysisResponse(content);
483
535
  } catch {
484
536
  return null;
485
537
  }
486
538
  }
487
539
 
488
- private async callLLM(
489
- messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,
490
- temperature: number
491
- ) {
492
- if (this.llm.complete) {
493
- return this.llm.complete({ messages, temperature });
494
- }
495
- return this.llm.chat({ model: this.model, messages, temperature });
496
- }
497
-
498
540
  getCandidates(): EvolutionCandidate[] {
499
541
  return [...this.candidates];
500
542
  }