@agentforge/core 0.16.48 → 0.16.50
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/index.cjs +138 -104
- package/dist/index.d.cts +14 -11
- package/dist/index.d.ts +14 -11
- package/dist/index.js +138 -104
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1290,14 +1290,125 @@ var ToolRegistry = class {
|
|
|
1290
1290
|
}
|
|
1291
1291
|
};
|
|
1292
1292
|
|
|
1293
|
-
// src/tools/executor.ts
|
|
1294
|
-
|
|
1293
|
+
// src/tools/executor-metrics.ts
|
|
1294
|
+
function createExecutionMetrics() {
|
|
1295
|
+
return {
|
|
1296
|
+
totalExecutions: 0,
|
|
1297
|
+
successfulExecutions: 0,
|
|
1298
|
+
failedExecutions: 0,
|
|
1299
|
+
totalDuration: 0,
|
|
1300
|
+
averageDuration: 0,
|
|
1301
|
+
byPriority: { low: 0, normal: 0, high: 0, critical: 0 }
|
|
1302
|
+
};
|
|
1303
|
+
}
|
|
1304
|
+
function recordExecutionResult(metrics, priority, duration, successful) {
|
|
1305
|
+
metrics.totalExecutions++;
|
|
1306
|
+
if (successful) {
|
|
1307
|
+
metrics.successfulExecutions++;
|
|
1308
|
+
} else {
|
|
1309
|
+
metrics.failedExecutions++;
|
|
1310
|
+
}
|
|
1311
|
+
metrics.totalDuration += duration;
|
|
1312
|
+
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1313
|
+
metrics.byPriority[priority]++;
|
|
1314
|
+
}
|
|
1315
|
+
function resetExecutionMetrics(metrics) {
|
|
1316
|
+
metrics.totalExecutions = 0;
|
|
1317
|
+
metrics.successfulExecutions = 0;
|
|
1318
|
+
metrics.failedExecutions = 0;
|
|
1319
|
+
metrics.totalDuration = 0;
|
|
1320
|
+
metrics.averageDuration = 0;
|
|
1321
|
+
metrics.byPriority = { low: 0, normal: 0, high: 0, critical: 0 };
|
|
1322
|
+
}
|
|
1323
|
+
function snapshotExecutionMetrics(metrics) {
|
|
1324
|
+
return {
|
|
1325
|
+
...metrics,
|
|
1326
|
+
byPriority: { ...metrics.byPriority }
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// src/tools/executor-retry.ts
|
|
1331
|
+
function calculateBackoff(attempt, policy) {
|
|
1332
|
+
const initialDelay = policy.initialDelay || 1e3;
|
|
1333
|
+
const maxDelay = policy.maxDelay || 3e4;
|
|
1334
|
+
let delay;
|
|
1335
|
+
switch (policy.backoff) {
|
|
1336
|
+
case "linear":
|
|
1337
|
+
delay = initialDelay * attempt;
|
|
1338
|
+
break;
|
|
1339
|
+
case "exponential":
|
|
1340
|
+
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
1341
|
+
break;
|
|
1342
|
+
case "fixed":
|
|
1343
|
+
default:
|
|
1344
|
+
delay = initialDelay;
|
|
1345
|
+
}
|
|
1346
|
+
return Math.min(delay, maxDelay);
|
|
1347
|
+
}
|
|
1348
|
+
function toError(error) {
|
|
1349
|
+
if (error instanceof Error) {
|
|
1350
|
+
return error;
|
|
1351
|
+
}
|
|
1352
|
+
return new Error(String(error));
|
|
1353
|
+
}
|
|
1354
|
+
function resolveExecutionMethod(tool, logger6) {
|
|
1355
|
+
const executeFn = tool.invoke || tool.execute;
|
|
1356
|
+
if (!executeFn) {
|
|
1357
|
+
throw new Error(
|
|
1358
|
+
"Tool must implement invoke() method. Tools created with createTool() or toolBuilder automatically have this method. If you are manually constructing a tool, ensure it has an invoke() method."
|
|
1359
|
+
);
|
|
1360
|
+
}
|
|
1361
|
+
if (!tool.invoke && tool.execute) {
|
|
1362
|
+
logger6.warn(
|
|
1363
|
+
`Tool "${tool.metadata?.name ?? tool.name ?? "unknown"}" only implements execute() which is deprecated. Please update to implement invoke() as the primary method. execute() will be removed in v1.0.0.`
|
|
1364
|
+
);
|
|
1365
|
+
}
|
|
1366
|
+
return executeFn;
|
|
1367
|
+
}
|
|
1368
|
+
async function executeWithRetry(tool, input, policy, logger6) {
|
|
1369
|
+
const executeFn = resolveExecutionMethod(tool, logger6);
|
|
1370
|
+
if (!policy) {
|
|
1371
|
+
return await executeFn.call(tool, input);
|
|
1372
|
+
}
|
|
1373
|
+
if (!Number.isInteger(policy.maxAttempts) || policy.maxAttempts < 1) {
|
|
1374
|
+
throw new Error(
|
|
1375
|
+
`Invalid retry policy: maxAttempts must be an integer >= 1 (received ${String(policy.maxAttempts)})`
|
|
1376
|
+
);
|
|
1377
|
+
}
|
|
1378
|
+
let lastError;
|
|
1379
|
+
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
1380
|
+
try {
|
|
1381
|
+
return await executeFn.call(tool, input);
|
|
1382
|
+
} catch (error) {
|
|
1383
|
+
const currentError = toError(error);
|
|
1384
|
+
lastError = currentError;
|
|
1385
|
+
if (policy.retryableErrors && policy.retryableErrors.length > 0) {
|
|
1386
|
+
const isRetryable = policy.retryableErrors.some(
|
|
1387
|
+
(message) => currentError.message.includes(message)
|
|
1388
|
+
);
|
|
1389
|
+
if (!isRetryable) {
|
|
1390
|
+
throw currentError;
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
if (attempt < policy.maxAttempts) {
|
|
1394
|
+
const delay = calculateBackoff(attempt, policy);
|
|
1395
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
throw lastError ?? new Error("Tool execution failed after retries");
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
// src/tools/executor-types.ts
|
|
1295
1403
|
var PRIORITY_ORDER = {
|
|
1296
1404
|
critical: 0,
|
|
1297
1405
|
high: 1,
|
|
1298
1406
|
normal: 2,
|
|
1299
1407
|
low: 3
|
|
1300
1408
|
};
|
|
1409
|
+
|
|
1410
|
+
// src/tools/executor.ts
|
|
1411
|
+
var logger2 = createLogger("agentforge:core:tools:executor");
|
|
1301
1412
|
function createToolExecutor(config = {}) {
|
|
1302
1413
|
const {
|
|
1303
1414
|
maxConcurrent = 5,
|
|
@@ -1310,119 +1421,47 @@ function createToolExecutor(config = {}) {
|
|
|
1310
1421
|
} = config;
|
|
1311
1422
|
let activeExecutions = 0;
|
|
1312
1423
|
const queue = [];
|
|
1313
|
-
const metrics =
|
|
1314
|
-
totalExecutions: 0,
|
|
1315
|
-
successfulExecutions: 0,
|
|
1316
|
-
failedExecutions: 0,
|
|
1317
|
-
totalDuration: 0,
|
|
1318
|
-
averageDuration: 0,
|
|
1319
|
-
byPriority: { low: 0, normal: 0, high: 0, critical: 0 }
|
|
1320
|
-
};
|
|
1321
|
-
function calculateBackoff(attempt, policy) {
|
|
1322
|
-
const initialDelay = policy.initialDelay || 1e3;
|
|
1323
|
-
const maxDelay = policy.maxDelay || 3e4;
|
|
1324
|
-
let delay;
|
|
1325
|
-
switch (policy.backoff) {
|
|
1326
|
-
case "linear":
|
|
1327
|
-
delay = initialDelay * attempt;
|
|
1328
|
-
break;
|
|
1329
|
-
case "exponential":
|
|
1330
|
-
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
1331
|
-
break;
|
|
1332
|
-
case "fixed":
|
|
1333
|
-
default:
|
|
1334
|
-
delay = initialDelay;
|
|
1335
|
-
}
|
|
1336
|
-
return Math.min(delay, maxDelay);
|
|
1337
|
-
}
|
|
1338
|
-
function toError2(error) {
|
|
1339
|
-
if (error instanceof Error) {
|
|
1340
|
-
return error;
|
|
1341
|
-
}
|
|
1342
|
-
return new Error(String(error));
|
|
1343
|
-
}
|
|
1344
|
-
async function executeWithRetry(tool, input, policy) {
|
|
1345
|
-
const executeFn = tool.invoke || tool.execute;
|
|
1346
|
-
if (!executeFn) {
|
|
1347
|
-
throw new Error(
|
|
1348
|
-
"Tool must implement invoke() method. Tools created with createTool() or toolBuilder automatically have this method. If you are manually constructing a tool, ensure it has an invoke() method."
|
|
1349
|
-
);
|
|
1350
|
-
}
|
|
1351
|
-
if (!tool.invoke && tool.execute) {
|
|
1352
|
-
logger2.warn(
|
|
1353
|
-
`Tool "${tool.metadata?.name || "unknown"}" only implements execute() which is deprecated. Please update to implement invoke() as the primary method. execute() will be removed in v1.0.0.`
|
|
1354
|
-
);
|
|
1355
|
-
}
|
|
1356
|
-
if (!policy) {
|
|
1357
|
-
return await executeFn.call(tool, input);
|
|
1358
|
-
}
|
|
1359
|
-
if (!Number.isInteger(policy.maxAttempts) || policy.maxAttempts < 1) {
|
|
1360
|
-
throw new Error(
|
|
1361
|
-
`Invalid retry policy: maxAttempts must be an integer >= 1 (received ${String(policy.maxAttempts)})`
|
|
1362
|
-
);
|
|
1363
|
-
}
|
|
1364
|
-
let lastError;
|
|
1365
|
-
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
1366
|
-
try {
|
|
1367
|
-
return await executeFn.call(tool, input);
|
|
1368
|
-
} catch (error) {
|
|
1369
|
-
const currentError = toError2(error);
|
|
1370
|
-
lastError = currentError;
|
|
1371
|
-
if (policy.retryableErrors && policy.retryableErrors.length > 0) {
|
|
1372
|
-
const isRetryable = policy.retryableErrors.some(
|
|
1373
|
-
(msg) => currentError.message.includes(msg)
|
|
1374
|
-
);
|
|
1375
|
-
if (!isRetryable) {
|
|
1376
|
-
throw currentError;
|
|
1377
|
-
}
|
|
1378
|
-
}
|
|
1379
|
-
if (attempt < policy.maxAttempts) {
|
|
1380
|
-
const delay = calculateBackoff(attempt, policy);
|
|
1381
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
throw lastError ?? new Error("Tool execution failed after retries");
|
|
1386
|
-
}
|
|
1424
|
+
const metrics = createExecutionMetrics();
|
|
1387
1425
|
async function executeSingle(tool, input, priority) {
|
|
1388
1426
|
const startTime = Date.now();
|
|
1427
|
+
let timeoutId;
|
|
1389
1428
|
try {
|
|
1390
1429
|
onExecutionStart?.(tool, input);
|
|
1391
1430
|
const result = await Promise.race([
|
|
1392
|
-
executeWithRetry(tool, input, retryPolicy),
|
|
1393
|
-
new Promise(
|
|
1394
|
-
|
|
1395
|
-
)
|
|
1431
|
+
executeWithRetry(tool, input, retryPolicy, logger2),
|
|
1432
|
+
new Promise((_, reject) => {
|
|
1433
|
+
timeoutId = setTimeout(() => reject(new Error("Tool execution timeout")), timeout2);
|
|
1434
|
+
})
|
|
1396
1435
|
]);
|
|
1397
1436
|
const duration = Date.now() - startTime;
|
|
1398
|
-
metrics
|
|
1399
|
-
metrics.successfulExecutions++;
|
|
1400
|
-
metrics.totalDuration += duration;
|
|
1401
|
-
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1402
|
-
metrics.byPriority[priority]++;
|
|
1437
|
+
recordExecutionResult(metrics, priority, duration, true);
|
|
1403
1438
|
onExecutionComplete?.(tool, input, result, duration);
|
|
1404
1439
|
return result;
|
|
1405
1440
|
} catch (error) {
|
|
1406
1441
|
const duration = Date.now() - startTime;
|
|
1407
|
-
const normalizedError =
|
|
1408
|
-
metrics
|
|
1409
|
-
metrics.failedExecutions++;
|
|
1410
|
-
metrics.totalDuration += duration;
|
|
1411
|
-
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1412
|
-
metrics.byPriority[priority]++;
|
|
1442
|
+
const normalizedError = toError(error);
|
|
1443
|
+
recordExecutionResult(metrics, priority, duration, false);
|
|
1413
1444
|
onExecutionError?.(tool, input, normalizedError, duration);
|
|
1414
1445
|
throw normalizedError;
|
|
1446
|
+
} finally {
|
|
1447
|
+
if (timeoutId) {
|
|
1448
|
+
clearTimeout(timeoutId);
|
|
1449
|
+
}
|
|
1415
1450
|
}
|
|
1416
1451
|
}
|
|
1417
|
-
|
|
1452
|
+
function processQueue() {
|
|
1418
1453
|
while (queue.length > 0 && activeExecutions < maxConcurrent) {
|
|
1419
1454
|
queue.sort((a, b) => {
|
|
1420
1455
|
const priorityDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
|
|
1421
|
-
if (priorityDiff !== 0)
|
|
1456
|
+
if (priorityDiff !== 0) {
|
|
1457
|
+
return priorityDiff;
|
|
1458
|
+
}
|
|
1422
1459
|
return a.timestamp - b.timestamp;
|
|
1423
1460
|
});
|
|
1424
1461
|
const execution = queue.shift();
|
|
1425
|
-
if (!execution)
|
|
1462
|
+
if (!execution) {
|
|
1463
|
+
break;
|
|
1464
|
+
}
|
|
1426
1465
|
activeExecutions++;
|
|
1427
1466
|
executeSingle(execution.tool, execution.input, execution.priority).then((result) => {
|
|
1428
1467
|
execution.resolve(result);
|
|
@@ -1451,20 +1490,15 @@ function createToolExecutor(config = {}) {
|
|
|
1451
1490
|
async function executeParallel(executions) {
|
|
1452
1491
|
return Promise.all(
|
|
1453
1492
|
executions.map(
|
|
1454
|
-
(
|
|
1493
|
+
(execution) => execute(execution.tool, execution.input, { priority: execution.priority })
|
|
1455
1494
|
)
|
|
1456
1495
|
);
|
|
1457
1496
|
}
|
|
1458
1497
|
function getMetrics() {
|
|
1459
|
-
return
|
|
1498
|
+
return snapshotExecutionMetrics(metrics);
|
|
1460
1499
|
}
|
|
1461
1500
|
function resetMetrics() {
|
|
1462
|
-
metrics
|
|
1463
|
-
metrics.successfulExecutions = 0;
|
|
1464
|
-
metrics.failedExecutions = 0;
|
|
1465
|
-
metrics.totalDuration = 0;
|
|
1466
|
-
metrics.averageDuration = 0;
|
|
1467
|
-
metrics.byPriority = { low: 0, normal: 0, high: 0, critical: 0 };
|
|
1501
|
+
resetExecutionMetrics(metrics);
|
|
1468
1502
|
}
|
|
1469
1503
|
function getQueueStatus() {
|
|
1470
1504
|
return {
|
|
@@ -1787,7 +1821,7 @@ function isConditionalStep(step) {
|
|
|
1787
1821
|
function calculateRetryDelay(attempt, delay, backoff) {
|
|
1788
1822
|
return backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
|
|
1789
1823
|
}
|
|
1790
|
-
function
|
|
1824
|
+
function toError2(error) {
|
|
1791
1825
|
return error instanceof Error ? error : new Error(String(error));
|
|
1792
1826
|
}
|
|
1793
1827
|
function sequential(tools) {
|
|
@@ -1860,7 +1894,7 @@ function retry(tool, options = {}) {
|
|
|
1860
1894
|
try {
|
|
1861
1895
|
return await tool.invoke(input);
|
|
1862
1896
|
} catch (error) {
|
|
1863
|
-
lastError =
|
|
1897
|
+
lastError = toError2(error);
|
|
1864
1898
|
if (attempt < maxAttempts) {
|
|
1865
1899
|
const waitTime = calculateRetryDelay(attempt, delay, backoff);
|
|
1866
1900
|
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
package/dist/index.d.cts
CHANGED
|
@@ -728,10 +728,6 @@ declare class ToolRegistry {
|
|
|
728
728
|
generatePrompt(options?: PromptOptions): string;
|
|
729
729
|
}
|
|
730
730
|
|
|
731
|
-
/**
|
|
732
|
-
* Tool Executor - Async tool execution with resource management
|
|
733
|
-
* @module tools/executor
|
|
734
|
-
*/
|
|
735
731
|
type Priority$1 = 'low' | 'normal' | 'high' | 'critical';
|
|
736
732
|
type BackoffStrategy$1 = 'linear' | 'exponential' | 'fixed';
|
|
737
733
|
interface RetryPolicy {
|
|
@@ -741,6 +737,14 @@ interface RetryPolicy {
|
|
|
741
737
|
maxDelay?: number;
|
|
742
738
|
retryableErrors?: string[];
|
|
743
739
|
}
|
|
740
|
+
interface ExecutableTool<TInput = unknown, TOutput = unknown> {
|
|
741
|
+
name?: string;
|
|
742
|
+
metadata?: {
|
|
743
|
+
name?: string;
|
|
744
|
+
};
|
|
745
|
+
invoke?: (input: TInput) => Promise<TOutput>;
|
|
746
|
+
execute?: (input: TInput) => Promise<TOutput>;
|
|
747
|
+
}
|
|
744
748
|
interface ToolExecutorConfig {
|
|
745
749
|
maxConcurrent?: number;
|
|
746
750
|
timeout?: number;
|
|
@@ -763,13 +767,12 @@ interface ExecutionMetrics {
|
|
|
763
767
|
averageDuration: number;
|
|
764
768
|
byPriority: Record<Priority$1, number>;
|
|
765
769
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Tool Executor - Async tool execution with resource management
|
|
773
|
+
* @module tools/executor
|
|
774
|
+
*/
|
|
775
|
+
|
|
773
776
|
/**
|
|
774
777
|
* Create a tool executor with resource management
|
|
775
778
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -728,10 +728,6 @@ declare class ToolRegistry {
|
|
|
728
728
|
generatePrompt(options?: PromptOptions): string;
|
|
729
729
|
}
|
|
730
730
|
|
|
731
|
-
/**
|
|
732
|
-
* Tool Executor - Async tool execution with resource management
|
|
733
|
-
* @module tools/executor
|
|
734
|
-
*/
|
|
735
731
|
type Priority$1 = 'low' | 'normal' | 'high' | 'critical';
|
|
736
732
|
type BackoffStrategy$1 = 'linear' | 'exponential' | 'fixed';
|
|
737
733
|
interface RetryPolicy {
|
|
@@ -741,6 +737,14 @@ interface RetryPolicy {
|
|
|
741
737
|
maxDelay?: number;
|
|
742
738
|
retryableErrors?: string[];
|
|
743
739
|
}
|
|
740
|
+
interface ExecutableTool<TInput = unknown, TOutput = unknown> {
|
|
741
|
+
name?: string;
|
|
742
|
+
metadata?: {
|
|
743
|
+
name?: string;
|
|
744
|
+
};
|
|
745
|
+
invoke?: (input: TInput) => Promise<TOutput>;
|
|
746
|
+
execute?: (input: TInput) => Promise<TOutput>;
|
|
747
|
+
}
|
|
744
748
|
interface ToolExecutorConfig {
|
|
745
749
|
maxConcurrent?: number;
|
|
746
750
|
timeout?: number;
|
|
@@ -763,13 +767,12 @@ interface ExecutionMetrics {
|
|
|
763
767
|
averageDuration: number;
|
|
764
768
|
byPriority: Record<Priority$1, number>;
|
|
765
769
|
}
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
}
|
|
770
|
+
|
|
771
|
+
/**
|
|
772
|
+
* Tool Executor - Async tool execution with resource management
|
|
773
|
+
* @module tools/executor
|
|
774
|
+
*/
|
|
775
|
+
|
|
773
776
|
/**
|
|
774
777
|
* Create a tool executor with resource management
|
|
775
778
|
*/
|
package/dist/index.js
CHANGED
|
@@ -1115,14 +1115,125 @@ var ToolRegistry = class {
|
|
|
1115
1115
|
}
|
|
1116
1116
|
};
|
|
1117
1117
|
|
|
1118
|
-
// src/tools/executor.ts
|
|
1119
|
-
|
|
1118
|
+
// src/tools/executor-metrics.ts
|
|
1119
|
+
function createExecutionMetrics() {
|
|
1120
|
+
return {
|
|
1121
|
+
totalExecutions: 0,
|
|
1122
|
+
successfulExecutions: 0,
|
|
1123
|
+
failedExecutions: 0,
|
|
1124
|
+
totalDuration: 0,
|
|
1125
|
+
averageDuration: 0,
|
|
1126
|
+
byPriority: { low: 0, normal: 0, high: 0, critical: 0 }
|
|
1127
|
+
};
|
|
1128
|
+
}
|
|
1129
|
+
function recordExecutionResult(metrics, priority, duration, successful) {
|
|
1130
|
+
metrics.totalExecutions++;
|
|
1131
|
+
if (successful) {
|
|
1132
|
+
metrics.successfulExecutions++;
|
|
1133
|
+
} else {
|
|
1134
|
+
metrics.failedExecutions++;
|
|
1135
|
+
}
|
|
1136
|
+
metrics.totalDuration += duration;
|
|
1137
|
+
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1138
|
+
metrics.byPriority[priority]++;
|
|
1139
|
+
}
|
|
1140
|
+
function resetExecutionMetrics(metrics) {
|
|
1141
|
+
metrics.totalExecutions = 0;
|
|
1142
|
+
metrics.successfulExecutions = 0;
|
|
1143
|
+
metrics.failedExecutions = 0;
|
|
1144
|
+
metrics.totalDuration = 0;
|
|
1145
|
+
metrics.averageDuration = 0;
|
|
1146
|
+
metrics.byPriority = { low: 0, normal: 0, high: 0, critical: 0 };
|
|
1147
|
+
}
|
|
1148
|
+
function snapshotExecutionMetrics(metrics) {
|
|
1149
|
+
return {
|
|
1150
|
+
...metrics,
|
|
1151
|
+
byPriority: { ...metrics.byPriority }
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
// src/tools/executor-retry.ts
|
|
1156
|
+
function calculateBackoff(attempt, policy) {
|
|
1157
|
+
const initialDelay = policy.initialDelay || 1e3;
|
|
1158
|
+
const maxDelay = policy.maxDelay || 3e4;
|
|
1159
|
+
let delay;
|
|
1160
|
+
switch (policy.backoff) {
|
|
1161
|
+
case "linear":
|
|
1162
|
+
delay = initialDelay * attempt;
|
|
1163
|
+
break;
|
|
1164
|
+
case "exponential":
|
|
1165
|
+
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
1166
|
+
break;
|
|
1167
|
+
case "fixed":
|
|
1168
|
+
default:
|
|
1169
|
+
delay = initialDelay;
|
|
1170
|
+
}
|
|
1171
|
+
return Math.min(delay, maxDelay);
|
|
1172
|
+
}
|
|
1173
|
+
function toError(error) {
|
|
1174
|
+
if (error instanceof Error) {
|
|
1175
|
+
return error;
|
|
1176
|
+
}
|
|
1177
|
+
return new Error(String(error));
|
|
1178
|
+
}
|
|
1179
|
+
function resolveExecutionMethod(tool, logger6) {
|
|
1180
|
+
const executeFn = tool.invoke || tool.execute;
|
|
1181
|
+
if (!executeFn) {
|
|
1182
|
+
throw new Error(
|
|
1183
|
+
"Tool must implement invoke() method. Tools created with createTool() or toolBuilder automatically have this method. If you are manually constructing a tool, ensure it has an invoke() method."
|
|
1184
|
+
);
|
|
1185
|
+
}
|
|
1186
|
+
if (!tool.invoke && tool.execute) {
|
|
1187
|
+
logger6.warn(
|
|
1188
|
+
`Tool "${tool.metadata?.name ?? tool.name ?? "unknown"}" only implements execute() which is deprecated. Please update to implement invoke() as the primary method. execute() will be removed in v1.0.0.`
|
|
1189
|
+
);
|
|
1190
|
+
}
|
|
1191
|
+
return executeFn;
|
|
1192
|
+
}
|
|
1193
|
+
async function executeWithRetry(tool, input, policy, logger6) {
|
|
1194
|
+
const executeFn = resolveExecutionMethod(tool, logger6);
|
|
1195
|
+
if (!policy) {
|
|
1196
|
+
return await executeFn.call(tool, input);
|
|
1197
|
+
}
|
|
1198
|
+
if (!Number.isInteger(policy.maxAttempts) || policy.maxAttempts < 1) {
|
|
1199
|
+
throw new Error(
|
|
1200
|
+
`Invalid retry policy: maxAttempts must be an integer >= 1 (received ${String(policy.maxAttempts)})`
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
let lastError;
|
|
1204
|
+
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
1205
|
+
try {
|
|
1206
|
+
return await executeFn.call(tool, input);
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
const currentError = toError(error);
|
|
1209
|
+
lastError = currentError;
|
|
1210
|
+
if (policy.retryableErrors && policy.retryableErrors.length > 0) {
|
|
1211
|
+
const isRetryable = policy.retryableErrors.some(
|
|
1212
|
+
(message) => currentError.message.includes(message)
|
|
1213
|
+
);
|
|
1214
|
+
if (!isRetryable) {
|
|
1215
|
+
throw currentError;
|
|
1216
|
+
}
|
|
1217
|
+
}
|
|
1218
|
+
if (attempt < policy.maxAttempts) {
|
|
1219
|
+
const delay = calculateBackoff(attempt, policy);
|
|
1220
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
throw lastError ?? new Error("Tool execution failed after retries");
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
// src/tools/executor-types.ts
|
|
1120
1228
|
var PRIORITY_ORDER = {
|
|
1121
1229
|
critical: 0,
|
|
1122
1230
|
high: 1,
|
|
1123
1231
|
normal: 2,
|
|
1124
1232
|
low: 3
|
|
1125
1233
|
};
|
|
1234
|
+
|
|
1235
|
+
// src/tools/executor.ts
|
|
1236
|
+
var logger2 = createLogger("agentforge:core:tools:executor");
|
|
1126
1237
|
function createToolExecutor(config = {}) {
|
|
1127
1238
|
const {
|
|
1128
1239
|
maxConcurrent = 5,
|
|
@@ -1135,119 +1246,47 @@ function createToolExecutor(config = {}) {
|
|
|
1135
1246
|
} = config;
|
|
1136
1247
|
let activeExecutions = 0;
|
|
1137
1248
|
const queue = [];
|
|
1138
|
-
const metrics =
|
|
1139
|
-
totalExecutions: 0,
|
|
1140
|
-
successfulExecutions: 0,
|
|
1141
|
-
failedExecutions: 0,
|
|
1142
|
-
totalDuration: 0,
|
|
1143
|
-
averageDuration: 0,
|
|
1144
|
-
byPriority: { low: 0, normal: 0, high: 0, critical: 0 }
|
|
1145
|
-
};
|
|
1146
|
-
function calculateBackoff(attempt, policy) {
|
|
1147
|
-
const initialDelay = policy.initialDelay || 1e3;
|
|
1148
|
-
const maxDelay = policy.maxDelay || 3e4;
|
|
1149
|
-
let delay;
|
|
1150
|
-
switch (policy.backoff) {
|
|
1151
|
-
case "linear":
|
|
1152
|
-
delay = initialDelay * attempt;
|
|
1153
|
-
break;
|
|
1154
|
-
case "exponential":
|
|
1155
|
-
delay = initialDelay * Math.pow(2, attempt - 1);
|
|
1156
|
-
break;
|
|
1157
|
-
case "fixed":
|
|
1158
|
-
default:
|
|
1159
|
-
delay = initialDelay;
|
|
1160
|
-
}
|
|
1161
|
-
return Math.min(delay, maxDelay);
|
|
1162
|
-
}
|
|
1163
|
-
function toError2(error) {
|
|
1164
|
-
if (error instanceof Error) {
|
|
1165
|
-
return error;
|
|
1166
|
-
}
|
|
1167
|
-
return new Error(String(error));
|
|
1168
|
-
}
|
|
1169
|
-
async function executeWithRetry(tool, input, policy) {
|
|
1170
|
-
const executeFn = tool.invoke || tool.execute;
|
|
1171
|
-
if (!executeFn) {
|
|
1172
|
-
throw new Error(
|
|
1173
|
-
"Tool must implement invoke() method. Tools created with createTool() or toolBuilder automatically have this method. If you are manually constructing a tool, ensure it has an invoke() method."
|
|
1174
|
-
);
|
|
1175
|
-
}
|
|
1176
|
-
if (!tool.invoke && tool.execute) {
|
|
1177
|
-
logger2.warn(
|
|
1178
|
-
`Tool "${tool.metadata?.name || "unknown"}" only implements execute() which is deprecated. Please update to implement invoke() as the primary method. execute() will be removed in v1.0.0.`
|
|
1179
|
-
);
|
|
1180
|
-
}
|
|
1181
|
-
if (!policy) {
|
|
1182
|
-
return await executeFn.call(tool, input);
|
|
1183
|
-
}
|
|
1184
|
-
if (!Number.isInteger(policy.maxAttempts) || policy.maxAttempts < 1) {
|
|
1185
|
-
throw new Error(
|
|
1186
|
-
`Invalid retry policy: maxAttempts must be an integer >= 1 (received ${String(policy.maxAttempts)})`
|
|
1187
|
-
);
|
|
1188
|
-
}
|
|
1189
|
-
let lastError;
|
|
1190
|
-
for (let attempt = 1; attempt <= policy.maxAttempts; attempt++) {
|
|
1191
|
-
try {
|
|
1192
|
-
return await executeFn.call(tool, input);
|
|
1193
|
-
} catch (error) {
|
|
1194
|
-
const currentError = toError2(error);
|
|
1195
|
-
lastError = currentError;
|
|
1196
|
-
if (policy.retryableErrors && policy.retryableErrors.length > 0) {
|
|
1197
|
-
const isRetryable = policy.retryableErrors.some(
|
|
1198
|
-
(msg) => currentError.message.includes(msg)
|
|
1199
|
-
);
|
|
1200
|
-
if (!isRetryable) {
|
|
1201
|
-
throw currentError;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
if (attempt < policy.maxAttempts) {
|
|
1205
|
-
const delay = calculateBackoff(attempt, policy);
|
|
1206
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1207
|
-
}
|
|
1208
|
-
}
|
|
1209
|
-
}
|
|
1210
|
-
throw lastError ?? new Error("Tool execution failed after retries");
|
|
1211
|
-
}
|
|
1249
|
+
const metrics = createExecutionMetrics();
|
|
1212
1250
|
async function executeSingle(tool, input, priority) {
|
|
1213
1251
|
const startTime = Date.now();
|
|
1252
|
+
let timeoutId;
|
|
1214
1253
|
try {
|
|
1215
1254
|
onExecutionStart?.(tool, input);
|
|
1216
1255
|
const result = await Promise.race([
|
|
1217
|
-
executeWithRetry(tool, input, retryPolicy),
|
|
1218
|
-
new Promise(
|
|
1219
|
-
|
|
1220
|
-
)
|
|
1256
|
+
executeWithRetry(tool, input, retryPolicy, logger2),
|
|
1257
|
+
new Promise((_, reject) => {
|
|
1258
|
+
timeoutId = setTimeout(() => reject(new Error("Tool execution timeout")), timeout2);
|
|
1259
|
+
})
|
|
1221
1260
|
]);
|
|
1222
1261
|
const duration = Date.now() - startTime;
|
|
1223
|
-
metrics
|
|
1224
|
-
metrics.successfulExecutions++;
|
|
1225
|
-
metrics.totalDuration += duration;
|
|
1226
|
-
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1227
|
-
metrics.byPriority[priority]++;
|
|
1262
|
+
recordExecutionResult(metrics, priority, duration, true);
|
|
1228
1263
|
onExecutionComplete?.(tool, input, result, duration);
|
|
1229
1264
|
return result;
|
|
1230
1265
|
} catch (error) {
|
|
1231
1266
|
const duration = Date.now() - startTime;
|
|
1232
|
-
const normalizedError =
|
|
1233
|
-
metrics
|
|
1234
|
-
metrics.failedExecutions++;
|
|
1235
|
-
metrics.totalDuration += duration;
|
|
1236
|
-
metrics.averageDuration = metrics.totalDuration / metrics.totalExecutions;
|
|
1237
|
-
metrics.byPriority[priority]++;
|
|
1267
|
+
const normalizedError = toError(error);
|
|
1268
|
+
recordExecutionResult(metrics, priority, duration, false);
|
|
1238
1269
|
onExecutionError?.(tool, input, normalizedError, duration);
|
|
1239
1270
|
throw normalizedError;
|
|
1271
|
+
} finally {
|
|
1272
|
+
if (timeoutId) {
|
|
1273
|
+
clearTimeout(timeoutId);
|
|
1274
|
+
}
|
|
1240
1275
|
}
|
|
1241
1276
|
}
|
|
1242
|
-
|
|
1277
|
+
function processQueue() {
|
|
1243
1278
|
while (queue.length > 0 && activeExecutions < maxConcurrent) {
|
|
1244
1279
|
queue.sort((a, b) => {
|
|
1245
1280
|
const priorityDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
|
|
1246
|
-
if (priorityDiff !== 0)
|
|
1281
|
+
if (priorityDiff !== 0) {
|
|
1282
|
+
return priorityDiff;
|
|
1283
|
+
}
|
|
1247
1284
|
return a.timestamp - b.timestamp;
|
|
1248
1285
|
});
|
|
1249
1286
|
const execution = queue.shift();
|
|
1250
|
-
if (!execution)
|
|
1287
|
+
if (!execution) {
|
|
1288
|
+
break;
|
|
1289
|
+
}
|
|
1251
1290
|
activeExecutions++;
|
|
1252
1291
|
executeSingle(execution.tool, execution.input, execution.priority).then((result) => {
|
|
1253
1292
|
execution.resolve(result);
|
|
@@ -1276,20 +1315,15 @@ function createToolExecutor(config = {}) {
|
|
|
1276
1315
|
async function executeParallel(executions) {
|
|
1277
1316
|
return Promise.all(
|
|
1278
1317
|
executions.map(
|
|
1279
|
-
(
|
|
1318
|
+
(execution) => execute(execution.tool, execution.input, { priority: execution.priority })
|
|
1280
1319
|
)
|
|
1281
1320
|
);
|
|
1282
1321
|
}
|
|
1283
1322
|
function getMetrics() {
|
|
1284
|
-
return
|
|
1323
|
+
return snapshotExecutionMetrics(metrics);
|
|
1285
1324
|
}
|
|
1286
1325
|
function resetMetrics() {
|
|
1287
|
-
metrics
|
|
1288
|
-
metrics.successfulExecutions = 0;
|
|
1289
|
-
metrics.failedExecutions = 0;
|
|
1290
|
-
metrics.totalDuration = 0;
|
|
1291
|
-
metrics.averageDuration = 0;
|
|
1292
|
-
metrics.byPriority = { low: 0, normal: 0, high: 0, critical: 0 };
|
|
1326
|
+
resetExecutionMetrics(metrics);
|
|
1293
1327
|
}
|
|
1294
1328
|
function getQueueStatus() {
|
|
1295
1329
|
return {
|
|
@@ -1612,7 +1646,7 @@ function isConditionalStep(step) {
|
|
|
1612
1646
|
function calculateRetryDelay(attempt, delay, backoff) {
|
|
1613
1647
|
return backoff === "exponential" ? delay * Math.pow(2, attempt - 1) : delay * attempt;
|
|
1614
1648
|
}
|
|
1615
|
-
function
|
|
1649
|
+
function toError2(error) {
|
|
1616
1650
|
return error instanceof Error ? error : new Error(String(error));
|
|
1617
1651
|
}
|
|
1618
1652
|
function sequential(tools) {
|
|
@@ -1685,7 +1719,7 @@ function retry(tool, options = {}) {
|
|
|
1685
1719
|
try {
|
|
1686
1720
|
return await tool.invoke(input);
|
|
1687
1721
|
} catch (error) {
|
|
1688
|
-
lastError =
|
|
1722
|
+
lastError = toError2(error);
|
|
1689
1723
|
if (attempt < maxAttempts) {
|
|
1690
1724
|
const waitTime = calculateRetryDelay(attempt, delay, backoff);
|
|
1691
1725
|
await new Promise((resolve) => setTimeout(resolve, waitTime));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentforge/core",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.50",
|
|
4
4
|
"description": "Production-ready TypeScript agent framework built on LangGraph with orchestration, middleware, and typed abstractions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|