@agentforge/patterns 0.16.0 → 0.16.2
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 +425 -287
- package/dist/index.d.cts +103 -109
- package/dist/index.d.ts +103 -109
- package/dist/index.js +425 -287
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -888,7 +888,7 @@ var PlanStepSchema = import_zod4.z.object({
|
|
|
888
888
|
/**
|
|
889
889
|
* Optional arguments for the tool
|
|
890
890
|
*/
|
|
891
|
-
args: import_zod4.z.record(import_zod4.z.
|
|
891
|
+
args: import_zod4.z.record(import_zod4.z.string(), import_zod4.z.unknown()).optional().describe("Arguments to pass to the tool")
|
|
892
892
|
});
|
|
893
893
|
var CompletedStepSchema = import_zod4.z.object({
|
|
894
894
|
/**
|
|
@@ -898,7 +898,7 @@ var CompletedStepSchema = import_zod4.z.object({
|
|
|
898
898
|
/**
|
|
899
899
|
* The result of executing the step
|
|
900
900
|
*/
|
|
901
|
-
result: import_zod4.z.
|
|
901
|
+
result: import_zod4.z.unknown().describe("The result of executing the step"),
|
|
902
902
|
/**
|
|
903
903
|
* Whether the step succeeded
|
|
904
904
|
*/
|
|
@@ -1075,6 +1075,9 @@ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
|
|
|
1075
1075
|
var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planner");
|
|
1076
1076
|
var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
|
|
1077
1077
|
var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
|
|
1078
|
+
function invokePlanExecuteTool(tool, args) {
|
|
1079
|
+
return tool.invoke.call(tool, args);
|
|
1080
|
+
}
|
|
1078
1081
|
function createPlannerNode(config) {
|
|
1079
1082
|
const {
|
|
1080
1083
|
model,
|
|
@@ -1142,10 +1145,18 @@ function createExecutorNode(config) {
|
|
|
1142
1145
|
const {
|
|
1143
1146
|
tools,
|
|
1144
1147
|
model,
|
|
1145
|
-
parallel
|
|
1148
|
+
parallel,
|
|
1146
1149
|
stepTimeout = 3e4,
|
|
1147
1150
|
enableDeduplication = true
|
|
1148
1151
|
} = config;
|
|
1152
|
+
if (typeof model !== "undefined") {
|
|
1153
|
+
executorLogger.warn("ExecutorConfig.model is currently unsupported and will be ignored");
|
|
1154
|
+
}
|
|
1155
|
+
if (typeof parallel !== "undefined") {
|
|
1156
|
+
executorLogger.warn("ExecutorConfig.parallel is currently unsupported and will be ignored", {
|
|
1157
|
+
parallel
|
|
1158
|
+
});
|
|
1159
|
+
}
|
|
1149
1160
|
return async (state) => {
|
|
1150
1161
|
const { plan, currentStepIndex = 0, pastSteps = [], iteration = 0 } = state;
|
|
1151
1162
|
try {
|
|
@@ -1222,13 +1233,20 @@ function createExecutorNode(config) {
|
|
|
1222
1233
|
throw new Error(`Tool not found: ${currentStep.tool}`);
|
|
1223
1234
|
}
|
|
1224
1235
|
const startTime = Date.now();
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1236
|
+
let timeoutId;
|
|
1237
|
+
try {
|
|
1238
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1239
|
+
timeoutId = setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout);
|
|
1240
|
+
});
|
|
1241
|
+
result = await Promise.race([
|
|
1242
|
+
invokePlanExecuteTool(tool, currentStep.args || {}),
|
|
1243
|
+
timeoutPromise
|
|
1244
|
+
]);
|
|
1245
|
+
} finally {
|
|
1246
|
+
if (timeoutId !== void 0) {
|
|
1247
|
+
clearTimeout(timeoutId);
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1232
1250
|
const executionTime = Date.now() - startTime;
|
|
1233
1251
|
executorLogger.debug("Step executed successfully", {
|
|
1234
1252
|
stepId: currentStep.id,
|
|
@@ -1285,9 +1303,14 @@ function createExecutorNode(config) {
|
|
|
1285
1303
|
function createReplannerNode(config) {
|
|
1286
1304
|
const {
|
|
1287
1305
|
model,
|
|
1288
|
-
replanThreshold
|
|
1306
|
+
replanThreshold,
|
|
1289
1307
|
systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
|
|
1290
1308
|
} = config;
|
|
1309
|
+
if (typeof replanThreshold !== "undefined") {
|
|
1310
|
+
replannerLogger.warn("ReplannerConfig.replanThreshold is currently unsupported and will be ignored", {
|
|
1311
|
+
replanThreshold
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1291
1314
|
return async (state) => {
|
|
1292
1315
|
const startTime = Date.now();
|
|
1293
1316
|
try {
|
|
@@ -2656,16 +2679,82 @@ function wrapReActAgent(workerId, agent, verbose = false) {
|
|
|
2656
2679
|
};
|
|
2657
2680
|
}
|
|
2658
2681
|
|
|
2659
|
-
// src/multi-agent/nodes.ts
|
|
2682
|
+
// src/multi-agent/nodes/shared.ts
|
|
2660
2683
|
var import_messages5 = require("@langchain/core/messages");
|
|
2661
2684
|
var import_core8 = require("@agentforge/core");
|
|
2662
2685
|
var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
|
|
2686
|
+
function createGeneratedId(prefix) {
|
|
2687
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
2688
|
+
}
|
|
2689
|
+
function createTaskAssignmentId() {
|
|
2690
|
+
return `task_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
|
|
2691
|
+
}
|
|
2692
|
+
function getLatestTaskContent(state) {
|
|
2693
|
+
return state.messages[state.messages.length - 1]?.content || state.input;
|
|
2694
|
+
}
|
|
2695
|
+
function createTaskAssignments(workerIds, task) {
|
|
2696
|
+
return workerIds.map((workerId) => ({
|
|
2697
|
+
id: createTaskAssignmentId(),
|
|
2698
|
+
workerId,
|
|
2699
|
+
task,
|
|
2700
|
+
priority: 5,
|
|
2701
|
+
assignedAt: Date.now()
|
|
2702
|
+
}));
|
|
2703
|
+
}
|
|
2704
|
+
function createAssignmentMessages(assignments) {
|
|
2705
|
+
return assignments.map((assignment) => ({
|
|
2706
|
+
id: createGeneratedId("msg"),
|
|
2707
|
+
from: "supervisor",
|
|
2708
|
+
to: [assignment.workerId],
|
|
2709
|
+
type: "task_assignment",
|
|
2710
|
+
content: assignment.task,
|
|
2711
|
+
timestamp: Date.now(),
|
|
2712
|
+
metadata: {
|
|
2713
|
+
assignmentId: assignment.id,
|
|
2714
|
+
priority: assignment.priority
|
|
2715
|
+
}
|
|
2716
|
+
}));
|
|
2717
|
+
}
|
|
2718
|
+
function findCurrentAssignment(state, workerId) {
|
|
2719
|
+
return state.activeAssignments.find(
|
|
2720
|
+
(assignment) => assignment.workerId === workerId && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2663
2723
|
function convertWorkerToolsForLangChain(tools) {
|
|
2664
2724
|
const safeTools = tools ?? [];
|
|
2665
|
-
return (0, import_core8.toLangChainTools)(
|
|
2666
|
-
safeTools
|
|
2667
|
-
);
|
|
2725
|
+
return (0, import_core8.toLangChainTools)(safeTools);
|
|
2668
2726
|
}
|
|
2727
|
+
function serializeModelContent(content) {
|
|
2728
|
+
if (typeof content === "string") {
|
|
2729
|
+
return content;
|
|
2730
|
+
}
|
|
2731
|
+
try {
|
|
2732
|
+
const serialized = JSON.stringify(content);
|
|
2733
|
+
if (typeof serialized !== "string") {
|
|
2734
|
+
const error = new Error(
|
|
2735
|
+
"Failed to serialize model content: JSON.stringify returned undefined"
|
|
2736
|
+
);
|
|
2737
|
+
logger2.error("Model content serialization failed", {
|
|
2738
|
+
errorMessage: error.message,
|
|
2739
|
+
contentType: content === null ? "null" : typeof content
|
|
2740
|
+
});
|
|
2741
|
+
throw error;
|
|
2742
|
+
}
|
|
2743
|
+
return serialized;
|
|
2744
|
+
} catch (error) {
|
|
2745
|
+
const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
|
|
2746
|
+
logger2.error("Model content serialization threw an error", {
|
|
2747
|
+
errorMessage: normalizedError.message,
|
|
2748
|
+
contentType: content === null ? "null" : typeof content
|
|
2749
|
+
});
|
|
2750
|
+
throw normalizedError;
|
|
2751
|
+
}
|
|
2752
|
+
}
|
|
2753
|
+
function createPromptMessages(systemPrompt, task) {
|
|
2754
|
+
return [new import_messages5.SystemMessage(systemPrompt), new import_messages5.HumanMessage(task)];
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2757
|
+
// src/multi-agent/nodes/aggregator.ts
|
|
2669
2758
|
var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
|
|
2670
2759
|
|
|
2671
2760
|
Your job is to:
|
|
@@ -2675,12 +2764,124 @@ Your job is to:
|
|
|
2675
2764
|
4. Provide a clear, comprehensive final answer
|
|
2676
2765
|
|
|
2677
2766
|
Be concise but thorough in your aggregation.`;
|
|
2678
|
-
function
|
|
2767
|
+
function createAggregationPrompt(state) {
|
|
2768
|
+
const taskResults = state.completedTasks.map((task, idx) => {
|
|
2769
|
+
const status = task.success ? "\u2713" : "\u2717";
|
|
2770
|
+
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
2771
|
+
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
2772
|
+
${result}`;
|
|
2773
|
+
}).join("\n\n");
|
|
2774
|
+
return `Original query: ${state.input}
|
|
2775
|
+
|
|
2776
|
+
Worker results:
|
|
2777
|
+
${taskResults}
|
|
2778
|
+
|
|
2779
|
+
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
2780
|
+
}
|
|
2781
|
+
function createAggregatorNode(config = {}) {
|
|
2679
2782
|
const {
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2783
|
+
model,
|
|
2784
|
+
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
2785
|
+
aggregateFn
|
|
2683
2786
|
} = config;
|
|
2787
|
+
return async (state) => {
|
|
2788
|
+
try {
|
|
2789
|
+
logger2.info("Aggregator node executing", {
|
|
2790
|
+
completedTasks: state.completedTasks.length,
|
|
2791
|
+
successfulTasks: state.completedTasks.filter((task) => task.success).length,
|
|
2792
|
+
failedTasks: state.completedTasks.filter((task) => !task.success).length
|
|
2793
|
+
});
|
|
2794
|
+
logger2.debug("Combining results from workers");
|
|
2795
|
+
if (aggregateFn) {
|
|
2796
|
+
logger2.debug("Using custom aggregation function");
|
|
2797
|
+
const response2 = await aggregateFn(state);
|
|
2798
|
+
logger2.info("Custom aggregation complete", {
|
|
2799
|
+
responseLength: response2.length
|
|
2800
|
+
});
|
|
2801
|
+
return {
|
|
2802
|
+
response: response2,
|
|
2803
|
+
status: "completed"
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
if (state.completedTasks.length === 0) {
|
|
2807
|
+
logger2.warn("No completed tasks to aggregate");
|
|
2808
|
+
return {
|
|
2809
|
+
response: "No tasks were completed.",
|
|
2810
|
+
status: "completed"
|
|
2811
|
+
};
|
|
2812
|
+
}
|
|
2813
|
+
if (!model) {
|
|
2814
|
+
logger2.debug("No model provided, concatenating results");
|
|
2815
|
+
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
2816
|
+
logger2.info("Simple concatenation complete", {
|
|
2817
|
+
resultLength: combinedResults.length
|
|
2818
|
+
});
|
|
2819
|
+
return {
|
|
2820
|
+
response: combinedResults || "No successful results to aggregate.",
|
|
2821
|
+
status: "completed"
|
|
2822
|
+
};
|
|
2823
|
+
}
|
|
2824
|
+
logger2.debug("Using LLM for intelligent aggregation", {
|
|
2825
|
+
taskCount: state.completedTasks.length
|
|
2826
|
+
});
|
|
2827
|
+
const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
|
|
2828
|
+
logger2.debug("Invoking aggregation LLM");
|
|
2829
|
+
const response = await model.invoke(messages);
|
|
2830
|
+
const aggregatedResponse = serializeModelContent(response.content);
|
|
2831
|
+
logger2.info("Aggregation complete", {
|
|
2832
|
+
responseLength: aggregatedResponse.length
|
|
2833
|
+
});
|
|
2834
|
+
logger2.debug("Aggregation response details", {
|
|
2835
|
+
responseLength: aggregatedResponse.length
|
|
2836
|
+
});
|
|
2837
|
+
logger2.debug("Aggregation complete");
|
|
2838
|
+
return {
|
|
2839
|
+
response: aggregatedResponse,
|
|
2840
|
+
status: "completed"
|
|
2841
|
+
};
|
|
2842
|
+
} catch (error) {
|
|
2843
|
+
const errorMessage = handleNodeError(error, "aggregator", false);
|
|
2844
|
+
logger2.error("Aggregator node error", {
|
|
2845
|
+
error: errorMessage,
|
|
2846
|
+
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2847
|
+
completedTasks: state.completedTasks.length
|
|
2848
|
+
});
|
|
2849
|
+
return {
|
|
2850
|
+
status: "failed",
|
|
2851
|
+
error: errorMessage
|
|
2852
|
+
};
|
|
2853
|
+
}
|
|
2854
|
+
};
|
|
2855
|
+
}
|
|
2856
|
+
|
|
2857
|
+
// src/multi-agent/nodes/supervisor.ts
|
|
2858
|
+
function allAssignmentsCompleted(state) {
|
|
2859
|
+
return state.activeAssignments.every(
|
|
2860
|
+
(assignment) => state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2861
|
+
);
|
|
2862
|
+
}
|
|
2863
|
+
function incrementAssignedWorkerLoads(state, assignments) {
|
|
2864
|
+
const updatedWorkers = { ...state.workers };
|
|
2865
|
+
for (const assignment of assignments) {
|
|
2866
|
+
const worker = updatedWorkers[assignment.workerId];
|
|
2867
|
+
if (!worker) {
|
|
2868
|
+
logger2.error("Worker not found in state", {
|
|
2869
|
+
workerId: assignment.workerId,
|
|
2870
|
+
availableWorkers: Object.keys(updatedWorkers)
|
|
2871
|
+
});
|
|
2872
|
+
throw new Error(
|
|
2873
|
+
`Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
|
|
2874
|
+
);
|
|
2875
|
+
}
|
|
2876
|
+
updatedWorkers[assignment.workerId] = {
|
|
2877
|
+
...worker,
|
|
2878
|
+
currentWorkload: worker.currentWorkload + 1
|
|
2879
|
+
};
|
|
2880
|
+
}
|
|
2881
|
+
return updatedWorkers;
|
|
2882
|
+
}
|
|
2883
|
+
function createSupervisorNode(config) {
|
|
2884
|
+
const { strategy, maxIterations = 10 } = config;
|
|
2684
2885
|
return async (state) => {
|
|
2685
2886
|
try {
|
|
2686
2887
|
logger2.info("Supervisor node executing", {
|
|
@@ -2701,9 +2902,7 @@ function createSupervisorNode(config) {
|
|
|
2701
2902
|
currentAgent: "aggregator"
|
|
2702
2903
|
};
|
|
2703
2904
|
}
|
|
2704
|
-
const allCompleted = state
|
|
2705
|
-
(assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
|
|
2706
|
-
);
|
|
2905
|
+
const allCompleted = allAssignmentsCompleted(state);
|
|
2707
2906
|
logger2.debug("Checking task completion", {
|
|
2708
2907
|
activeAssignments: state.activeAssignments.length,
|
|
2709
2908
|
completedTasks: state.completedTasks.length,
|
|
@@ -2741,6 +2940,7 @@ function createSupervisorNode(config) {
|
|
|
2741
2940
|
reasoning: decision.reasoning,
|
|
2742
2941
|
confidence: decision.confidence
|
|
2743
2942
|
});
|
|
2943
|
+
logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
|
|
2744
2944
|
} else {
|
|
2745
2945
|
logger2.info("Routing to multiple agents in parallel", {
|
|
2746
2946
|
targetAgents,
|
|
@@ -2748,57 +2948,22 @@ function createSupervisorNode(config) {
|
|
|
2748
2948
|
reasoning: decision.reasoning,
|
|
2749
2949
|
confidence: decision.confidence
|
|
2750
2950
|
});
|
|
2951
|
+
logger2.debug(
|
|
2952
|
+
`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
|
|
2953
|
+
);
|
|
2751
2954
|
}
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
} else {
|
|
2755
|
-
logger2.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
|
|
2756
|
-
}
|
|
2757
|
-
const task = state.messages[state.messages.length - 1]?.content || state.input;
|
|
2758
|
-
const assignments = targetAgents.map((workerId) => ({
|
|
2759
|
-
id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2760
|
-
workerId,
|
|
2761
|
-
task,
|
|
2762
|
-
priority: 5,
|
|
2763
|
-
assignedAt: Date.now()
|
|
2764
|
-
}));
|
|
2955
|
+
const task = getLatestTaskContent(state);
|
|
2956
|
+
const assignments = createTaskAssignments(targetAgents, task);
|
|
2765
2957
|
logger2.debug("Created task assignments", {
|
|
2766
2958
|
assignmentCount: assignments.length,
|
|
2767
|
-
assignments: assignments.map((
|
|
2768
|
-
id:
|
|
2769
|
-
workerId:
|
|
2770
|
-
taskLength:
|
|
2959
|
+
assignments: assignments.map((assignment) => ({
|
|
2960
|
+
id: assignment.id,
|
|
2961
|
+
workerId: assignment.workerId,
|
|
2962
|
+
taskLength: assignment.task.length
|
|
2771
2963
|
}))
|
|
2772
2964
|
});
|
|
2773
|
-
const messages = assignments
|
|
2774
|
-
|
|
2775
|
-
from: "supervisor",
|
|
2776
|
-
to: [assignment.workerId],
|
|
2777
|
-
type: "task_assignment",
|
|
2778
|
-
content: assignment.task,
|
|
2779
|
-
timestamp: Date.now(),
|
|
2780
|
-
metadata: {
|
|
2781
|
-
assignmentId: assignment.id,
|
|
2782
|
-
priority: assignment.priority
|
|
2783
|
-
}
|
|
2784
|
-
}));
|
|
2785
|
-
const updatedWorkers = { ...state.workers };
|
|
2786
|
-
for (const assignment of assignments) {
|
|
2787
|
-
const worker = updatedWorkers[assignment.workerId];
|
|
2788
|
-
if (!worker) {
|
|
2789
|
-
logger2.error("Worker not found in state", {
|
|
2790
|
-
workerId: assignment.workerId,
|
|
2791
|
-
availableWorkers: Object.keys(updatedWorkers)
|
|
2792
|
-
});
|
|
2793
|
-
throw new Error(
|
|
2794
|
-
`Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
|
|
2795
|
-
);
|
|
2796
|
-
}
|
|
2797
|
-
updatedWorkers[assignment.workerId] = {
|
|
2798
|
-
...worker,
|
|
2799
|
-
currentWorkload: worker.currentWorkload + 1
|
|
2800
|
-
};
|
|
2801
|
-
}
|
|
2965
|
+
const messages = createAssignmentMessages(assignments);
|
|
2966
|
+
const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
|
|
2802
2967
|
logger2.info("Supervisor routing complete", {
|
|
2803
2968
|
currentAgent: targetAgents.join(","),
|
|
2804
2969
|
status: "executing",
|
|
@@ -2808,41 +2973,187 @@ function createSupervisorNode(config) {
|
|
|
2808
2973
|
});
|
|
2809
2974
|
return {
|
|
2810
2975
|
currentAgent: targetAgents.join(","),
|
|
2811
|
-
// Store all agents (for backward compat)
|
|
2812
2976
|
status: "executing",
|
|
2813
2977
|
routingHistory: [decision],
|
|
2814
2978
|
activeAssignments: assignments,
|
|
2815
|
-
// Multiple assignments for parallel execution!
|
|
2816
2979
|
messages,
|
|
2817
2980
|
workers: updatedWorkers,
|
|
2818
|
-
// Include updated workload
|
|
2819
|
-
// Add 1 to iteration counter (uses additive reducer)
|
|
2820
2981
|
iteration: 1
|
|
2821
2982
|
};
|
|
2822
2983
|
} catch (error) {
|
|
2984
|
+
const errorMessage = handleNodeError(error, "supervisor", false);
|
|
2823
2985
|
logger2.error("Supervisor node error", {
|
|
2824
|
-
error:
|
|
2986
|
+
error: errorMessage,
|
|
2825
2987
|
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
2826
2988
|
iteration: state.iteration
|
|
2827
2989
|
});
|
|
2828
2990
|
return {
|
|
2829
2991
|
status: "failed",
|
|
2830
|
-
error:
|
|
2992
|
+
error: errorMessage
|
|
2831
2993
|
};
|
|
2832
2994
|
}
|
|
2833
2995
|
};
|
|
2834
2996
|
}
|
|
2997
|
+
|
|
2998
|
+
// src/multi-agent/nodes/worker.ts
|
|
2999
|
+
function buildDefaultSystemPrompt(config) {
|
|
3000
|
+
return `You are a specialized worker agent with the following capabilities:
|
|
3001
|
+
Skills: ${config.capabilities.skills.join(", ")}
|
|
3002
|
+
Tools: ${config.capabilities.tools.join(", ")}
|
|
3003
|
+
|
|
3004
|
+
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
3005
|
+
}
|
|
3006
|
+
async function invokeWorkerModel(model, config, assignment) {
|
|
3007
|
+
const messages = createPromptMessages(
|
|
3008
|
+
config.systemPrompt || buildDefaultSystemPrompt(config),
|
|
3009
|
+
assignment.task
|
|
3010
|
+
);
|
|
3011
|
+
let modelToUse = model;
|
|
3012
|
+
if (config.tools && config.tools.length > 0 && model.bindTools) {
|
|
3013
|
+
logger2.debug("Binding tools to model", {
|
|
3014
|
+
workerId: config.id,
|
|
3015
|
+
toolCount: config.tools.length,
|
|
3016
|
+
toolNames: config.tools.map((tool) => tool.metadata.name)
|
|
3017
|
+
});
|
|
3018
|
+
modelToUse = model.bindTools(
|
|
3019
|
+
convertWorkerToolsForLangChain(config.tools)
|
|
3020
|
+
);
|
|
3021
|
+
}
|
|
3022
|
+
logger2.debug("Invoking LLM", { workerId: config.id });
|
|
3023
|
+
const response = await modelToUse.invoke(messages);
|
|
3024
|
+
const result = serializeModelContent(response.content);
|
|
3025
|
+
logger2.info("Worker task completed", {
|
|
3026
|
+
workerId: config.id,
|
|
3027
|
+
assignmentId: assignment.id,
|
|
3028
|
+
resultLength: result.length
|
|
3029
|
+
});
|
|
3030
|
+
logger2.debug("Worker result details", {
|
|
3031
|
+
workerId: config.id,
|
|
3032
|
+
assignmentId: assignment.id,
|
|
3033
|
+
resultLength: result.length
|
|
3034
|
+
});
|
|
3035
|
+
const taskResult = {
|
|
3036
|
+
assignmentId: assignment.id,
|
|
3037
|
+
workerId: config.id,
|
|
3038
|
+
success: true,
|
|
3039
|
+
result,
|
|
3040
|
+
completedAt: Date.now(),
|
|
3041
|
+
metadata: {
|
|
3042
|
+
skills_used: config.capabilities.skills
|
|
3043
|
+
}
|
|
3044
|
+
};
|
|
3045
|
+
const message = {
|
|
3046
|
+
id: createGeneratedId("msg"),
|
|
3047
|
+
from: config.id,
|
|
3048
|
+
to: ["supervisor"],
|
|
3049
|
+
type: "task_result",
|
|
3050
|
+
content: result,
|
|
3051
|
+
timestamp: Date.now(),
|
|
3052
|
+
metadata: {
|
|
3053
|
+
assignmentId: assignment.id,
|
|
3054
|
+
success: true
|
|
3055
|
+
}
|
|
3056
|
+
};
|
|
3057
|
+
return {
|
|
3058
|
+
completedTasks: [taskResult],
|
|
3059
|
+
messages: [message]
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
function getStateWorkerOrThrow(state, workerId) {
|
|
3063
|
+
const currentWorker = state.workers[workerId];
|
|
3064
|
+
if (!currentWorker) {
|
|
3065
|
+
logger2.error("Attempted to decrement workload for unknown worker", {
|
|
3066
|
+
workerId,
|
|
3067
|
+
availableWorkers: Object.keys(state.workers)
|
|
3068
|
+
});
|
|
3069
|
+
throw new Error(`Worker "${workerId}" not found when decrementing workload.`);
|
|
3070
|
+
}
|
|
3071
|
+
return currentWorker;
|
|
3072
|
+
}
|
|
3073
|
+
function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
|
|
3074
|
+
const workloadFromWorker = workerFromExecution?.currentWorkload;
|
|
3075
|
+
if (typeof workloadFromWorker === "number" && Number.isFinite(workloadFromWorker)) {
|
|
3076
|
+
return workloadFromWorker;
|
|
3077
|
+
}
|
|
3078
|
+
if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
|
|
3079
|
+
return currentWorker.currentWorkload;
|
|
3080
|
+
}
|
|
3081
|
+
logger2.error("Worker workload is not a valid number; cannot decrement", {
|
|
3082
|
+
workerId,
|
|
3083
|
+
workloadFromState: currentWorker.currentWorkload,
|
|
3084
|
+
...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
|
|
3085
|
+
});
|
|
3086
|
+
throw new Error(
|
|
3087
|
+
`Worker "${workerId}" does not have a valid numeric currentWorkload to decrement.`
|
|
3088
|
+
);
|
|
3089
|
+
}
|
|
3090
|
+
function mergeWorkersWithDecrement(state, workerId, executionResult) {
|
|
3091
|
+
const currentWorker = getStateWorkerOrThrow(state, workerId);
|
|
3092
|
+
const executionResultWorkers = executionResult.workers || {};
|
|
3093
|
+
const baseWorkers = {
|
|
3094
|
+
...state.workers,
|
|
3095
|
+
...executionResultWorkers
|
|
3096
|
+
};
|
|
3097
|
+
const workerFromExecution = executionResultWorkers[workerId];
|
|
3098
|
+
const previousWorkload = resolvePreviousWorkload(
|
|
3099
|
+
workerId,
|
|
3100
|
+
currentWorker,
|
|
3101
|
+
workerFromExecution
|
|
3102
|
+
);
|
|
3103
|
+
const hasWorkerOverride = typeof workerFromExecution === "object" && workerFromExecution !== null;
|
|
3104
|
+
const updatedWorker = hasWorkerOverride ? {
|
|
3105
|
+
...currentWorker,
|
|
3106
|
+
...workerFromExecution,
|
|
3107
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3108
|
+
} : {
|
|
3109
|
+
...currentWorker,
|
|
3110
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3111
|
+
};
|
|
3112
|
+
const updatedWorkers = {
|
|
3113
|
+
...baseWorkers,
|
|
3114
|
+
[workerId]: updatedWorker
|
|
3115
|
+
};
|
|
3116
|
+
logger2.debug("Worker workload decremented", {
|
|
3117
|
+
workerId,
|
|
3118
|
+
previousWorkload,
|
|
3119
|
+
newWorkload: updatedWorkers[workerId].currentWorkload,
|
|
3120
|
+
hadExecutionResultWorkers: !!executionResult.workers
|
|
3121
|
+
});
|
|
3122
|
+
return updatedWorkers;
|
|
3123
|
+
}
|
|
3124
|
+
function decrementWorkerOnError(state, workerId) {
|
|
3125
|
+
const currentWorker = getStateWorkerOrThrow(state, workerId);
|
|
3126
|
+
const previousWorkload = resolvePreviousWorkload(workerId, currentWorker);
|
|
3127
|
+
const updatedWorkers = {
|
|
3128
|
+
...state.workers,
|
|
3129
|
+
[workerId]: {
|
|
3130
|
+
...currentWorker,
|
|
3131
|
+
currentWorkload: Math.max(0, previousWorkload - 1)
|
|
3132
|
+
}
|
|
3133
|
+
};
|
|
3134
|
+
logger2.debug("Worker workload decremented (error path)", {
|
|
3135
|
+
workerId,
|
|
3136
|
+
previousWorkload,
|
|
3137
|
+
newWorkload: updatedWorkers[workerId].currentWorkload
|
|
3138
|
+
});
|
|
3139
|
+
return updatedWorkers;
|
|
3140
|
+
}
|
|
3141
|
+
function createErrorTaskResult(assignment, workerId, errorMessage) {
|
|
3142
|
+
logger2.warn("Creating error result for assignment", {
|
|
3143
|
+
workerId,
|
|
3144
|
+
assignmentId: assignment.id
|
|
3145
|
+
});
|
|
3146
|
+
return {
|
|
3147
|
+
assignmentId: assignment.id,
|
|
3148
|
+
workerId,
|
|
3149
|
+
success: false,
|
|
3150
|
+
result: "",
|
|
3151
|
+
error: errorMessage,
|
|
3152
|
+
completedAt: Date.now()
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
2835
3155
|
function createWorkerNode(config) {
|
|
2836
|
-
const {
|
|
2837
|
-
id,
|
|
2838
|
-
capabilities,
|
|
2839
|
-
model,
|
|
2840
|
-
tools = [],
|
|
2841
|
-
systemPrompt,
|
|
2842
|
-
verbose = false,
|
|
2843
|
-
executeFn,
|
|
2844
|
-
agent
|
|
2845
|
-
} = config;
|
|
3156
|
+
const { id, model, executeFn, agent } = config;
|
|
2846
3157
|
return async (state, runConfig) => {
|
|
2847
3158
|
try {
|
|
2848
3159
|
logger2.info("Worker node executing", {
|
|
@@ -2850,9 +3161,7 @@ function createWorkerNode(config) {
|
|
|
2850
3161
|
iteration: state.iteration,
|
|
2851
3162
|
activeAssignments: state.activeAssignments.length
|
|
2852
3163
|
});
|
|
2853
|
-
const currentAssignment = state
|
|
2854
|
-
(assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
|
|
2855
|
-
);
|
|
3164
|
+
const currentAssignment = findCurrentAssignment(state, id);
|
|
2856
3165
|
if (!currentAssignment) {
|
|
2857
3166
|
logger2.debug("No active assignment found for worker", {
|
|
2858
3167
|
workerId: id,
|
|
@@ -2864,105 +3173,35 @@ function createWorkerNode(config) {
|
|
|
2864
3173
|
logger2.info("Worker processing assignment", {
|
|
2865
3174
|
workerId: id,
|
|
2866
3175
|
assignmentId: currentAssignment.id,
|
|
2867
|
-
taskLength: currentAssignment.task.length
|
|
2868
|
-
|
|
3176
|
+
taskLength: currentAssignment.task.length
|
|
3177
|
+
});
|
|
3178
|
+
logger2.debug("Worker assignment details", {
|
|
3179
|
+
workerId: id,
|
|
3180
|
+
assignmentId: currentAssignment.id,
|
|
3181
|
+
taskLength: currentAssignment.task.length
|
|
2869
3182
|
});
|
|
2870
|
-
async function executeWithLLM() {
|
|
2871
|
-
logger2.debug("Using default LLM execution", {
|
|
2872
|
-
workerId: id,
|
|
2873
|
-
hasTools: tools.length > 0,
|
|
2874
|
-
toolCount: tools.length
|
|
2875
|
-
});
|
|
2876
|
-
const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
|
|
2877
|
-
Skills: ${capabilities.skills.join(", ")}
|
|
2878
|
-
Tools: ${capabilities.tools.join(", ")}
|
|
2879
|
-
|
|
2880
|
-
Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
|
|
2881
|
-
const messages = [
|
|
2882
|
-
new import_messages5.SystemMessage(systemPrompt || defaultSystemPrompt),
|
|
2883
|
-
new import_messages5.HumanMessage(currentAssignment.task)
|
|
2884
|
-
];
|
|
2885
|
-
let modelToUse = model;
|
|
2886
|
-
if (tools.length > 0 && model.bindTools) {
|
|
2887
|
-
logger2.debug("Binding tools to model", {
|
|
2888
|
-
workerId: id,
|
|
2889
|
-
toolCount: tools.length,
|
|
2890
|
-
toolNames: tools.map((t) => t.metadata.name)
|
|
2891
|
-
});
|
|
2892
|
-
const langchainTools = convertWorkerToolsForLangChain(tools);
|
|
2893
|
-
modelToUse = model.bindTools(langchainTools);
|
|
2894
|
-
}
|
|
2895
|
-
logger2.debug("Invoking LLM", { workerId: id });
|
|
2896
|
-
const response = await modelToUse.invoke(messages);
|
|
2897
|
-
const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
2898
|
-
logger2.info("Worker task completed", {
|
|
2899
|
-
workerId: id,
|
|
2900
|
-
assignmentId: currentAssignment.id,
|
|
2901
|
-
resultLength: result.length,
|
|
2902
|
-
resultPreview: result.substring(0, 100)
|
|
2903
|
-
});
|
|
2904
|
-
const taskResult = {
|
|
2905
|
-
assignmentId: currentAssignment.id,
|
|
2906
|
-
workerId: id,
|
|
2907
|
-
success: true,
|
|
2908
|
-
result,
|
|
2909
|
-
completedAt: Date.now(),
|
|
2910
|
-
metadata: {
|
|
2911
|
-
skills_used: capabilities.skills
|
|
2912
|
-
}
|
|
2913
|
-
};
|
|
2914
|
-
const message = {
|
|
2915
|
-
id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
2916
|
-
from: id,
|
|
2917
|
-
to: ["supervisor"],
|
|
2918
|
-
type: "task_result",
|
|
2919
|
-
content: result,
|
|
2920
|
-
timestamp: Date.now(),
|
|
2921
|
-
metadata: {
|
|
2922
|
-
assignmentId: currentAssignment.id,
|
|
2923
|
-
success: true
|
|
2924
|
-
}
|
|
2925
|
-
};
|
|
2926
|
-
return {
|
|
2927
|
-
completedTasks: [taskResult],
|
|
2928
|
-
messages: [message]
|
|
2929
|
-
};
|
|
2930
|
-
}
|
|
2931
3183
|
let executionResult;
|
|
2932
3184
|
if (executeFn) {
|
|
2933
3185
|
logger2.debug("Using custom execution function", { workerId: id });
|
|
2934
3186
|
executionResult = await executeFn(state, runConfig);
|
|
2935
3187
|
} else if (agent && isReActAgent(agent)) {
|
|
2936
3188
|
logger2.debug("Using ReAct agent", { workerId: id });
|
|
2937
|
-
const wrappedFn = wrapReActAgent(id, agent, verbose);
|
|
3189
|
+
const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
|
|
2938
3190
|
executionResult = await wrappedFn(state, runConfig);
|
|
2939
3191
|
} else if (model) {
|
|
2940
|
-
|
|
3192
|
+
logger2.debug("Using default LLM execution", {
|
|
3193
|
+
workerId: id,
|
|
3194
|
+
hasTools: (config.tools ?? []).length > 0,
|
|
3195
|
+
toolCount: (config.tools ?? []).length
|
|
3196
|
+
});
|
|
3197
|
+
executionResult = await invokeWorkerModel(model, config, currentAssignment);
|
|
2941
3198
|
} else {
|
|
2942
3199
|
logger2.error("Worker missing required configuration", { workerId: id });
|
|
2943
3200
|
throw new Error(
|
|
2944
3201
|
`Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
|
|
2945
3202
|
);
|
|
2946
3203
|
}
|
|
2947
|
-
const
|
|
2948
|
-
const baseWorkers = {
|
|
2949
|
-
...state.workers,
|
|
2950
|
-
...executionResult.workers || {}
|
|
2951
|
-
};
|
|
2952
|
-
const workerToUpdate = baseWorkers[id] || currentWorker;
|
|
2953
|
-
const updatedWorkers = {
|
|
2954
|
-
...baseWorkers,
|
|
2955
|
-
[id]: {
|
|
2956
|
-
...workerToUpdate,
|
|
2957
|
-
currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
|
|
2958
|
-
}
|
|
2959
|
-
};
|
|
2960
|
-
logger2.debug("Worker workload decremented", {
|
|
2961
|
-
workerId: id,
|
|
2962
|
-
previousWorkload: workerToUpdate.currentWorkload,
|
|
2963
|
-
newWorkload: updatedWorkers[id].currentWorkload,
|
|
2964
|
-
hadExecutionResultWorkers: !!executionResult.workers
|
|
2965
|
-
});
|
|
3204
|
+
const updatedWorkers = mergeWorkersWithDecrement(state, id, executionResult);
|
|
2966
3205
|
return {
|
|
2967
3206
|
...executionResult,
|
|
2968
3207
|
workers: updatedWorkers
|
|
@@ -2973,41 +3212,29 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
2973
3212
|
workerId: id,
|
|
2974
3213
|
error: errorMessage
|
|
2975
3214
|
});
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3215
|
+
let updatedWorkers;
|
|
3216
|
+
try {
|
|
3217
|
+
updatedWorkers = decrementWorkerOnError(state, id);
|
|
3218
|
+
} catch (workloadError) {
|
|
3219
|
+
const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
|
|
3220
|
+
logger2.error("Worker error handling failed", {
|
|
3221
|
+
workerId: id,
|
|
3222
|
+
error: workloadErrorMessage
|
|
3223
|
+
});
|
|
3224
|
+
return {
|
|
3225
|
+
status: "failed",
|
|
3226
|
+
error: `${errorMessage}. ${workloadErrorMessage}`
|
|
3227
|
+
};
|
|
3228
|
+
}
|
|
2989
3229
|
const currentAssignment = state.activeAssignments.find(
|
|
2990
3230
|
(assignment) => assignment.workerId === id
|
|
2991
3231
|
);
|
|
2992
3232
|
if (currentAssignment) {
|
|
2993
|
-
logger2.warn("Creating error result for assignment", {
|
|
2994
|
-
workerId: id,
|
|
2995
|
-
assignmentId: currentAssignment.id
|
|
2996
|
-
});
|
|
2997
|
-
const errorResult = {
|
|
2998
|
-
assignmentId: currentAssignment.id,
|
|
2999
|
-
workerId: id,
|
|
3000
|
-
success: false,
|
|
3001
|
-
result: "",
|
|
3002
|
-
error: errorMessage,
|
|
3003
|
-
completedAt: Date.now()
|
|
3004
|
-
};
|
|
3005
3233
|
return {
|
|
3006
|
-
completedTasks: [
|
|
3234
|
+
completedTasks: [createErrorTaskResult(currentAssignment, id, errorMessage)],
|
|
3007
3235
|
currentAgent: "supervisor",
|
|
3008
3236
|
status: "routing",
|
|
3009
3237
|
workers: updatedWorkers
|
|
3010
|
-
// Include workload update
|
|
3011
3238
|
};
|
|
3012
3239
|
}
|
|
3013
3240
|
logger2.error("No assignment found for error handling", { workerId: id });
|
|
@@ -3015,95 +3242,6 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
|
|
|
3015
3242
|
status: "failed",
|
|
3016
3243
|
error: errorMessage,
|
|
3017
3244
|
workers: updatedWorkers
|
|
3018
|
-
// Include workload update even on failure
|
|
3019
|
-
};
|
|
3020
|
-
}
|
|
3021
|
-
};
|
|
3022
|
-
}
|
|
3023
|
-
function createAggregatorNode(config = {}) {
|
|
3024
|
-
const {
|
|
3025
|
-
model,
|
|
3026
|
-
systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
|
|
3027
|
-
aggregateFn,
|
|
3028
|
-
verbose = false
|
|
3029
|
-
} = config;
|
|
3030
|
-
return async (state) => {
|
|
3031
|
-
try {
|
|
3032
|
-
logger2.info("Aggregator node executing", {
|
|
3033
|
-
completedTasks: state.completedTasks.length,
|
|
3034
|
-
successfulTasks: state.completedTasks.filter((t) => t.success).length,
|
|
3035
|
-
failedTasks: state.completedTasks.filter((t) => !t.success).length
|
|
3036
|
-
});
|
|
3037
|
-
logger2.debug("Combining results from workers");
|
|
3038
|
-
if (aggregateFn) {
|
|
3039
|
-
logger2.debug("Using custom aggregation function");
|
|
3040
|
-
const response2 = await aggregateFn(state);
|
|
3041
|
-
logger2.info("Custom aggregation complete", {
|
|
3042
|
-
responseLength: response2.length
|
|
3043
|
-
});
|
|
3044
|
-
return {
|
|
3045
|
-
response: response2,
|
|
3046
|
-
status: "completed"
|
|
3047
|
-
};
|
|
3048
|
-
}
|
|
3049
|
-
if (state.completedTasks.length === 0) {
|
|
3050
|
-
logger2.warn("No completed tasks to aggregate");
|
|
3051
|
-
return {
|
|
3052
|
-
response: "No tasks were completed.",
|
|
3053
|
-
status: "completed"
|
|
3054
|
-
};
|
|
3055
|
-
}
|
|
3056
|
-
if (!model) {
|
|
3057
|
-
logger2.debug("No model provided, concatenating results");
|
|
3058
|
-
const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
|
|
3059
|
-
logger2.info("Simple concatenation complete", {
|
|
3060
|
-
resultLength: combinedResults.length
|
|
3061
|
-
});
|
|
3062
|
-
return {
|
|
3063
|
-
response: combinedResults || "No successful results to aggregate.",
|
|
3064
|
-
status: "completed"
|
|
3065
|
-
};
|
|
3066
|
-
}
|
|
3067
|
-
logger2.debug("Using LLM for intelligent aggregation", {
|
|
3068
|
-
taskCount: state.completedTasks.length
|
|
3069
|
-
});
|
|
3070
|
-
const taskResults = state.completedTasks.map((task, idx) => {
|
|
3071
|
-
const status = task.success ? "\u2713" : "\u2717";
|
|
3072
|
-
const result = task.success ? task.result : `Error: ${task.error}`;
|
|
3073
|
-
return `${idx + 1}. [${status}] Worker ${task.workerId}:
|
|
3074
|
-
${result}`;
|
|
3075
|
-
}).join("\n\n");
|
|
3076
|
-
const userPrompt = `Original query: ${state.input}
|
|
3077
|
-
|
|
3078
|
-
Worker results:
|
|
3079
|
-
${taskResults}
|
|
3080
|
-
|
|
3081
|
-
Please synthesize these results into a comprehensive response that addresses the original query.`;
|
|
3082
|
-
const messages = [
|
|
3083
|
-
new import_messages5.SystemMessage(systemPrompt),
|
|
3084
|
-
new import_messages5.HumanMessage(userPrompt)
|
|
3085
|
-
];
|
|
3086
|
-
logger2.debug("Invoking aggregation LLM");
|
|
3087
|
-
const response = await model.invoke(messages);
|
|
3088
|
-
const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
|
|
3089
|
-
logger2.info("Aggregation complete", {
|
|
3090
|
-
responseLength: aggregatedResponse.length,
|
|
3091
|
-
responsePreview: aggregatedResponse.substring(0, 100)
|
|
3092
|
-
});
|
|
3093
|
-
logger2.debug("Aggregation complete");
|
|
3094
|
-
return {
|
|
3095
|
-
response: aggregatedResponse,
|
|
3096
|
-
status: "completed"
|
|
3097
|
-
};
|
|
3098
|
-
} catch (error) {
|
|
3099
|
-
logger2.error("Aggregator node error", {
|
|
3100
|
-
error: error instanceof Error ? error.message : String(error),
|
|
3101
|
-
...error instanceof Error && error.stack ? { stack: error.stack } : {},
|
|
3102
|
-
completedTasks: state.completedTasks.length
|
|
3103
|
-
});
|
|
3104
|
-
return {
|
|
3105
|
-
status: "failed",
|
|
3106
|
-
error: error instanceof Error ? error.message : "Unknown error in aggregator"
|
|
3107
3245
|
};
|
|
3108
3246
|
}
|
|
3109
3247
|
};
|