@cogitator-ai/self-modifying 17.0.18 → 19.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 (84) hide show
  1. package/README.md +114 -50
  2. package/dist/architecture-evolution/capability-analyzer.d.ts.map +1 -1
  3. package/dist/architecture-evolution/capability-analyzer.js +38 -37
  4. package/dist/architecture-evolution/capability-analyzer.js.map +1 -1
  5. package/dist/architecture-evolution/index.d.ts +2 -2
  6. package/dist/architecture-evolution/index.d.ts.map +1 -1
  7. package/dist/architecture-evolution/index.js +1 -1
  8. package/dist/architecture-evolution/index.js.map +1 -1
  9. package/dist/architecture-evolution/parameter-optimizer.d.ts +5 -4
  10. package/dist/architecture-evolution/parameter-optimizer.d.ts.map +1 -1
  11. package/dist/architecture-evolution/parameter-optimizer.js +36 -31
  12. package/dist/architecture-evolution/parameter-optimizer.js.map +1 -1
  13. package/dist/architecture-evolution/prompts.d.ts +21 -2
  14. package/dist/architecture-evolution/prompts.d.ts.map +1 -1
  15. package/dist/architecture-evolution/prompts.js +150 -36
  16. package/dist/architecture-evolution/prompts.js.map +1 -1
  17. package/dist/constraints/modification-validator.d.ts +1 -0
  18. package/dist/constraints/modification-validator.d.ts.map +1 -1
  19. package/dist/constraints/modification-validator.js +30 -14
  20. package/dist/constraints/modification-validator.js.map +1 -1
  21. package/dist/index.d.ts +4 -4
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -3
  24. package/dist/index.js.map +1 -1
  25. package/dist/meta-reasoning/meta-reasoner.d.ts +3 -0
  26. package/dist/meta-reasoning/meta-reasoner.d.ts.map +1 -1
  27. package/dist/meta-reasoning/meta-reasoner.js +101 -33
  28. package/dist/meta-reasoning/meta-reasoner.js.map +1 -1
  29. package/dist/meta-reasoning/prompts.d.ts +10 -14
  30. package/dist/meta-reasoning/prompts.d.ts.map +1 -1
  31. package/dist/meta-reasoning/prompts.js +109 -1
  32. package/dist/meta-reasoning/prompts.js.map +1 -1
  33. package/dist/self-modifying-agent.d.ts +83 -17
  34. package/dist/self-modifying-agent.d.ts.map +1 -1
  35. package/dist/self-modifying-agent.js +557 -444
  36. package/dist/self-modifying-agent.js.map +1 -1
  37. package/dist/tool-generation/gap-analyzer.d.ts +0 -1
  38. package/dist/tool-generation/gap-analyzer.d.ts.map +1 -1
  39. package/dist/tool-generation/gap-analyzer.js +14 -14
  40. package/dist/tool-generation/gap-analyzer.js.map +1 -1
  41. package/dist/tool-generation/index.d.ts +3 -3
  42. package/dist/tool-generation/index.d.ts.map +1 -1
  43. package/dist/tool-generation/index.js +2 -2
  44. package/dist/tool-generation/index.js.map +1 -1
  45. package/dist/tool-generation/prompts.d.ts +1 -0
  46. package/dist/tool-generation/prompts.d.ts.map +1 -1
  47. package/dist/tool-generation/prompts.js +33 -8
  48. package/dist/tool-generation/prompts.js.map +1 -1
  49. package/dist/tool-generation/tool-generator.d.ts +5 -5
  50. package/dist/tool-generation/tool-generator.d.ts.map +1 -1
  51. package/dist/tool-generation/tool-generator.js +41 -28
  52. package/dist/tool-generation/tool-generator.js.map +1 -1
  53. package/dist/tool-generation/tool-sandbox.d.ts +9 -6
  54. package/dist/tool-generation/tool-sandbox.d.ts.map +1 -1
  55. package/dist/tool-generation/tool-sandbox.js +257 -157
  56. package/dist/tool-generation/tool-sandbox.js.map +1 -1
  57. package/dist/tool-generation/tool-validator.d.ts +3 -8
  58. package/dist/tool-generation/tool-validator.d.ts.map +1 -1
  59. package/dist/tool-generation/tool-validator.js +53 -54
  60. package/dist/tool-generation/tool-validator.js.map +1 -1
  61. package/dist/utils/llm-helper.d.ts.map +1 -1
  62. package/dist/utils/llm-helper.js +1 -0
  63. package/dist/utils/llm-helper.js.map +1 -1
  64. package/package.json +11 -18
  65. package/src/__tests__/component-regressions.test.ts +599 -0
  66. package/src/__tests__/meta-reasoning.test.ts +5 -1
  67. package/src/__tests__/sandbox-security.test.ts +186 -0
  68. package/src/__tests__/self-modifying-agent.test.ts +363 -1
  69. package/src/architecture-evolution/capability-analyzer.ts +56 -40
  70. package/src/architecture-evolution/index.ts +2 -0
  71. package/src/architecture-evolution/parameter-optimizer.ts +52 -34
  72. package/src/architecture-evolution/prompts.ts +179 -41
  73. package/src/constraints/modification-validator.ts +30 -14
  74. package/src/index.ts +14 -0
  75. package/src/meta-reasoning/meta-reasoner.ts +105 -40
  76. package/src/meta-reasoning/prompts.ts +129 -15
  77. package/src/self-modifying-agent.ts +764 -489
  78. package/src/tool-generation/gap-analyzer.ts +19 -18
  79. package/src/tool-generation/index.ts +13 -3
  80. package/src/tool-generation/prompts.ts +41 -8
  81. package/src/tool-generation/tool-generator.ts +59 -33
  82. package/src/tool-generation/tool-sandbox.ts +306 -175
  83. package/src/tool-generation/tool-validator.ts +66 -63
  84. package/src/utils/llm-helper.ts +1 -0
@@ -7,6 +7,7 @@ import type {
7
7
  ToolSelfGenerationConfig,
8
8
  } from '@cogitator-ai/types';
9
9
  import { buildGapAnalysisPrompt, parseGapAnalysisResponse } from './prompts';
10
+ import { llmChat } from '../utils/llm-helper';
10
11
 
11
12
  export interface GapAnalyzerOptions {
12
13
  llm: LLMBackend;
@@ -34,7 +35,7 @@ export class GapAnalyzer {
34
35
  previousGaps?: CapabilityGap[];
35
36
  }
36
37
  ): Promise<GapAnalysisResult> {
37
- const cacheKey = this.buildCacheKey(userIntent, availableTools);
38
+ const cacheKey = this.buildCacheKey(userIntent, availableTools, context);
38
39
  const cached = this.analysisCache.get(cacheKey);
39
40
  if (cached && Date.now() - cached.timestamp.getTime() < 60000) {
40
41
  return cached;
@@ -47,7 +48,8 @@ export class GapAnalyzer {
47
48
 
48
49
  const prompt = buildGapAnalysisPrompt(userIntent, toolSummaries, context?.failedAttempts);
49
50
 
50
- const response = await this.callLLM(
51
+ const content = await llmChat(
52
+ this.llm,
51
53
  [
52
54
  {
53
55
  role: 'system',
@@ -58,10 +60,10 @@ Consider tool composition before suggesting new tools.`,
58
60
  },
59
61
  { role: 'user', content: prompt },
60
62
  ],
61
- 0.3
63
+ { model: this.model, temperature: 0.3 }
62
64
  );
63
65
 
64
- const parsed = parseGapAnalysisResponse(response.content);
66
+ const parsed = parseGapAnalysisResponse(content);
65
67
  const filteredGaps = this.filterAndPrioritizeGaps(
66
68
  parsed.gaps,
67
69
  availableTools,
@@ -217,26 +219,25 @@ Consider tool composition before suggesting new tools.`,
217
219
  return `Identified ${gaps.length} capability gap(s):\n${gapDescriptions}`;
218
220
  }
219
221
 
220
- private buildCacheKey(userIntent: string, tools: Tool[]): string {
222
+ private buildCacheKey(
223
+ userIntent: string,
224
+ tools: Tool[],
225
+ context?: { failedAttempts?: string[]; previousGaps?: CapabilityGap[] }
226
+ ): string {
221
227
  const toolSignature = tools
222
- .map((t) => t.name)
228
+ .map((t) => `${t.name}:${t.description}`)
223
229
  .sort()
224
- .join(',');
225
- const raw = `${userIntent}|${toolSignature}`;
230
+ .join('\n');
231
+ const raw = JSON.stringify([
232
+ userIntent,
233
+ toolSignature,
234
+ context?.failedAttempts ?? [],
235
+ (context?.previousGaps ?? []).map((g) => g.id).sort(),
236
+ ]);
226
237
  return createHash('sha256').update(raw).digest('hex');
227
238
  }
228
239
 
229
240
  clearCache(): void {
230
241
  this.analysisCache.clear();
231
242
  }
232
-
233
- private async callLLM(
234
- messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,
235
- temperature: number
236
- ) {
237
- if (this.llm.complete) {
238
- return this.llm.complete({ messages, temperature });
239
- }
240
- return this.llm.chat({ model: this.model, messages, temperature });
241
- }
242
243
  }
@@ -1,7 +1,17 @@
1
1
  export { GapAnalyzer, type GapAnalyzerOptions } from './gap-analyzer';
2
- export { ToolGenerator, type ToolGeneratorOptions, type GenerationResult } from './tool-generator';
3
- export { ToolValidator, type ToolValidatorOptions } from './tool-validator';
4
- export { ToolSandbox, DEFAULT_SANDBOX_CONFIG, deepEqual } from './tool-sandbox';
2
+ export {
3
+ ToolGenerator,
4
+ type ToolGeneratorOptions,
5
+ type GenerateOptions,
6
+ type GenerationResult,
7
+ } from './tool-generator';
8
+ export { ToolValidator, type ToolValidatorOptions, type ValidationRule } from './tool-validator';
9
+ export {
10
+ ToolSandbox,
11
+ DEFAULT_SANDBOX_CONFIG,
12
+ deepEqual,
13
+ type SandboxTestCase,
14
+ } from './tool-sandbox';
5
15
  export {
6
16
  InMemoryGeneratedToolStore,
7
17
  type ToolUsageRecord,
@@ -78,6 +78,7 @@ export function buildToolGenerationPrompt(
78
78
  maxLines?: number;
79
79
  allowedModules?: string[];
80
80
  securityLevel?: 'strict' | 'moderate' | 'permissive';
81
+ parameters?: Record<string, unknown>;
81
82
  }
82
83
  ): string {
83
84
  const toolName = sanitizeToolName(gap.suggestedToolName);
@@ -112,12 +113,19 @@ export function buildToolGenerationPrompt(
112
113
  const constraintsText =
113
114
  constraintsSection.length > 0 ? `\nCONSTRAINTS:\n${constraintsSection.join('\n')}\n` : '';
114
115
 
116
+ const parametersJson = constraints?.parameters
117
+ ? JSON.stringify(constraints.parameters)
118
+ : '{"type":"object","properties":{/* DEFINE PARAMS */},"required":[/* REQUIRED PARAMS */]}';
119
+ const parametersText = constraints?.parameters
120
+ ? `\nThe tool MUST accept exactly this parameters JSON Schema: ${parametersJson}\n`
121
+ : '';
122
+
115
123
  return `Create a tool named "${toolName}".
116
124
  Task: ${description}
117
125
  Formula/Logic: ${capability}
118
- ${existingToolsSection}${constraintsText}
126
+ ${existingToolsSection}${constraintsText}${parametersText}
119
127
  Respond with ONLY this JSON (no other text):
120
- {"name":"${toolName}","description":"${description}","implementation":"async function execute(params) { /* YOUR CODE HERE — throw on invalid input */ }","parameters":{"type":"object","properties":{/* DEFINE PARAMS */},"required":[/* REQUIRED PARAMS */]},"reasoning":"explanation"}`;
128
+ {"name":"${toolName}","description":"${description}","implementation":"async function execute(params) { /* YOUR CODE HERE — throw on invalid input */ }","parameters":${parametersJson},"reasoning":"explanation"}`;
121
129
  }
122
130
 
123
131
  export function buildToolValidationPrompt(
@@ -224,7 +232,10 @@ export function parseGapAnalysisResponse(response: string): {
224
232
  complexity: (['simple', 'moderate', 'complex'].includes(String(g.complexity))
225
233
  ? g.complexity
226
234
  : 'moderate') as 'simple' | 'moderate' | 'complex',
227
- confidence: typeof g.confidence === 'number' ? g.confidence : 0.5,
235
+ confidence:
236
+ typeof g.confidence === 'number' && Number.isFinite(g.confidence)
237
+ ? Math.min(1, Math.max(0, g.confidence))
238
+ : 0.5,
228
239
  reasoning: String(g.reasoning || ''),
229
240
  }))
230
241
  : [],
@@ -279,16 +290,38 @@ export function parseToolGenerationResponse(response: string): GeneratedTool | n
279
290
  return null;
280
291
  }
281
292
 
293
+ function normalizeParameters(value: unknown): Record<string, unknown> {
294
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
295
+ return { type: 'object', properties: {} };
296
+ }
297
+
298
+ const schema = value as Record<string, unknown>;
299
+ const properties =
300
+ schema.properties && typeof schema.properties === 'object' && !Array.isArray(schema.properties)
301
+ ? (schema.properties as Record<string, unknown>)
302
+ : {};
303
+ const required = Array.isArray(schema.required)
304
+ ? schema.required.filter(
305
+ (key): key is string => typeof key === 'string' && Object.hasOwn(properties, key)
306
+ )
307
+ : [];
308
+
309
+ const normalized: Record<string, unknown> = { ...schema, type: 'object', properties };
310
+ if (required.length > 0) {
311
+ normalized.required = required;
312
+ } else {
313
+ delete normalized.required;
314
+ }
315
+ return normalized;
316
+ }
317
+
282
318
  function buildToolFromParsed(parsed: Record<string, unknown>): GeneratedTool {
283
319
  return {
284
320
  id: `gen_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
285
- name: String(parsed.name),
321
+ name: sanitizeToolName(String(parsed.name)),
286
322
  description: String(parsed.description || ''),
287
323
  implementation: String(parsed.implementation),
288
- parameters: (parsed.parameters as GeneratedTool['parameters']) || {
289
- type: 'object',
290
- properties: {},
291
- },
324
+ parameters: normalizeParameters(parsed.parameters),
292
325
  createdAt: new Date(),
293
326
  version: 1,
294
327
  status: 'pending_validation',
@@ -1,5 +1,6 @@
1
1
  import type {
2
2
  Tool,
3
+ ToolSchema,
3
4
  LLMBackend,
4
5
  CapabilityGap,
5
6
  GeneratedTool,
@@ -8,7 +9,8 @@ import type {
8
9
  } from '@cogitator-ai/types';
9
10
  import { z, type ZodType } from 'zod';
10
11
  import { ToolValidator } from './tool-validator';
11
- import { ToolSandbox } from './tool-sandbox';
12
+ import { ToolSandbox, type SandboxTestCase } from './tool-sandbox';
13
+ import { llmChat } from '../utils/llm-helper';
12
14
  import {
13
15
  TOOL_GENERATION_SYSTEM_PROMPT,
14
16
  buildToolGenerationPrompt,
@@ -22,6 +24,10 @@ export interface ToolGeneratorOptions {
22
24
  model?: string;
23
25
  }
24
26
 
27
+ export interface GenerateOptions {
28
+ parameters?: Record<string, unknown>;
29
+ }
30
+
25
31
  export interface GenerationResult {
26
32
  tool: GeneratedTool | null;
27
33
  validationResult: ToolValidationResult | null;
@@ -52,7 +58,8 @@ export class ToolGenerator {
52
58
  async generate(
53
59
  gap: CapabilityGap,
54
60
  existingTools: Tool[],
55
- testCases?: Array<{ input: unknown; expectedOutput?: unknown }>
61
+ testCases?: SandboxTestCase[],
62
+ options: GenerateOptions = {}
56
63
  ): Promise<GenerationResult> {
57
64
  let currentTool: GeneratedTool | null = null;
58
65
  let validationResult: ToolValidationResult | null = null;
@@ -65,15 +72,23 @@ export class ToolGenerator {
65
72
 
66
73
  try {
67
74
  if (currentTool === null) {
68
- currentTool = await this.generateInitial(gap, existingTools);
75
+ currentTool = await this.generateInitial(gap, existingTools, options.parameters);
69
76
  } else if (validationResult) {
70
- currentTool = await this.improve(currentTool, validationResult, iterations);
77
+ const improved = await this.improve(currentTool, validationResult, iterations);
78
+ if (!improved) {
79
+ continue;
80
+ }
81
+ currentTool = improved;
71
82
  }
72
83
 
73
84
  if (!currentTool) {
74
85
  continue;
75
86
  }
76
87
 
88
+ if (options.parameters) {
89
+ currentTool.parameters = options.parameters;
90
+ }
91
+
77
92
  validationResult = await this.validator.validate(currentTool, testCases);
78
93
 
79
94
  if (validationResult.isValid) {
@@ -132,21 +147,22 @@ export class ToolGenerator {
132
147
  reasoning: 'User-requested quick generation',
133
148
  };
134
149
 
135
- const result = await this.generate(gap, []);
136
-
137
- if (result.tool && Object.keys(parameters).length > 0) {
138
- result.tool.parameters = {
139
- type: 'object',
140
- properties: parameters,
141
- };
142
- }
150
+ const result = await this.generate(
151
+ gap,
152
+ [],
153
+ undefined,
154
+ Object.keys(parameters).length > 0
155
+ ? { parameters: { type: 'object', properties: parameters } }
156
+ : {}
157
+ );
143
158
 
144
- return result.tool;
159
+ return result.success ? result.tool : null;
145
160
  }
146
161
 
147
162
  private async generateInitial(
148
163
  gap: CapabilityGap,
149
- existingTools: Tool[]
164
+ existingTools: Tool[],
165
+ parameters?: Record<string, unknown>
150
166
  ): Promise<GeneratedTool | null> {
151
167
  const toolSummaries = existingTools.map((t) => ({
152
168
  name: t.name,
@@ -157,17 +173,19 @@ export class ToolGenerator {
157
173
  maxLines: 100,
158
174
  securityLevel: 'strict',
159
175
  allowedModules: this.config.sandboxConfig?.allowedModules,
176
+ parameters,
160
177
  });
161
178
 
162
- const response = await this.callLLM(
179
+ const content = await llmChat(
180
+ this.llm,
163
181
  [
164
182
  { role: 'system', content: TOOL_GENERATION_SYSTEM_PROMPT },
165
183
  { role: 'user', content: prompt },
166
184
  ],
167
- 0.4
185
+ { model: this.model, temperature: 0.4 }
168
186
  );
169
187
 
170
- const tool = parseToolGenerationResponse(response.content);
188
+ const tool = parseToolGenerationResponse(content);
171
189
 
172
190
  if (tool) {
173
191
  tool.metadata = {
@@ -187,18 +205,21 @@ export class ToolGenerator {
187
205
  ): Promise<GeneratedTool | null> {
188
206
  const prompt = buildToolImprovementPrompt(tool, validationResult, iteration);
189
207
 
190
- const response = await this.callLLM(
208
+ const content = await llmChat(
209
+ this.llm,
191
210
  [
192
211
  { role: 'system', content: TOOL_GENERATION_SYSTEM_PROMPT },
193
212
  { role: 'user', content: prompt },
194
213
  ],
195
- 0.3
214
+ { model: this.model, temperature: 0.3 }
196
215
  );
197
216
 
198
- const improved = parseToolGenerationResponse(response.content);
217
+ const improved = parseToolGenerationResponse(content);
199
218
 
200
219
  if (improved) {
201
220
  improved.id = tool.id;
221
+ improved.name = tool.name;
222
+ improved.createdAt = tool.createdAt;
202
223
  improved.version = tool.version + 1;
203
224
  improved.metadata = {
204
225
  ...tool.metadata,
@@ -215,6 +236,7 @@ export class ToolGenerator {
215
236
  const sandbox = this.sandbox;
216
237
  const tool = generated;
217
238
  const paramSchema = buildZodSchema(generated.parameters);
239
+ const jsonSchema = toToolJsonSchema(generated.parameters);
218
240
 
219
241
  const execute = async (params: unknown): Promise<unknown> => {
220
242
  const parsed = paramSchema.safeParse(params);
@@ -239,23 +261,27 @@ export class ToolGenerator {
239
261
  toJSON: () => ({
240
262
  name: generated.name,
241
263
  description: generated.description,
242
- parameters: {
243
- type: 'object' as const,
244
- properties: generated.parameters as Record<string, unknown>,
245
- },
264
+ parameters: jsonSchema,
246
265
  }),
247
266
  };
248
267
  }
268
+ }
249
269
 
250
- private async callLLM(
251
- messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,
252
- temperature: number
253
- ) {
254
- if (this.llm.complete) {
255
- return this.llm.complete({ messages, temperature });
256
- }
257
- return this.llm.chat({ model: this.model, messages, temperature });
258
- }
270
+ function toToolJsonSchema(parameters: Record<string, unknown>): ToolSchema['parameters'] {
271
+ const properties =
272
+ parameters.type === 'object' &&
273
+ parameters.properties &&
274
+ typeof parameters.properties === 'object' &&
275
+ !Array.isArray(parameters.properties)
276
+ ? (parameters.properties as Record<string, unknown>)
277
+ : {};
278
+ const required = Array.isArray(parameters.required)
279
+ ? parameters.required.filter((key): key is string => typeof key === 'string')
280
+ : [];
281
+
282
+ return required.length > 0
283
+ ? { type: 'object', properties, required }
284
+ : { type: 'object', properties };
259
285
  }
260
286
 
261
287
  function buildZodSchema(parameters: Record<string, unknown>): ZodType<unknown> {