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