@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.
- package/README.md +112 -48
- package/dist/architecture-evolution/capability-analyzer.d.ts.map +1 -1
- package/dist/architecture-evolution/capability-analyzer.js +44 -40
- package/dist/architecture-evolution/capability-analyzer.js.map +1 -1
- package/dist/architecture-evolution/evolution-strategy.d.ts.map +1 -1
- package/dist/architecture-evolution/evolution-strategy.js +4 -2
- package/dist/architecture-evolution/evolution-strategy.js.map +1 -1
- package/dist/architecture-evolution/index.d.ts +2 -2
- package/dist/architecture-evolution/index.d.ts.map +1 -1
- package/dist/architecture-evolution/index.js +1 -1
- package/dist/architecture-evolution/index.js.map +1 -1
- package/dist/architecture-evolution/parameter-optimizer.d.ts +5 -4
- package/dist/architecture-evolution/parameter-optimizer.d.ts.map +1 -1
- package/dist/architecture-evolution/parameter-optimizer.js +62 -34
- package/dist/architecture-evolution/parameter-optimizer.js.map +1 -1
- package/dist/architecture-evolution/prompts.d.ts +21 -2
- package/dist/architecture-evolution/prompts.d.ts.map +1 -1
- package/dist/architecture-evolution/prompts.js +189 -37
- package/dist/architecture-evolution/prompts.js.map +1 -1
- package/dist/constraints/modification-validator.d.ts +1 -0
- package/dist/constraints/modification-validator.d.ts.map +1 -1
- package/dist/constraints/modification-validator.js +45 -18
- package/dist/constraints/modification-validator.js.map +1 -1
- package/dist/constraints/rollback-manager.d.ts +1 -0
- package/dist/constraints/rollback-manager.d.ts.map +1 -1
- package/dist/constraints/rollback-manager.js +25 -1
- package/dist/constraints/rollback-manager.js.map +1 -1
- package/dist/events/event-emitter.d.ts +0 -1
- package/dist/events/event-emitter.d.ts.map +1 -1
- package/dist/events/event-emitter.js +6 -10
- package/dist/events/event-emitter.js.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/meta-reasoning/meta-reasoner.d.ts +4 -1
- package/dist/meta-reasoning/meta-reasoner.d.ts.map +1 -1
- package/dist/meta-reasoning/meta-reasoner.js +120 -43
- package/dist/meta-reasoning/meta-reasoner.js.map +1 -1
- package/dist/meta-reasoning/observation-collector.d.ts.map +1 -1
- package/dist/meta-reasoning/observation-collector.js +6 -2
- package/dist/meta-reasoning/observation-collector.js.map +1 -1
- package/dist/meta-reasoning/prompts.d.ts +10 -14
- package/dist/meta-reasoning/prompts.d.ts.map +1 -1
- package/dist/meta-reasoning/prompts.js +108 -33
- package/dist/meta-reasoning/prompts.js.map +1 -1
- package/dist/meta-reasoning/strategy-selector.d.ts.map +1 -1
- package/dist/meta-reasoning/strategy-selector.js +6 -0
- package/dist/meta-reasoning/strategy-selector.js.map +1 -1
- package/dist/self-modifying-agent.d.ts +88 -15
- package/dist/self-modifying-agent.d.ts.map +1 -1
- package/dist/self-modifying-agent.js +583 -376
- package/dist/self-modifying-agent.js.map +1 -1
- package/dist/tool-generation/gap-analyzer.d.ts +0 -1
- package/dist/tool-generation/gap-analyzer.d.ts.map +1 -1
- package/dist/tool-generation/gap-analyzer.js +16 -15
- package/dist/tool-generation/gap-analyzer.js.map +1 -1
- package/dist/tool-generation/generated-tool-store.d.ts.map +1 -1
- package/dist/tool-generation/generated-tool-store.js +90 -10
- package/dist/tool-generation/generated-tool-store.js.map +1 -1
- package/dist/tool-generation/index.d.ts +3 -3
- package/dist/tool-generation/index.d.ts.map +1 -1
- package/dist/tool-generation/index.js +2 -2
- package/dist/tool-generation/index.js.map +1 -1
- package/dist/tool-generation/prompts.d.ts +4 -3
- package/dist/tool-generation/prompts.d.ts.map +1 -1
- package/dist/tool-generation/prompts.js +96 -18
- package/dist/tool-generation/prompts.js.map +1 -1
- package/dist/tool-generation/tool-generator.d.ts +6 -6
- package/dist/tool-generation/tool-generator.d.ts.map +1 -1
- package/dist/tool-generation/tool-generator.js +88 -23
- package/dist/tool-generation/tool-generator.js.map +1 -1
- package/dist/tool-generation/tool-sandbox.d.ts +10 -11
- package/dist/tool-generation/tool-sandbox.d.ts.map +1 -1
- package/dist/tool-generation/tool-sandbox.js +346 -156
- package/dist/tool-generation/tool-sandbox.js.map +1 -1
- package/dist/tool-generation/tool-validator.d.ts +4 -8
- package/dist/tool-generation/tool-validator.d.ts.map +1 -1
- package/dist/tool-generation/tool-validator.js +74 -28
- package/dist/tool-generation/tool-validator.js.map +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +30 -5
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/llm-helper.d.ts.map +1 -1
- package/dist/utils/llm-helper.js +1 -0
- package/dist/utils/llm-helper.js.map +1 -1
- package/package.json +3 -12
- package/src/__tests__/component-regressions.test.ts +599 -0
- package/src/__tests__/meta-reasoning.test.ts +5 -1
- package/src/__tests__/sandbox-security.test.ts +186 -0
- package/src/__tests__/self-modifying-agent.test.ts +405 -11
- package/src/architecture-evolution/capability-analyzer.ts +63 -44
- package/src/architecture-evolution/evolution-strategy.ts +5 -2
- package/src/architecture-evolution/index.ts +2 -0
- package/src/architecture-evolution/parameter-optimizer.ts +82 -40
- package/src/architecture-evolution/prompts.ts +224 -42
- package/src/constraints/modification-validator.ts +48 -21
- package/src/constraints/rollback-manager.ts +20 -1
- package/src/events/event-emitter.ts +6 -15
- package/src/index.ts +14 -0
- package/src/meta-reasoning/meta-reasoner.ts +124 -52
- package/src/meta-reasoning/observation-collector.ts +6 -2
- package/src/meta-reasoning/prompts.ts +131 -56
- package/src/meta-reasoning/strategy-selector.ts +6 -0
- package/src/self-modifying-agent.ts +797 -413
- package/src/tool-generation/gap-analyzer.ts +21 -19
- package/src/tool-generation/generated-tool-store.ts +97 -10
- package/src/tool-generation/index.ts +13 -3
- package/src/tool-generation/prompts.ts +120 -18
- package/src/tool-generation/tool-generator.ts +120 -26
- package/src/tool-generation/tool-sandbox.ts +408 -206
- package/src/tool-generation/tool-validator.ts +90 -37
- package/src/utils/index.ts +29 -5
- package/src/utils/llm-helper.ts +1 -0
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
1
2
|
import type {
|
|
2
3
|
Tool,
|
|
3
4
|
LLMBackend,
|
|
@@ -6,6 +7,7 @@ import type {
|
|
|
6
7
|
ToolSelfGenerationConfig,
|
|
7
8
|
} from '@cogitator-ai/types';
|
|
8
9
|
import { buildGapAnalysisPrompt, parseGapAnalysisResponse } from './prompts';
|
|
10
|
+
import { llmChat } from '../utils/llm-helper';
|
|
9
11
|
|
|
10
12
|
export interface GapAnalyzerOptions {
|
|
11
13
|
llm: LLMBackend;
|
|
@@ -33,7 +35,7 @@ export class GapAnalyzer {
|
|
|
33
35
|
previousGaps?: CapabilityGap[];
|
|
34
36
|
}
|
|
35
37
|
): Promise<GapAnalysisResult> {
|
|
36
|
-
const cacheKey = this.buildCacheKey(userIntent, availableTools);
|
|
38
|
+
const cacheKey = this.buildCacheKey(userIntent, availableTools, context);
|
|
37
39
|
const cached = this.analysisCache.get(cacheKey);
|
|
38
40
|
if (cached && Date.now() - cached.timestamp.getTime() < 60000) {
|
|
39
41
|
return cached;
|
|
@@ -46,7 +48,8 @@ export class GapAnalyzer {
|
|
|
46
48
|
|
|
47
49
|
const prompt = buildGapAnalysisPrompt(userIntent, toolSummaries, context?.failedAttempts);
|
|
48
50
|
|
|
49
|
-
const
|
|
51
|
+
const content = await llmChat(
|
|
52
|
+
this.llm,
|
|
50
53
|
[
|
|
51
54
|
{
|
|
52
55
|
role: 'system',
|
|
@@ -57,10 +60,10 @@ Consider tool composition before suggesting new tools.`,
|
|
|
57
60
|
},
|
|
58
61
|
{ role: 'user', content: prompt },
|
|
59
62
|
],
|
|
60
|
-
0.3
|
|
63
|
+
{ model: this.model, temperature: 0.3 }
|
|
61
64
|
);
|
|
62
65
|
|
|
63
|
-
const parsed = parseGapAnalysisResponse(
|
|
66
|
+
const parsed = parseGapAnalysisResponse(content);
|
|
64
67
|
const filteredGaps = this.filterAndPrioritizeGaps(
|
|
65
68
|
parsed.gaps,
|
|
66
69
|
availableTools,
|
|
@@ -151,7 +154,6 @@ Consider tool composition before suggesting new tools.`,
|
|
|
151
154
|
return 1.0;
|
|
152
155
|
}
|
|
153
156
|
|
|
154
|
-
const _intentWords = userIntent.toLowerCase().split(/\s+/).length;
|
|
155
157
|
const toolDescriptionWords = availableTools
|
|
156
158
|
.map((t) => t.description.toLowerCase().split(/\s+/))
|
|
157
159
|
.flat();
|
|
@@ -217,25 +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(
|
|
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
|
-
|
|
230
|
+
.join('\n');
|
|
231
|
+
const raw = JSON.stringify([
|
|
232
|
+
userIntent,
|
|
233
|
+
toolSignature,
|
|
234
|
+
context?.failedAttempts ?? [],
|
|
235
|
+
(context?.previousGaps ?? []).map((g) => g.id).sort(),
|
|
236
|
+
]);
|
|
237
|
+
return createHash('sha256').update(raw).digest('hex');
|
|
226
238
|
}
|
|
227
239
|
|
|
228
240
|
clearCache(): void {
|
|
229
241
|
this.analysisCache.clear();
|
|
230
242
|
}
|
|
231
|
-
|
|
232
|
-
private async callLLM(
|
|
233
|
-
messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>,
|
|
234
|
-
temperature: number
|
|
235
|
-
) {
|
|
236
|
-
if (this.llm.complete) {
|
|
237
|
-
return this.llm.complete({ messages, temperature });
|
|
238
|
-
}
|
|
239
|
-
return this.llm.chat({ model: this.model, messages, temperature });
|
|
240
|
-
}
|
|
241
243
|
}
|
|
@@ -1,5 +1,78 @@
|
|
|
1
1
|
import type { GeneratedTool, GeneratedToolStore as IGeneratedToolStore } from '@cogitator-ai/types';
|
|
2
2
|
|
|
3
|
+
const STOP_WORDS = new Set([
|
|
4
|
+
'the',
|
|
5
|
+
'and',
|
|
6
|
+
'for',
|
|
7
|
+
'not',
|
|
8
|
+
'are',
|
|
9
|
+
'was',
|
|
10
|
+
'were',
|
|
11
|
+
'been',
|
|
12
|
+
'being',
|
|
13
|
+
'have',
|
|
14
|
+
'has',
|
|
15
|
+
'had',
|
|
16
|
+
'does',
|
|
17
|
+
'did',
|
|
18
|
+
'will',
|
|
19
|
+
'would',
|
|
20
|
+
'could',
|
|
21
|
+
'should',
|
|
22
|
+
'may',
|
|
23
|
+
'might',
|
|
24
|
+
'shall',
|
|
25
|
+
'can',
|
|
26
|
+
'this',
|
|
27
|
+
'that',
|
|
28
|
+
'these',
|
|
29
|
+
'those',
|
|
30
|
+
'with',
|
|
31
|
+
'from',
|
|
32
|
+
'into',
|
|
33
|
+
'through',
|
|
34
|
+
'during',
|
|
35
|
+
'before',
|
|
36
|
+
'after',
|
|
37
|
+
'above',
|
|
38
|
+
'below',
|
|
39
|
+
'between',
|
|
40
|
+
'under',
|
|
41
|
+
'over',
|
|
42
|
+
'again',
|
|
43
|
+
'further',
|
|
44
|
+
'then',
|
|
45
|
+
'once',
|
|
46
|
+
'here',
|
|
47
|
+
'there',
|
|
48
|
+
'when',
|
|
49
|
+
'where',
|
|
50
|
+
'why',
|
|
51
|
+
'how',
|
|
52
|
+
'all',
|
|
53
|
+
'each',
|
|
54
|
+
'every',
|
|
55
|
+
'both',
|
|
56
|
+
'few',
|
|
57
|
+
'more',
|
|
58
|
+
'most',
|
|
59
|
+
'other',
|
|
60
|
+
'some',
|
|
61
|
+
'such',
|
|
62
|
+
'only',
|
|
63
|
+
'own',
|
|
64
|
+
'same',
|
|
65
|
+
'than',
|
|
66
|
+
'too',
|
|
67
|
+
'very',
|
|
68
|
+
'just',
|
|
69
|
+
'because',
|
|
70
|
+
'but',
|
|
71
|
+
'nor',
|
|
72
|
+
'yet',
|
|
73
|
+
'about',
|
|
74
|
+
]);
|
|
75
|
+
|
|
3
76
|
export interface ToolUsageRecord {
|
|
4
77
|
toolId: string;
|
|
5
78
|
timestamp: Date;
|
|
@@ -25,7 +98,7 @@ export class InMemoryGeneratedToolStore implements IGeneratedToolStore {
|
|
|
25
98
|
|
|
26
99
|
async save(tool: GeneratedTool): Promise<void> {
|
|
27
100
|
const existing = this.tools.get(tool.id);
|
|
28
|
-
const version = existing ? existing.version + 1 :
|
|
101
|
+
const version = existing ? existing.version + 1 : 1;
|
|
29
102
|
this.tools.set(tool.id, { ...tool, version, updatedAt: new Date() });
|
|
30
103
|
}
|
|
31
104
|
|
|
@@ -140,17 +213,27 @@ export class InMemoryGeneratedToolStore implements IGeneratedToolStore {
|
|
|
140
213
|
}
|
|
141
214
|
|
|
142
215
|
async findSimilar(description: string, limit: number = 5): Promise<GeneratedTool[]> {
|
|
143
|
-
const descWords = new Set(
|
|
216
|
+
const descWords = new Set(
|
|
217
|
+
description
|
|
218
|
+
.toLowerCase()
|
|
219
|
+
.split(/\s+/)
|
|
220
|
+
.filter((w) => w.length >= 3 && !STOP_WORDS.has(w))
|
|
221
|
+
);
|
|
144
222
|
const scored: Array<{ tool: GeneratedTool; score: number }> = [];
|
|
145
223
|
|
|
146
224
|
for (const tool of this.tools.values()) {
|
|
147
225
|
if (tool.status === 'deprecated') continue;
|
|
148
226
|
|
|
149
|
-
const toolWords = new Set(
|
|
227
|
+
const toolWords = new Set(
|
|
228
|
+
`${tool.name} ${tool.description}`
|
|
229
|
+
.toLowerCase()
|
|
230
|
+
.split(/\s+/)
|
|
231
|
+
.filter((w) => w.length >= 3 && !STOP_WORDS.has(w))
|
|
232
|
+
);
|
|
150
233
|
|
|
151
234
|
let matchCount = 0;
|
|
152
235
|
for (const word of descWords) {
|
|
153
|
-
if (
|
|
236
|
+
if (toolWords.has(word)) {
|
|
154
237
|
matchCount++;
|
|
155
238
|
}
|
|
156
239
|
}
|
|
@@ -204,13 +287,13 @@ export class InMemoryGeneratedToolStore implements IGeneratedToolStore {
|
|
|
204
287
|
let removed = 0;
|
|
205
288
|
const now = Date.now();
|
|
206
289
|
|
|
290
|
+
const keysToRemove: string[] = [];
|
|
291
|
+
|
|
207
292
|
for (const [id, tool] of this.tools) {
|
|
208
293
|
if (options.maxAge && tool.createdAt) {
|
|
209
294
|
const age = now - tool.createdAt.getTime();
|
|
210
295
|
if (age > options.maxAge && tool.status !== 'active') {
|
|
211
|
-
|
|
212
|
-
this.usageRecords.delete(id);
|
|
213
|
-
removed++;
|
|
296
|
+
keysToRemove.push(id);
|
|
214
297
|
continue;
|
|
215
298
|
}
|
|
216
299
|
}
|
|
@@ -218,13 +301,17 @@ export class InMemoryGeneratedToolStore implements IGeneratedToolStore {
|
|
|
218
301
|
if (options.minUsage !== undefined) {
|
|
219
302
|
const usage = tool.usageCount || 0;
|
|
220
303
|
if (usage < options.minUsage && tool.status === 'deprecated') {
|
|
221
|
-
|
|
222
|
-
this.usageRecords.delete(id);
|
|
223
|
-
removed++;
|
|
304
|
+
keysToRemove.push(id);
|
|
224
305
|
}
|
|
225
306
|
}
|
|
226
307
|
}
|
|
227
308
|
|
|
309
|
+
for (const id of keysToRemove) {
|
|
310
|
+
this.tools.delete(id);
|
|
311
|
+
this.usageRecords.delete(id);
|
|
312
|
+
removed++;
|
|
313
|
+
}
|
|
314
|
+
|
|
228
315
|
if (options.maxDeprecated !== undefined) {
|
|
229
316
|
const deprecated = Array.from(this.tools.values())
|
|
230
317
|
.filter((t) => t.status === 'deprecated')
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
export { GapAnalyzer, type GapAnalyzerOptions } from './gap-analyzer';
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
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,
|
|
@@ -7,9 +7,30 @@ Rules:
|
|
|
7
7
|
- Plain JavaScript only, no TypeScript
|
|
8
8
|
- No require/import/eval/process/fetch
|
|
9
9
|
- Must have an async function called "execute"
|
|
10
|
+
- Always propagate errors with throw, never silently swallow them
|
|
10
11
|
|
|
11
12
|
Example response:
|
|
12
|
-
{"name":"add","description":"Add two numbers","implementation":"async function execute(params) {
|
|
13
|
+
{"name":"add","description":"Add two numbers","implementation":"async function execute(params) { if (typeof params.a !== 'number' || typeof params.b !== 'number') { throw new Error('Parameters a and b must be numbers'); } return params.a + params.b; }","parameters":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"]},"reasoning":"Simple addition"}`;
|
|
14
|
+
|
|
15
|
+
const TOOL_NAME_REGEX = /^[a-z_][a-z0-9_]*$/;
|
|
16
|
+
|
|
17
|
+
function sanitizeForPrompt(value: string): string {
|
|
18
|
+
return value
|
|
19
|
+
.replace(/\\/g, '\\\\')
|
|
20
|
+
.replace(/"/g, '\\"')
|
|
21
|
+
.replace(/\n/g, '\\n')
|
|
22
|
+
.replace(/\r/g, '\\r')
|
|
23
|
+
.replace(/\t/g, '\\t');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function sanitizeToolName(name: string): string {
|
|
27
|
+
const cleaned = name
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.replace(/[^a-z0-9_]/g, '_')
|
|
30
|
+
.replace(/^[0-9]/, '_$&')
|
|
31
|
+
.slice(0, 64);
|
|
32
|
+
return TOOL_NAME_REGEX.test(cleaned) ? cleaned : `generated_tool_${Date.now()}`;
|
|
33
|
+
}
|
|
13
34
|
|
|
14
35
|
export function buildGapAnalysisPrompt(
|
|
15
36
|
userIntent: string,
|
|
@@ -52,19 +73,59 @@ Respond with a JSON object:
|
|
|
52
73
|
|
|
53
74
|
export function buildToolGenerationPrompt(
|
|
54
75
|
gap: CapabilityGap,
|
|
55
|
-
|
|
56
|
-
|
|
76
|
+
existingTools: Array<{ name: string; description: string }>,
|
|
77
|
+
constraints?: {
|
|
57
78
|
maxLines?: number;
|
|
58
79
|
allowedModules?: string[];
|
|
59
80
|
securityLevel?: 'strict' | 'moderate' | 'permissive';
|
|
81
|
+
parameters?: Record<string, unknown>;
|
|
60
82
|
}
|
|
61
83
|
): string {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
84
|
+
const toolName = sanitizeToolName(gap.suggestedToolName);
|
|
85
|
+
const description = sanitizeForPrompt(gap.description);
|
|
86
|
+
const capability = sanitizeForPrompt(gap.requiredCapability);
|
|
87
|
+
|
|
88
|
+
const existingToolsSection =
|
|
89
|
+
existingTools.length > 0
|
|
90
|
+
? `\nEXISTING TOOLS (avoid duplication, consider composing with these):\n${existingTools.map((t) => `- ${t.name}: ${t.description}`).join('\n')}\n`
|
|
91
|
+
: '';
|
|
92
|
+
|
|
93
|
+
const constraintsSection: string[] = [];
|
|
94
|
+
if (constraints?.maxLines) {
|
|
95
|
+
constraintsSection.push(`- Maximum ${constraints.maxLines} lines of code`);
|
|
96
|
+
}
|
|
97
|
+
if (constraints?.securityLevel) {
|
|
98
|
+
constraintsSection.push(`- Security level: ${constraints.securityLevel}`);
|
|
99
|
+
}
|
|
100
|
+
if (constraints?.allowedModules && constraints.allowedModules.length > 0) {
|
|
101
|
+
constraintsSection.push(
|
|
102
|
+
`- Only these modules may be referenced: ${constraints.allowedModules.join(', ')}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
if (constraints?.securityLevel === 'strict') {
|
|
106
|
+
constraintsSection.push(
|
|
107
|
+
'- No eval, Function constructor, process, global, globalThis, fetch, setTimeout, setInterval',
|
|
108
|
+
'- No bracket notation access to global objects',
|
|
109
|
+
'- No prototype chain manipulation'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
65
112
|
|
|
113
|
+
const constraintsText =
|
|
114
|
+
constraintsSection.length > 0 ? `\nCONSTRAINTS:\n${constraintsSection.join('\n')}\n` : '';
|
|
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
|
+
|
|
123
|
+
return `Create a tool named "${toolName}".
|
|
124
|
+
Task: ${description}
|
|
125
|
+
Formula/Logic: ${capability}
|
|
126
|
+
${existingToolsSection}${constraintsText}${parametersText}
|
|
66
127
|
Respond with ONLY this JSON (no other text):
|
|
67
|
-
{"name":"${
|
|
128
|
+
{"name":"${toolName}","description":"${description}","implementation":"async function execute(params) { /* YOUR CODE HERE — throw on invalid input */ }","parameters":${parametersJson},"reasoning":"explanation"}`;
|
|
68
129
|
}
|
|
69
130
|
|
|
70
131
|
export function buildToolValidationPrompt(
|
|
@@ -78,7 +139,7 @@ Name: ${tool.name}
|
|
|
78
139
|
Description: ${tool.description}
|
|
79
140
|
|
|
80
141
|
IMPLEMENTATION:
|
|
81
|
-
\`\`\`
|
|
142
|
+
\`\`\`javascript
|
|
82
143
|
${tool.implementation}
|
|
83
144
|
\`\`\`
|
|
84
145
|
|
|
@@ -123,7 +184,7 @@ Name: ${tool.name}
|
|
|
123
184
|
Description: ${tool.description}
|
|
124
185
|
|
|
125
186
|
CURRENT IMPLEMENTATION:
|
|
126
|
-
\`\`\`
|
|
187
|
+
\`\`\`javascript
|
|
127
188
|
${tool.implementation}
|
|
128
189
|
\`\`\`
|
|
129
190
|
|
|
@@ -165,11 +226,16 @@ export function parseGapAnalysisResponse(response: string): {
|
|
|
165
226
|
id: String(g.id || `gap_${idx}`),
|
|
166
227
|
description: String(g.description || ''),
|
|
167
228
|
requiredCapability: String(g.requiredCapability || ''),
|
|
168
|
-
suggestedToolName:
|
|
229
|
+
suggestedToolName: sanitizeToolName(
|
|
230
|
+
String(g.suggestedToolName || `generated_tool_${idx}`)
|
|
231
|
+
),
|
|
169
232
|
complexity: (['simple', 'moderate', 'complex'].includes(String(g.complexity))
|
|
170
233
|
? g.complexity
|
|
171
234
|
: 'moderate') as 'simple' | 'moderate' | 'complex',
|
|
172
|
-
confidence:
|
|
235
|
+
confidence:
|
|
236
|
+
typeof g.confidence === 'number' && Number.isFinite(g.confidence)
|
|
237
|
+
? Math.min(1, Math.max(0, g.confidence))
|
|
238
|
+
: 0.5,
|
|
173
239
|
reasoning: String(g.reasoning || ''),
|
|
174
240
|
}))
|
|
175
241
|
: [],
|
|
@@ -194,15 +260,29 @@ export function parseToolGenerationResponse(response: string): GeneratedTool | n
|
|
|
194
260
|
} catch {}
|
|
195
261
|
}
|
|
196
262
|
|
|
197
|
-
const fnRegex = /(?:async\s+)?function\s+execute\s*\([^)]*\)\s*\{[\s\S]
|
|
263
|
+
const fnRegex = /(?:async\s+)?function\s+execute\s*\([^)]*\)\s*\{[\s\S]*\}/;
|
|
198
264
|
const fnMatch = fnRegex.exec(response);
|
|
199
265
|
if (fnMatch) {
|
|
266
|
+
let impl = fnMatch[0];
|
|
267
|
+
let braceDepth = 0;
|
|
268
|
+
let endIdx = impl.length;
|
|
269
|
+
const firstBrace = impl.indexOf('{');
|
|
270
|
+
for (let i = firstBrace; i < impl.length; i++) {
|
|
271
|
+
if (impl[i] === '{') braceDepth++;
|
|
272
|
+
if (impl[i] === '}') braceDepth--;
|
|
273
|
+
if (braceDepth === 0) {
|
|
274
|
+
endIdx = i + 1;
|
|
275
|
+
break;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
impl = impl.slice(0, endIdx);
|
|
279
|
+
|
|
200
280
|
const nameRegex = /["']?name["']?\s*[:=]\s*["']([^"']+)["']/;
|
|
201
281
|
const nameMatch = nameRegex.exec(response);
|
|
202
282
|
return buildToolFromParsed({
|
|
203
283
|
name: nameMatch?.[1] ?? 'generated_tool',
|
|
204
284
|
description: '',
|
|
205
|
-
implementation:
|
|
285
|
+
implementation: impl,
|
|
206
286
|
parameters: { type: 'object', properties: {} },
|
|
207
287
|
});
|
|
208
288
|
}
|
|
@@ -210,16 +290,38 @@ export function parseToolGenerationResponse(response: string): GeneratedTool | n
|
|
|
210
290
|
return null;
|
|
211
291
|
}
|
|
212
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
|
+
|
|
213
318
|
function buildToolFromParsed(parsed: Record<string, unknown>): GeneratedTool {
|
|
214
319
|
return {
|
|
215
320
|
id: `gen_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
216
|
-
name: String(parsed.name),
|
|
321
|
+
name: sanitizeToolName(String(parsed.name)),
|
|
217
322
|
description: String(parsed.description || ''),
|
|
218
323
|
implementation: String(parsed.implementation),
|
|
219
|
-
parameters: (parsed.parameters
|
|
220
|
-
type: 'object',
|
|
221
|
-
properties: {},
|
|
222
|
-
},
|
|
324
|
+
parameters: normalizeParameters(parsed.parameters),
|
|
223
325
|
createdAt: new Date(),
|
|
224
326
|
version: 1,
|
|
225
327
|
status: 'pending_validation',
|