@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.
Files changed (67) hide show
  1. package/dist/final-demo.js +0 -3
  2. package/dist/final-demo.js.map +1 -1
  3. package/dist/index.js +25 -15
  4. package/dist/index.js.map +1 -1
  5. package/dist/schemas/brain.schema.d.ts +70 -0
  6. package/dist/schemas/brain.schema.d.ts.map +1 -0
  7. package/dist/schemas/brain.schema.js +70 -0
  8. package/dist/schemas/brain.schema.js.map +1 -0
  9. package/dist/security.test.d.ts +2 -0
  10. package/dist/security.test.d.ts.map +1 -0
  11. package/dist/security.test.js +81 -0
  12. package/dist/security.test.js.map +1 -0
  13. package/dist/sse-server.js +39 -8
  14. package/dist/sse-server.js.map +1 -1
  15. package/dist/test-all.js +0 -20
  16. package/dist/test-all.js.map +1 -1
  17. package/dist/test-extended.js +0 -9
  18. package/dist/test-extended.js.map +1 -1
  19. package/dist/test-max-intelligence.d.ts +2 -0
  20. package/dist/test-max-intelligence.d.ts.map +1 -0
  21. package/dist/test-max-intelligence.js +52 -0
  22. package/dist/test-max-intelligence.js.map +1 -0
  23. package/dist/test-memory.js +0 -2
  24. package/dist/test-memory.js.map +1 -1
  25. package/dist/test-reflective.js +0 -6
  26. package/dist/test-reflective.js.map +1 -1
  27. package/dist/test-resilience.d.ts +2 -0
  28. package/dist/test-resilience.d.ts.map +1 -0
  29. package/dist/test-resilience.js +41 -0
  30. package/dist/test-resilience.js.map +1 -0
  31. package/dist/tools/orchestrator.d.ts +2 -47
  32. package/dist/tools/orchestrator.d.ts.map +1 -1
  33. package/dist/tools/orchestrator.js +224 -215
  34. package/dist/tools/orchestrator.js.map +1 -1
  35. package/dist/tools/sequential-thinking.d.ts +16 -95
  36. package/dist/tools/sequential-thinking.d.ts.map +1 -1
  37. package/dist/tools/sequential-thinking.js +313 -781
  38. package/dist/tools/sequential-thinking.js.map +1 -1
  39. package/dist/types/index.d.ts +18 -3
  40. package/dist/types/index.d.ts.map +1 -1
  41. package/dist/types/index.js +0 -3
  42. package/dist/types/index.js.map +1 -1
  43. package/dist/utils/dag.d.ts +0 -20
  44. package/dist/utils/dag.d.ts.map +1 -1
  45. package/dist/utils/dag.js +8 -48
  46. package/dist/utils/dag.js.map +1 -1
  47. package/dist/utils/memory.d.ts +5 -0
  48. package/dist/utils/memory.d.ts.map +1 -1
  49. package/dist/utils/memory.js +66 -60
  50. package/dist/utils/memory.js.map +1 -1
  51. package/dist/utils/mutex.d.ts +6 -0
  52. package/dist/utils/mutex.d.ts.map +1 -0
  53. package/dist/utils/mutex.js +22 -0
  54. package/dist/utils/mutex.js.map +1 -0
  55. package/dist/utils/resilience.d.ts +23 -0
  56. package/dist/utils/resilience.d.ts.map +1 -0
  57. package/dist/utils/resilience.js +82 -0
  58. package/dist/utils/resilience.js.map +1 -0
  59. package/dist/utils/tool-cache.d.ts +9 -0
  60. package/dist/utils/tool-cache.d.ts.map +1 -0
  61. package/dist/utils/tool-cache.js +23 -0
  62. package/dist/utils/tool-cache.js.map +1 -0
  63. package/dist/utils/vector-memory.d.ts +18 -0
  64. package/dist/utils/vector-memory.d.ts.map +1 -0
  65. package/dist/utils/vector-memory.js +86 -0
  66. package/dist/utils/vector-memory.js.map +1 -0
  67. package/package.json +22 -20
@@ -1,37 +1,45 @@
1
- import * as vm from 'vm';
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, { method, body, headers });
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
- const freshParam = getFreshnessParams('scraper');
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: 'Scraper Fallback',
367
- results,
368
- searchUrl,
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
- // Compute tool - performs calculations
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
- // Use vm for sandboxed execution
428
- if (/^[\d\s+\-*/().]+$/.test(resultExpr)) {
429
- return { result: vm.runInNewContext(resultExpr, {}) };
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
- // Get tool
636
- const tool = this.toolRegistry[task.toolName];
637
- if (!tool) {
638
- const error = `Tool "${task.toolName}" not found`;
639
- errors.push({
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: 'skipped',
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(` Failed: ${task.name} - ${errorMessage}`);
704
- errors.push({
705
- taskId: task.id,
706
- taskName: task.name,
707
- error: errorMessage,
708
- timestamp: new Date().toISOString()
709
- });
710
- // Check for onFail recovery task
711
- if (task.onFail) {
712
- console.log(` 🛡️ Task failed. Attempting recovery task: ${task.onFail}`);
713
- // In a more complex system, we would look up the recovery task and run it.
714
- // For now, we flag it in the result so the AI can decide to trigger it or we can handle it in the loop.
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
- return {
717
- taskId: task.id,
718
- taskName: task.name,
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
- // Should never reach here, but TypeScript needs it
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
- // 1. Resolve placeholders in the condition string
743
- const resolvedCondition = this.resolveValue(condition, taskOutputs);
744
- // 2. Simple evaluation for common cases
745
- // Handling literal booleans
746
- if (resolvedCondition === 'true')
747
- return true;
748
- if (resolvedCondition === 'false')
749
- return false;
750
- try {
751
- // 3. Safe evaluation using vm (sandboxed)
752
- // This prevents access to global objects like process, require, etc.
753
- return !!vm.runInNewContext(resolvedCondition, {});
754
- }
755
- catch {
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