@gotza02/mathinking 2.9.3 → 2.9.5
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/dist/final-demo.js +0 -3
- package/dist/final-demo.js.map +1 -1
- package/dist/index.js +25 -15
- package/dist/index.js.map +1 -1
- package/dist/schemas/brain.schema.d.ts +70 -0
- package/dist/schemas/brain.schema.d.ts.map +1 -0
- package/dist/schemas/brain.schema.js +70 -0
- package/dist/schemas/brain.schema.js.map +1 -0
- package/dist/security.test.d.ts +2 -0
- package/dist/security.test.d.ts.map +1 -0
- package/dist/security.test.js +81 -0
- package/dist/security.test.js.map +1 -0
- package/dist/sse-server.js +39 -8
- package/dist/sse-server.js.map +1 -1
- package/dist/test-all.js +0 -20
- package/dist/test-all.js.map +1 -1
- package/dist/test-extended.js +0 -9
- package/dist/test-extended.js.map +1 -1
- package/dist/test-max-intelligence.d.ts +2 -0
- package/dist/test-max-intelligence.d.ts.map +1 -0
- package/dist/test-max-intelligence.js +52 -0
- package/dist/test-max-intelligence.js.map +1 -0
- package/dist/test-memory.js +0 -2
- package/dist/test-memory.js.map +1 -1
- package/dist/test-reflective.js +0 -6
- package/dist/test-reflective.js.map +1 -1
- package/dist/test-resilience.d.ts +2 -0
- package/dist/test-resilience.d.ts.map +1 -0
- package/dist/test-resilience.js +41 -0
- package/dist/test-resilience.js.map +1 -0
- package/dist/tools/orchestrator.d.ts +2 -47
- package/dist/tools/orchestrator.d.ts.map +1 -1
- package/dist/tools/orchestrator.js +224 -215
- package/dist/tools/orchestrator.js.map +1 -1
- package/dist/tools/sequential-thinking.d.ts +16 -95
- package/dist/tools/sequential-thinking.d.ts.map +1 -1
- package/dist/tools/sequential-thinking.js +313 -781
- package/dist/tools/sequential-thinking.js.map +1 -1
- package/dist/types/index.d.ts +18 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +0 -3
- package/dist/types/index.js.map +1 -1
- package/dist/utils/dag.d.ts +0 -20
- package/dist/utils/dag.d.ts.map +1 -1
- package/dist/utils/dag.js +8 -48
- package/dist/utils/dag.js.map +1 -1
- package/dist/utils/memory.d.ts +5 -0
- package/dist/utils/memory.d.ts.map +1 -1
- package/dist/utils/memory.js +66 -60
- package/dist/utils/memory.js.map +1 -1
- package/dist/utils/mutex.d.ts +6 -0
- package/dist/utils/mutex.d.ts.map +1 -0
- package/dist/utils/mutex.js +22 -0
- package/dist/utils/mutex.js.map +1 -0
- package/dist/utils/resilience.d.ts +23 -0
- package/dist/utils/resilience.d.ts.map +1 -0
- package/dist/utils/resilience.js +82 -0
- package/dist/utils/resilience.js.map +1 -0
- package/dist/utils/tool-cache.d.ts +9 -0
- package/dist/utils/tool-cache.d.ts.map +1 -0
- package/dist/utils/tool-cache.js +23 -0
- package/dist/utils/tool-cache.js.map +1 -0
- package/dist/utils/vector-memory.d.ts +18 -0
- package/dist/utils/vector-memory.d.ts.map +1 -0
- package/dist/utils/vector-memory.js +86 -0
- package/dist/utils/vector-memory.js.map +1 -0
- package/package.json +22 -20
|
@@ -1,37 +1,45 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { getQuickJS } from 'quickjs-emscripten';
|
|
2
|
+
import { resilienceManager } from '../utils/resilience.js';
|
|
3
|
+
import { toolCache } from '../utils/tool-cache.js';
|
|
2
4
|
import { topologicalSortWithLayers, validateDAG, visualizeDAG, getCriticalPath, getMaxParallelism } from '../utils/dag.js';
|
|
3
|
-
/**
|
|
4
|
-
* Orchestrator Manager (The Body)
|
|
5
|
-
*
|
|
6
|
-
* Provides DAG-based task execution with:
|
|
7
|
-
* - Topological sorting for dependency resolution
|
|
8
|
-
* - Parallel execution of independent tasks
|
|
9
|
-
* - Tool orchestration via registry
|
|
10
|
-
* - Graceful error handling
|
|
11
|
-
* - Result aggregation
|
|
12
|
-
*/
|
|
13
5
|
export class OrchestratorManager {
|
|
14
6
|
toolRegistry = {};
|
|
15
7
|
executionHistory = new Map();
|
|
16
8
|
lastActionTimestamp = 0;
|
|
17
9
|
MIN_DELAY_MS = 1000; // 1s delay for orchestrator actions
|
|
10
|
+
quickJS;
|
|
18
11
|
constructor() {
|
|
19
12
|
this.registerBuiltInTools();
|
|
20
13
|
}
|
|
14
|
+
async safeEval(code) {
|
|
15
|
+
if (!this.quickJS) {
|
|
16
|
+
this.quickJS = await getQuickJS();
|
|
17
|
+
}
|
|
18
|
+
const vm = this.quickJS.newContext();
|
|
19
|
+
try {
|
|
20
|
+
const result = vm.evalCode(code);
|
|
21
|
+
if (result.error) {
|
|
22
|
+
const error = vm.dump(result.error);
|
|
23
|
+
result.error.dispose();
|
|
24
|
+
throw new Error(String(error));
|
|
25
|
+
}
|
|
26
|
+
const value = vm.dump(result.value);
|
|
27
|
+
result.value.dispose();
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
vm.dispose();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
21
34
|
sleep(ms) {
|
|
22
35
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
36
|
}
|
|
24
|
-
/**
|
|
25
|
-
* Register built-in tools for testing and common operations
|
|
26
|
-
*/
|
|
27
37
|
registerBuiltInTools() {
|
|
28
|
-
// Echo tool - returns input as output (useful for testing)
|
|
29
38
|
this.registerTool({
|
|
30
39
|
name: 'echo',
|
|
31
40
|
description: 'Returns the input as output',
|
|
32
41
|
execute: async (input) => input
|
|
33
42
|
});
|
|
34
|
-
// Delay tool - waits for specified milliseconds
|
|
35
43
|
this.registerTool({
|
|
36
44
|
name: 'delay',
|
|
37
45
|
description: 'Waits for specified milliseconds',
|
|
@@ -41,7 +49,6 @@ export class OrchestratorManager {
|
|
|
41
49
|
return { delayed: ms, timestamp: new Date().toISOString() };
|
|
42
50
|
}
|
|
43
51
|
});
|
|
44
|
-
// Transform tool - applies simple transformations
|
|
45
52
|
this.registerTool({
|
|
46
53
|
name: 'transform',
|
|
47
54
|
description: 'Applies transformations to data',
|
|
@@ -64,7 +71,6 @@ export class OrchestratorManager {
|
|
|
64
71
|
}
|
|
65
72
|
}
|
|
66
73
|
});
|
|
67
|
-
// Aggregate tool - combines multiple inputs
|
|
68
74
|
this.registerTool({
|
|
69
75
|
name: 'aggregate',
|
|
70
76
|
description: 'Aggregates multiple values',
|
|
@@ -88,7 +94,6 @@ export class OrchestratorManager {
|
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
96
|
});
|
|
91
|
-
// HTTP fetch tool - Real implementation using native fetch
|
|
92
97
|
this.registerTool({
|
|
93
98
|
name: 'fetch',
|
|
94
99
|
description: 'Fetches data from a URL',
|
|
@@ -97,8 +102,15 @@ export class OrchestratorManager {
|
|
|
97
102
|
const method = input.method || 'GET';
|
|
98
103
|
const body = input.body ? JSON.stringify(input.body) : undefined;
|
|
99
104
|
const headers = input.headers || {};
|
|
105
|
+
const controller = new AbortController();
|
|
106
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000); // 30s timeout
|
|
100
107
|
try {
|
|
101
|
-
const response = await fetch(url, {
|
|
108
|
+
const response = await fetch(url, {
|
|
109
|
+
method,
|
|
110
|
+
body,
|
|
111
|
+
headers,
|
|
112
|
+
signal: controller.signal
|
|
113
|
+
});
|
|
102
114
|
const contentType = response.headers.get('content-type');
|
|
103
115
|
let data;
|
|
104
116
|
if (contentType && contentType.includes('application/json')) {
|
|
@@ -118,9 +130,11 @@ export class OrchestratorManager {
|
|
|
118
130
|
catch (error) {
|
|
119
131
|
throw new Error(`Fetch failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
120
132
|
}
|
|
133
|
+
finally {
|
|
134
|
+
clearTimeout(timeoutId);
|
|
135
|
+
}
|
|
121
136
|
}
|
|
122
137
|
});
|
|
123
|
-
// File System: Read file
|
|
124
138
|
this.registerTool({
|
|
125
139
|
name: 'read_file',
|
|
126
140
|
description: 'Reads content from a file',
|
|
@@ -137,7 +151,6 @@ export class OrchestratorManager {
|
|
|
137
151
|
}
|
|
138
152
|
}
|
|
139
153
|
});
|
|
140
|
-
// File System: Write file
|
|
141
154
|
this.registerTool({
|
|
142
155
|
name: 'write_file',
|
|
143
156
|
description: 'Writes content to a file',
|
|
@@ -154,7 +167,6 @@ export class OrchestratorManager {
|
|
|
154
167
|
}
|
|
155
168
|
}
|
|
156
169
|
});
|
|
157
|
-
// File System: List directory
|
|
158
170
|
this.registerTool({
|
|
159
171
|
name: 'list_directory',
|
|
160
172
|
description: 'Lists files in a directory',
|
|
@@ -170,12 +182,48 @@ export class OrchestratorManager {
|
|
|
170
182
|
}
|
|
171
183
|
}
|
|
172
184
|
});
|
|
173
|
-
// Shell execute
|
|
174
185
|
this.registerTool({
|
|
175
186
|
name: 'shell_execute',
|
|
176
187
|
description: 'Executes a shell command',
|
|
177
188
|
execute: async (input) => {
|
|
178
189
|
const command = input.command;
|
|
190
|
+
const path = await import('path');
|
|
191
|
+
const ALLOWED_COMMANDS = [
|
|
192
|
+
'ls',
|
|
193
|
+
'echo',
|
|
194
|
+
'cat',
|
|
195
|
+
'mkdir',
|
|
196
|
+
'rm',
|
|
197
|
+
'touch',
|
|
198
|
+
'git',
|
|
199
|
+
'grep',
|
|
200
|
+
'pwd',
|
|
201
|
+
'find',
|
|
202
|
+
'whoami'
|
|
203
|
+
];
|
|
204
|
+
const tokenRegex = /[^\s"]+|"([^"]*)"/gi;
|
|
205
|
+
const tokens = [];
|
|
206
|
+
let match;
|
|
207
|
+
while ((match = tokenRegex.exec(command)) !== null) {
|
|
208
|
+
tokens.push(match[1] ? match[1] : match[0]); // match[1] is the content inside quotes
|
|
209
|
+
}
|
|
210
|
+
let binaryToken = tokens[0];
|
|
211
|
+
for (const token of tokens) {
|
|
212
|
+
if (!token.includes('=')) {
|
|
213
|
+
binaryToken = token;
|
|
214
|
+
break;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
const binary = path.basename(binaryToken);
|
|
218
|
+
if (!ALLOWED_COMMANDS.includes(binary)) {
|
|
219
|
+
throw new Error(`Command "${binary}" is not allowed. Allowed: ${ALLOWED_COMMANDS.join(', ')}`);
|
|
220
|
+
}
|
|
221
|
+
if (/[\n\r;&|`>]|\$\(/.test(command)) {
|
|
222
|
+
throw new Error('Command contains disallowed characters (newlines, operators, redirection) to prevent injection.');
|
|
223
|
+
}
|
|
224
|
+
if (binary === 'git' && (command.includes('-c') || command.includes('--config'))) {
|
|
225
|
+
throw new Error('Git configuration flags are not allowed for security reasons.');
|
|
226
|
+
}
|
|
179
227
|
const { exec } = await import('child_process');
|
|
180
228
|
const { promisify } = await import('util');
|
|
181
229
|
const execAsync = promisify(exec);
|
|
@@ -188,7 +236,6 @@ export class OrchestratorManager {
|
|
|
188
236
|
}
|
|
189
237
|
}
|
|
190
238
|
});
|
|
191
|
-
// Web Search tool - Enhanced with Google, Exa, and Brave API support
|
|
192
239
|
this.registerTool({
|
|
193
240
|
name: 'web_search',
|
|
194
241
|
description: 'Searches the web for information (Prioritizes Tools like Brave/Exa, then MPC/Google, then Fallback)',
|
|
@@ -198,7 +245,6 @@ export class OrchestratorManager {
|
|
|
198
245
|
const freshness = input.freshness || 'year'; // day, week, month, year
|
|
199
246
|
const getFreshnessParams = (provider) => {
|
|
200
247
|
if (provider === 'google') {
|
|
201
|
-
// d[number], w[number], m[number], y[number]
|
|
202
248
|
if (freshness === 'day')
|
|
203
249
|
return 'd1';
|
|
204
250
|
if (freshness === 'week')
|
|
@@ -208,7 +254,6 @@ export class OrchestratorManager {
|
|
|
208
254
|
return 'y1'; // Default to past year for "latest" relevance
|
|
209
255
|
}
|
|
210
256
|
if (provider === 'brave') {
|
|
211
|
-
// pd, pw, pm, py
|
|
212
257
|
if (freshness === 'day')
|
|
213
258
|
return 'pd';
|
|
214
259
|
if (freshness === 'week')
|
|
@@ -218,7 +263,6 @@ export class OrchestratorManager {
|
|
|
218
263
|
return 'py';
|
|
219
264
|
}
|
|
220
265
|
if (provider === 'scraper') {
|
|
221
|
-
// qdr:d, qdr:w, qdr:m, qdr:y
|
|
222
266
|
if (freshness === 'day')
|
|
223
267
|
return '&tbs=qdr:d';
|
|
224
268
|
if (freshness === 'week')
|
|
@@ -229,7 +273,6 @@ export class OrchestratorManager {
|
|
|
229
273
|
}
|
|
230
274
|
return '';
|
|
231
275
|
};
|
|
232
|
-
// 1. Brave Search (Tool) - Priority 1A
|
|
233
276
|
const braveApiKey = process.env.BRAVE_SEARCH_API_KEY;
|
|
234
277
|
if (braveApiKey) {
|
|
235
278
|
try {
|
|
@@ -253,10 +296,8 @@ export class OrchestratorManager {
|
|
|
253
296
|
console.error('Brave API failed, falling back:', e);
|
|
254
297
|
}
|
|
255
298
|
}
|
|
256
|
-
// 2. Exa.ai Search (Tool) - Priority 1B
|
|
257
299
|
if (process.env.EXA_API_KEY) {
|
|
258
300
|
try {
|
|
259
|
-
// Exa uses startPublishedDate for freshness
|
|
260
301
|
let startPublishedDate;
|
|
261
302
|
const now = new Date();
|
|
262
303
|
if (freshness === 'day')
|
|
@@ -298,7 +339,6 @@ export class OrchestratorManager {
|
|
|
298
339
|
console.error('Exa Search failed, falling back:', e);
|
|
299
340
|
}
|
|
300
341
|
}
|
|
301
|
-
// 3. Google Custom Search (MPC/Gemini) - Priority 2
|
|
302
342
|
if (process.env.GOOGLE_SEARCH_API_KEY && process.env.GOOGLE_SEARCH_CX) {
|
|
303
343
|
try {
|
|
304
344
|
const dateRestrict = getFreshnessParams('google');
|
|
@@ -322,50 +362,16 @@ export class OrchestratorManager {
|
|
|
322
362
|
console.error('Google Search failed, falling back:', e);
|
|
323
363
|
}
|
|
324
364
|
}
|
|
325
|
-
// 4. Fallback to basic scraper
|
|
326
365
|
try {
|
|
327
|
-
|
|
328
|
-
const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(query)}${freshParam}`;
|
|
329
|
-
const response = await fetch(searchUrl, {
|
|
330
|
-
headers: {
|
|
331
|
-
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
const html = await response.text();
|
|
335
|
-
const results = [];
|
|
336
|
-
// Enhanced patterns for Google search results
|
|
337
|
-
const patterns = [
|
|
338
|
-
// Standard snippets
|
|
339
|
-
/<div class="vvSyob">(.+?)<\/div>| <div class="BNeawe s3v9rd AP7Wnd">(.+?)<\/div>/g,
|
|
340
|
-
// Mobile snippets
|
|
341
|
-
/<div class="BNeawe vvSyob s3v9rd AP7Wnd">(.+?)<\/div>/g,
|
|
342
|
-
// Alternative layout snippets
|
|
343
|
-
/<span class="aCOpbd">(.+?)<\/span>/g
|
|
344
|
-
];
|
|
345
|
-
for (const pattern of patterns) {
|
|
346
|
-
let match;
|
|
347
|
-
while ((match = pattern.exec(html)) !== null && results.length < numResults) {
|
|
348
|
-
const text = (match[1] || match[2] || match[0]).replace(/<[^>]+>/g, '').trim();
|
|
349
|
-
if (text.length > 20 && !results.some((r) => r.snippet === text)) {
|
|
350
|
-
results.push({ snippet: text });
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
// Fallback: If no snippets found, try to extract anything that looks like a title/link description
|
|
355
|
-
if (results.length === 0) {
|
|
356
|
-
const linkDescRegex = /<h3[^>]*>(.+?)<\/h3>/g;
|
|
357
|
-
let match;
|
|
358
|
-
while ((match = linkDescRegex.exec(html)) !== null && results.length < numResults) {
|
|
359
|
-
const text = match[1].replace(/<[^>]+>/g, '').trim();
|
|
360
|
-
if (text.length > 10)
|
|
361
|
-
results.push({ snippet: text });
|
|
362
|
-
}
|
|
363
|
-
}
|
|
366
|
+
console.warn('[Web Search] Scraper fallback is deprecated/unstable. Please set GOOGLE_SEARCH_API_KEY or BRAVE_SEARCH_API_KEY.');
|
|
364
367
|
return {
|
|
365
368
|
query,
|
|
366
|
-
source: '
|
|
367
|
-
results
|
|
368
|
-
|
|
369
|
+
source: 'System',
|
|
370
|
+
results: [{
|
|
371
|
+
title: 'Configuration Required',
|
|
372
|
+
snippet: 'Web search scraping is disabled due to reliability issues. Please configure GOOGLE_SEARCH_API_KEY (and CX) or BRAVE_SEARCH_API_KEY in your environment to enable search functionality.',
|
|
373
|
+
url: 'https://console.cloud.google.com/apis/credentials'
|
|
374
|
+
}],
|
|
369
375
|
timestamp: new Date().toISOString()
|
|
370
376
|
};
|
|
371
377
|
}
|
|
@@ -374,7 +380,6 @@ export class OrchestratorManager {
|
|
|
374
380
|
}
|
|
375
381
|
}
|
|
376
382
|
});
|
|
377
|
-
// Memory Save tool
|
|
378
383
|
this.registerTool({
|
|
379
384
|
name: 'memory_save',
|
|
380
385
|
description: 'Saves a fact or information to long-term memory',
|
|
@@ -385,7 +390,6 @@ export class OrchestratorManager {
|
|
|
385
390
|
return { success: true, message: `Saved to memory: ${key}`, entry };
|
|
386
391
|
}
|
|
387
392
|
});
|
|
388
|
-
// Memory Query tool - Enhanced with filters
|
|
389
393
|
this.registerTool({
|
|
390
394
|
name: 'memory_query',
|
|
391
395
|
description: 'Queries long-term memory with optional category and tag filters',
|
|
@@ -399,7 +403,6 @@ export class OrchestratorManager {
|
|
|
399
403
|
return { query, results, count: results.length };
|
|
400
404
|
}
|
|
401
405
|
});
|
|
402
|
-
// Memory Delete tool
|
|
403
406
|
this.registerTool({
|
|
404
407
|
name: 'memory_delete',
|
|
405
408
|
description: 'Deletes an entry from long-term memory by its ID or Key',
|
|
@@ -410,49 +413,59 @@ export class OrchestratorManager {
|
|
|
410
413
|
return { success: true, ...result };
|
|
411
414
|
}
|
|
412
415
|
});
|
|
413
|
-
|
|
416
|
+
this.registerTool({
|
|
417
|
+
name: 'vector_save',
|
|
418
|
+
description: 'Saves text to semantic vector memory (The Scholar)',
|
|
419
|
+
execute: async (input) => {
|
|
420
|
+
const { content, metadata } = input;
|
|
421
|
+
const { vectorMemory } = await import('../utils/vector-memory.js');
|
|
422
|
+
const entry = await vectorMemory.add(content, metadata || {});
|
|
423
|
+
return { success: true, id: entry.id, message: 'Saved to vector memory' };
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
this.registerTool({
|
|
427
|
+
name: 'vector_search',
|
|
428
|
+
description: 'Semantically searches vector memory',
|
|
429
|
+
execute: async (input) => {
|
|
430
|
+
const { query, limit } = input;
|
|
431
|
+
const { vectorMemory } = await import('../utils/vector-memory.js');
|
|
432
|
+
const results = await vectorMemory.search(query, limit || 5);
|
|
433
|
+
return { query, results };
|
|
434
|
+
}
|
|
435
|
+
});
|
|
414
436
|
this.registerTool({
|
|
415
437
|
name: 'compute',
|
|
416
438
|
description: 'Performs mathematical computations',
|
|
417
439
|
execute: async (input) => {
|
|
418
440
|
const expression = input.expression;
|
|
419
441
|
const variables = input.variables || {};
|
|
420
|
-
// Simple expression evaluator (safe subset)
|
|
421
442
|
let resultExpr = expression;
|
|
422
443
|
for (const [key, value] of Object.entries(variables)) {
|
|
423
444
|
resultExpr = resultExpr.replace(new RegExp(`\\b${key}\\b`, 'g'), String(value));
|
|
424
445
|
}
|
|
425
|
-
// Evaluate simple math expressions
|
|
426
446
|
try {
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
447
|
+
if (/^[\d\s+\-*/().,MathPIE_a-zA-Z]+$/.test(resultExpr) &&
|
|
448
|
+
!resultExpr.includes('process') &&
|
|
449
|
+
!resultExpr.includes('require') &&
|
|
450
|
+
!resultExpr.includes('import')) {
|
|
451
|
+
const result = await this.safeEval(resultExpr);
|
|
452
|
+
return { result };
|
|
430
453
|
}
|
|
431
454
|
return { error: 'Invalid expression', expression: resultExpr };
|
|
432
455
|
}
|
|
433
|
-
catch {
|
|
434
|
-
return { error: 'Computation failed', expression: resultExpr };
|
|
456
|
+
catch (error) {
|
|
457
|
+
return { error: 'Computation failed', expression: resultExpr, details: String(error) };
|
|
435
458
|
}
|
|
436
459
|
}
|
|
437
460
|
});
|
|
438
461
|
}
|
|
439
|
-
/**
|
|
440
|
-
* Register a custom tool
|
|
441
|
-
*/
|
|
442
462
|
registerTool(tool) {
|
|
443
463
|
this.toolRegistry[tool.name] = tool;
|
|
444
464
|
}
|
|
445
|
-
/**
|
|
446
|
-
* Get list of registered tools
|
|
447
|
-
*/
|
|
448
465
|
getRegisteredTools() {
|
|
449
466
|
return Object.keys(this.toolRegistry);
|
|
450
467
|
}
|
|
451
|
-
/**
|
|
452
|
-
* Process an orchestrator action
|
|
453
|
-
*/
|
|
454
468
|
async process(input) {
|
|
455
|
-
// Rate limiting / Flood protection
|
|
456
469
|
const now = Date.now();
|
|
457
470
|
const elapsed = now - this.lastActionTimestamp;
|
|
458
471
|
if (elapsed < this.MIN_DELAY_MS) {
|
|
@@ -477,9 +490,6 @@ export class OrchestratorManager {
|
|
|
477
490
|
};
|
|
478
491
|
}
|
|
479
492
|
}
|
|
480
|
-
/**
|
|
481
|
-
* Delete an execution plan from history
|
|
482
|
-
*/
|
|
483
493
|
deletePlan(input) {
|
|
484
494
|
if (!input.planId) {
|
|
485
495
|
return {
|
|
@@ -500,9 +510,6 @@ export class OrchestratorManager {
|
|
|
500
510
|
message: `Successfully deleted execution history for plan: ${input.planId}`
|
|
501
511
|
};
|
|
502
512
|
}
|
|
503
|
-
/**
|
|
504
|
-
* Validate an execution plan
|
|
505
|
-
*/
|
|
506
513
|
validatePlan(input) {
|
|
507
514
|
if (!input.plan) {
|
|
508
515
|
return {
|
|
@@ -513,7 +520,6 @@ export class OrchestratorManager {
|
|
|
513
520
|
}
|
|
514
521
|
const validation = validateDAG(input.plan);
|
|
515
522
|
const errors = [...validation.errors];
|
|
516
|
-
// Check for unregistered tools
|
|
517
523
|
for (const task of input.plan.tasks) {
|
|
518
524
|
if (!this.toolRegistry[task.toolName]) {
|
|
519
525
|
errors.push(`Task "${task.id}" uses unregistered tool "${task.toolName}". Available tools: ${this.getRegisteredTools().join(', ')}`);
|
|
@@ -535,9 +541,6 @@ export class OrchestratorManager {
|
|
|
535
541
|
executionOrder: topoResult.layers
|
|
536
542
|
};
|
|
537
543
|
}
|
|
538
|
-
/**
|
|
539
|
-
* Execute a plan with parallel processing
|
|
540
|
-
*/
|
|
541
544
|
async executePlan(input) {
|
|
542
545
|
if (!input.plan) {
|
|
543
546
|
return {
|
|
@@ -545,14 +548,12 @@ export class OrchestratorManager {
|
|
|
545
548
|
message: 'Execution plan is required'
|
|
546
549
|
};
|
|
547
550
|
}
|
|
548
|
-
// Validate first
|
|
549
551
|
const validation = this.validatePlan(input);
|
|
550
552
|
if (!validation.success) {
|
|
551
553
|
return validation;
|
|
552
554
|
}
|
|
553
555
|
const plan = input.plan;
|
|
554
556
|
const startTime = new Date();
|
|
555
|
-
// Get execution layers
|
|
556
557
|
const topoResult = topologicalSortWithLayers(plan);
|
|
557
558
|
const taskMap = new Map(plan.tasks.map((t) => [t.id, t]));
|
|
558
559
|
const taskResults = [];
|
|
@@ -562,19 +563,44 @@ export class OrchestratorManager {
|
|
|
562
563
|
console.log(`📊 Total tasks: ${plan.tasks.length}`);
|
|
563
564
|
console.log(`📦 Execution layers: ${topoResult.layers.length}`);
|
|
564
565
|
console.log(`⚡ Max parallelism: ${Math.max(...topoResult.layers.map((l) => l.length))}\n`);
|
|
565
|
-
// Execute layer by layer
|
|
566
566
|
for (let layerIndex = 0; layerIndex < topoResult.layers.length; layerIndex++) {
|
|
567
567
|
const layer = topoResult.layers[layerIndex];
|
|
568
568
|
console.log(`\n📍 Layer ${layerIndex + 1}: Executing ${layer.length} task(s) in parallel`);
|
|
569
569
|
console.log(` Tasks: [${layer.join(', ')}]`);
|
|
570
|
-
// Execute all tasks in this layer in parallel
|
|
571
570
|
const layerPromises = layer.map(async (taskId) => {
|
|
572
571
|
const task = taskMap.get(taskId);
|
|
572
|
+
const dependencies = task.dependencies.map(d => taskResults.find(r => r.taskId === d));
|
|
573
|
+
const failedDep = dependencies.find(d => d?.status === 'failed');
|
|
574
|
+
const skippedDep = dependencies.find(d => d?.status === 'skipped');
|
|
575
|
+
if (failedDep) {
|
|
576
|
+
console.log(` ○ Skipped: ${task.name} (Dependency "${failedDep.taskName}" failed)`);
|
|
577
|
+
return {
|
|
578
|
+
taskId: task.id,
|
|
579
|
+
taskName: task.name,
|
|
580
|
+
status: 'skipped',
|
|
581
|
+
error: `Dependency ${failedDep.taskName} failed`,
|
|
582
|
+
startTime: new Date().toISOString(),
|
|
583
|
+
endTime: new Date().toISOString(),
|
|
584
|
+
duration: 0,
|
|
585
|
+
retries: 0
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
if (skippedDep) {
|
|
589
|
+
console.log(` ○ Skipped: ${task.name} (Dependency "${skippedDep.taskName}" skipped)`);
|
|
590
|
+
return {
|
|
591
|
+
taskId: task.id,
|
|
592
|
+
taskName: task.name,
|
|
593
|
+
status: 'skipped',
|
|
594
|
+
error: `Dependency ${skippedDep.taskName} skipped`,
|
|
595
|
+
startTime: new Date().toISOString(),
|
|
596
|
+
endTime: new Date().toISOString(),
|
|
597
|
+
duration: 0,
|
|
598
|
+
retries: 0
|
|
599
|
+
};
|
|
600
|
+
}
|
|
573
601
|
return this.executeTask(task, taskOutputs, errors);
|
|
574
602
|
});
|
|
575
|
-
// Wait for all tasks in this layer to complete
|
|
576
603
|
const layerResults = await Promise.all(layerPromises);
|
|
577
|
-
// Store results
|
|
578
604
|
for (const result of layerResults) {
|
|
579
605
|
taskResults.push(result);
|
|
580
606
|
if (result.status === 'completed' && result.result !== undefined) {
|
|
@@ -584,7 +610,6 @@ export class OrchestratorManager {
|
|
|
584
610
|
}
|
|
585
611
|
const endTime = new Date();
|
|
586
612
|
const totalDuration = endTime.getTime() - startTime.getTime();
|
|
587
|
-
// Aggregate results
|
|
588
613
|
const aggregatedResults = {};
|
|
589
614
|
for (const [taskId, output] of taskOutputs) {
|
|
590
615
|
aggregatedResults[taskId] = output;
|
|
@@ -623,47 +648,24 @@ export class OrchestratorManager {
|
|
|
623
648
|
executionOrder: topoResult.layers
|
|
624
649
|
};
|
|
625
650
|
}
|
|
626
|
-
/**
|
|
627
|
-
* Execute a single task with error handling and retries
|
|
628
|
-
*/
|
|
629
651
|
async executeTask(task, taskOutputs, errors) {
|
|
630
652
|
const startTime = new Date();
|
|
631
653
|
const maxRetries = task.retryCount || 0;
|
|
632
654
|
const retryDelay = task.retryDelay || 1000;
|
|
633
655
|
let retries = 0;
|
|
656
|
+
let repairAttempts = 0; // Guard against infinite repair loops
|
|
657
|
+
let currentInput = task.toolInput; // Allow modification
|
|
634
658
|
console.log(` ⚙️ Starting: ${task.name} (${task.id})`);
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
taskId: task.id,
|
|
641
|
-
taskName: task.name,
|
|
642
|
-
error,
|
|
643
|
-
timestamp: new Date().toISOString()
|
|
644
|
-
});
|
|
645
|
-
return {
|
|
646
|
-
taskId: task.id,
|
|
647
|
-
taskName: task.name,
|
|
648
|
-
status: 'failed',
|
|
649
|
-
error,
|
|
650
|
-
startTime: startTime.toISOString(),
|
|
651
|
-
endTime: new Date().toISOString(),
|
|
652
|
-
duration: 0,
|
|
653
|
-
retries: 0
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
// Resolve input with dependency outputs
|
|
657
|
-
const resolvedInput = this.resolveInput(task.toolInput, taskOutputs);
|
|
658
|
-
// Evaluate condition if present
|
|
659
|
-
if (task.runIf) {
|
|
660
|
-
const isConditionMet = this.evaluateCondition(task.runIf, taskOutputs);
|
|
661
|
-
if (!isConditionMet) {
|
|
662
|
-
console.log(` ○ Skipped: ${task.name} (Condition "${task.runIf}" not met)`);
|
|
659
|
+
let resolvedInput = this.resolveInput(currentInput, taskOutputs);
|
|
660
|
+
if (['fetch', 'web_search'].includes(task.toolName)) {
|
|
661
|
+
const cached = toolCache.get(task.toolName, resolvedInput);
|
|
662
|
+
if (cached) {
|
|
663
|
+
console.log(` ⚡ Cached Hit: ${task.name}`);
|
|
663
664
|
return {
|
|
664
665
|
taskId: task.id,
|
|
665
666
|
taskName: task.name,
|
|
666
|
-
status: '
|
|
667
|
+
status: 'completed',
|
|
668
|
+
result: cached,
|
|
667
669
|
startTime: startTime.toISOString(),
|
|
668
670
|
endTime: new Date().toISOString(),
|
|
669
671
|
duration: 0,
|
|
@@ -671,14 +673,29 @@ export class OrchestratorManager {
|
|
|
671
673
|
};
|
|
672
674
|
}
|
|
673
675
|
}
|
|
676
|
+
const tool = this.toolRegistry[task.toolName];
|
|
677
|
+
if (!tool) {
|
|
678
|
+
const error = `Tool "${task.toolName}" not found`;
|
|
679
|
+
errors.push({ taskId: task.id, taskName: task.name, error, timestamp: new Date().toISOString() });
|
|
680
|
+
return { taskId: task.id, taskName: task.name, status: 'failed', error, startTime: startTime.toISOString(), endTime: new Date().toISOString(), duration: 0, retries: 0 };
|
|
681
|
+
}
|
|
682
|
+
if (task.runIf) {
|
|
683
|
+
const isConditionMet = await this.evaluateCondition(task.runIf, taskOutputs);
|
|
684
|
+
if (!isConditionMet) {
|
|
685
|
+
console.log(` ○ Skipped: ${task.name} (Condition "${task.runIf}" not met)`);
|
|
686
|
+
return { taskId: task.id, taskName: task.name, status: 'skipped', startTime: startTime.toISOString(), endTime: new Date().toISOString(), duration: 0, retries: 0 };
|
|
687
|
+
}
|
|
688
|
+
}
|
|
674
689
|
while (retries <= maxRetries) {
|
|
675
690
|
try {
|
|
676
|
-
// Execute with timeout if specified
|
|
677
691
|
const result = task.timeout
|
|
678
692
|
? await this.executeWithTimeout(tool.execute(resolvedInput), task.timeout)
|
|
679
693
|
: await tool.execute(resolvedInput);
|
|
680
694
|
const endTime = new Date();
|
|
681
695
|
console.log(` ✓ Completed: ${task.name} (${endTime.getTime() - startTime.getTime()}ms)`);
|
|
696
|
+
if (['fetch', 'web_search'].includes(task.toolName)) {
|
|
697
|
+
toolCache.set(task.toolName, currentInput, result);
|
|
698
|
+
}
|
|
682
699
|
return {
|
|
683
700
|
taskId: task.id,
|
|
684
701
|
taskName: task.name,
|
|
@@ -691,75 +708,79 @@ export class OrchestratorManager {
|
|
|
691
708
|
};
|
|
692
709
|
}
|
|
693
710
|
catch (err) {
|
|
694
|
-
retries++;
|
|
695
711
|
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
712
|
+
if (repairAttempts < 3) {
|
|
713
|
+
const recovery = await resilienceManager.diagnose(err, { ...task, toolInput: currentInput });
|
|
714
|
+
if (recovery) {
|
|
715
|
+
console.log(` 🚑 Self-Healing (${recovery.type}): ${recovery.description}`);
|
|
716
|
+
repairAttempts++; // Increment guard
|
|
717
|
+
if (recovery.type === 'run_command' && recovery.payload?.command) {
|
|
718
|
+
try {
|
|
719
|
+
await this.toolRegistry['shell_execute'].execute({ command: recovery.payload.command });
|
|
720
|
+
// continue; // Let it fall through to retry logic
|
|
721
|
+
}
|
|
722
|
+
catch (fixErr) {
|
|
723
|
+
console.log(` ❌ Fix failed: ${fixErr}`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
else if (recovery.type === 'modify_input' && recovery.payload) {
|
|
727
|
+
currentInput = { ...currentInput, ...recovery.payload };
|
|
728
|
+
resolvedInput = this.resolveInput(currentInput, taskOutputs);
|
|
729
|
+
// continue; // Let it fall through to retry logic
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
retries++;
|
|
696
734
|
if (retries <= maxRetries) {
|
|
697
|
-
// Exponential backoff with jitter and cap
|
|
698
735
|
const backoff = Math.min(retryDelay * Math.pow(2, retries - 1), 30000);
|
|
699
736
|
console.log(` ⟳ Retry ${retries}/${maxRetries}: ${task.name} (Waiting ${backoff}ms)`);
|
|
700
737
|
await new Promise((resolve) => setTimeout(resolve, backoff));
|
|
701
738
|
}
|
|
702
739
|
else {
|
|
703
|
-
console.log(`
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
740
|
+
console.log(` 🚨 All retries failed for: ${task.name}. Requesting mid-execution re-plan...`);
|
|
741
|
+
const replanResult = await resilienceManager.requestStrategicReplan(errorMessage, task);
|
|
742
|
+
if (replanResult?.success && replanResult.executionPlan) {
|
|
743
|
+
console.log(` ✨ MIRACLE! Brain provided a recovery plan: ${replanResult.executionPlan.name}`);
|
|
744
|
+
const subResult = await this.executePlan({ action: 'execute_plan', plan: replanResult.executionPlan });
|
|
745
|
+
if (subResult.success) {
|
|
746
|
+
return {
|
|
747
|
+
taskId: task.id,
|
|
748
|
+
taskName: task.name,
|
|
749
|
+
status: 'completed',
|
|
750
|
+
result: subResult.result?.aggregatedResults,
|
|
751
|
+
startTime: startTime.toISOString(),
|
|
752
|
+
endTime: new Date().toISOString(),
|
|
753
|
+
duration: new Date().getTime() - startTime.getTime(),
|
|
754
|
+
retries
|
|
755
|
+
};
|
|
756
|
+
}
|
|
715
757
|
}
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
status: 'failed',
|
|
720
|
-
error: errorMessage,
|
|
721
|
-
startTime: startTime.toISOString(),
|
|
722
|
-
endTime: new Date().toISOString(),
|
|
723
|
-
duration: new Date().getTime() - startTime.getTime(),
|
|
724
|
-
retries: retries - 1
|
|
725
|
-
};
|
|
758
|
+
console.log(` ✗ Failed: ${task.name} - ${errorMessage}`);
|
|
759
|
+
errors.push({ taskId: task.id, taskName: task.name, error: errorMessage, timestamp: new Date().toISOString() });
|
|
760
|
+
return { taskId: task.id, taskName: task.name, status: 'failed', error: errorMessage, startTime: startTime.toISOString(), endTime: new Date().toISOString(), duration: new Date().getTime() - startTime.getTime(), retries: retries - 1 };
|
|
726
761
|
}
|
|
727
762
|
}
|
|
728
763
|
}
|
|
729
|
-
|
|
730
|
-
return {
|
|
731
|
-
taskId: task.id,
|
|
732
|
-
taskName: task.name,
|
|
733
|
-
status: 'failed',
|
|
734
|
-
error: 'Unknown error',
|
|
735
|
-
startTime: startTime.toISOString(),
|
|
736
|
-
endTime: new Date().toISOString(),
|
|
737
|
-
duration: 0,
|
|
738
|
-
retries
|
|
739
|
-
};
|
|
764
|
+
return { taskId: task.id, taskName: task.name, status: 'failed', error: 'Unknown', startTime: startTime.toISOString(), endTime: new Date().toISOString(), duration: 0, retries };
|
|
740
765
|
}
|
|
741
|
-
evaluateCondition(condition, taskOutputs) {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
return false;
|
|
766
|
+
async evaluateCondition(condition, taskOutputs) {
|
|
767
|
+
const resolvedCondition = this.resolveValue(condition, taskOutputs, true);
|
|
768
|
+
if (typeof resolvedCondition === 'boolean')
|
|
769
|
+
return resolvedCondition;
|
|
770
|
+
if (typeof resolvedCondition === 'string') {
|
|
771
|
+
if (resolvedCondition === 'true')
|
|
772
|
+
return true;
|
|
773
|
+
if (resolvedCondition === 'false')
|
|
774
|
+
return false;
|
|
775
|
+
try {
|
|
776
|
+
return !!(await this.safeEval(resolvedCondition));
|
|
777
|
+
}
|
|
778
|
+
catch {
|
|
779
|
+
return false;
|
|
780
|
+
}
|
|
757
781
|
}
|
|
782
|
+
return !!resolvedCondition;
|
|
758
783
|
}
|
|
759
|
-
/**
|
|
760
|
-
* Resolve input placeholders with outputs from previous tasks
|
|
761
|
-
* Supports syntax: "${taskId.property}" or "${taskId}"
|
|
762
|
-
*/
|
|
763
784
|
resolveInput(input, taskOutputs) {
|
|
764
785
|
const resolved = {};
|
|
765
786
|
for (const [key, value] of Object.entries(input)) {
|
|
@@ -767,18 +788,18 @@ export class OrchestratorManager {
|
|
|
767
788
|
}
|
|
768
789
|
return resolved;
|
|
769
790
|
}
|
|
770
|
-
resolveValue(value, taskOutputs) {
|
|
791
|
+
resolveValue(value, taskOutputs, safeForEval = false) {
|
|
771
792
|
if (typeof value === 'string') {
|
|
772
|
-
// Check for placeholder pattern: ${taskId} or ${taskId.property}
|
|
773
793
|
const placeholderRegex = /\$\{([^}]+)\}/g;
|
|
774
|
-
// If entire string is a single placeholder, return the actual value (not stringified)
|
|
775
794
|
const singleMatch = value.match(/^\$\{([^}]+)\}$/);
|
|
776
795
|
if (singleMatch) {
|
|
777
796
|
return this.resolvePlaceholder(singleMatch[1], taskOutputs);
|
|
778
797
|
}
|
|
779
|
-
// Otherwise, replace placeholders in string
|
|
780
798
|
return value.replace(placeholderRegex, (_, placeholder) => {
|
|
781
799
|
const resolved = this.resolvePlaceholder(placeholder, taskOutputs);
|
|
800
|
+
if (safeForEval && typeof resolved === 'string') {
|
|
801
|
+
return resolved.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
802
|
+
}
|
|
782
803
|
return String(resolved);
|
|
783
804
|
});
|
|
784
805
|
}
|
|
@@ -800,10 +821,8 @@ export class OrchestratorManager {
|
|
|
800
821
|
if (parts.length === 1) {
|
|
801
822
|
return output;
|
|
802
823
|
}
|
|
803
|
-
// Navigate nested properties
|
|
804
824
|
let current = output;
|
|
805
825
|
for (let i = 1; i < parts.length; i++) {
|
|
806
|
-
// Allow traversal of objects and strings (for .length etc)
|
|
807
826
|
if (current && (typeof current === 'object' || typeof current === 'string')) {
|
|
808
827
|
current = current[parts[i]];
|
|
809
828
|
}
|
|
@@ -813,18 +832,12 @@ export class OrchestratorManager {
|
|
|
813
832
|
}
|
|
814
833
|
return current;
|
|
815
834
|
}
|
|
816
|
-
/**
|
|
817
|
-
* Execute a promise with timeout
|
|
818
|
-
*/
|
|
819
835
|
async executeWithTimeout(promise, timeoutMs) {
|
|
820
836
|
return Promise.race([
|
|
821
837
|
promise,
|
|
822
838
|
new Promise((_, reject) => setTimeout(() => reject(new Error(`Task timed out after ${timeoutMs}ms`)), timeoutMs))
|
|
823
839
|
]);
|
|
824
840
|
}
|
|
825
|
-
/**
|
|
826
|
-
* Get execution status for a plan
|
|
827
|
-
*/
|
|
828
841
|
getExecutionStatus(input) {
|
|
829
842
|
if (!input.planId) {
|
|
830
843
|
return {
|
|
@@ -845,9 +858,6 @@ export class OrchestratorManager {
|
|
|
845
858
|
result
|
|
846
859
|
};
|
|
847
860
|
}
|
|
848
|
-
/**
|
|
849
|
-
* Format execution summary for display
|
|
850
|
-
*/
|
|
851
861
|
formatExecutionSummary(result) {
|
|
852
862
|
const lines = [
|
|
853
863
|
'┌─────────────────────────────────────────────────┐',
|
|
@@ -872,6 +882,5 @@ export class OrchestratorManager {
|
|
|
872
882
|
return lines.join('\n');
|
|
873
883
|
}
|
|
874
884
|
}
|
|
875
|
-
// Export singleton instance
|
|
876
885
|
export const orchestratorManager = new OrchestratorManager();
|
|
877
886
|
//# sourceMappingURL=orchestrator.js.map
|