@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.js CHANGED
@@ -790,7 +790,7 @@ var PlanStepSchema = z4.object({
790
790
  /**
791
791
  * Optional arguments for the tool
792
792
  */
793
- args: z4.record(z4.any()).optional().describe("Arguments to pass to the tool")
793
+ args: z4.record(z4.string(), z4.unknown()).optional().describe("Arguments to pass to the tool")
794
794
  });
795
795
  var CompletedStepSchema = z4.object({
796
796
  /**
@@ -800,7 +800,7 @@ var CompletedStepSchema = z4.object({
800
800
  /**
801
801
  * The result of executing the step
802
802
  */
803
- result: z4.any().describe("The result of executing the step"),
803
+ result: z4.unknown().describe("The result of executing the step"),
804
804
  /**
805
805
  * Whether the step succeeded
806
806
  */
@@ -977,6 +977,9 @@ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
977
977
  var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planner");
978
978
  var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
979
979
  var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
980
+ function invokePlanExecuteTool(tool, args) {
981
+ return tool.invoke.call(tool, args);
982
+ }
980
983
  function createPlannerNode(config) {
981
984
  const {
982
985
  model,
@@ -1044,10 +1047,18 @@ function createExecutorNode(config) {
1044
1047
  const {
1045
1048
  tools,
1046
1049
  model,
1047
- parallel = false,
1050
+ parallel,
1048
1051
  stepTimeout = 3e4,
1049
1052
  enableDeduplication = true
1050
1053
  } = config;
1054
+ if (typeof model !== "undefined") {
1055
+ executorLogger.warn("ExecutorConfig.model is currently unsupported and will be ignored");
1056
+ }
1057
+ if (typeof parallel !== "undefined") {
1058
+ executorLogger.warn("ExecutorConfig.parallel is currently unsupported and will be ignored", {
1059
+ parallel
1060
+ });
1061
+ }
1051
1062
  return async (state) => {
1052
1063
  const { plan, currentStepIndex = 0, pastSteps = [], iteration = 0 } = state;
1053
1064
  try {
@@ -1124,13 +1135,20 @@ function createExecutorNode(config) {
1124
1135
  throw new Error(`Tool not found: ${currentStep.tool}`);
1125
1136
  }
1126
1137
  const startTime = Date.now();
1127
- const timeoutPromise = new Promise(
1128
- (_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
1129
- );
1130
- result = await Promise.race([
1131
- tool.invoke(currentStep.args || {}),
1132
- timeoutPromise
1133
- ]);
1138
+ let timeoutId;
1139
+ try {
1140
+ const timeoutPromise = new Promise((_, reject) => {
1141
+ timeoutId = setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout);
1142
+ });
1143
+ result = await Promise.race([
1144
+ invokePlanExecuteTool(tool, currentStep.args || {}),
1145
+ timeoutPromise
1146
+ ]);
1147
+ } finally {
1148
+ if (timeoutId !== void 0) {
1149
+ clearTimeout(timeoutId);
1150
+ }
1151
+ }
1134
1152
  const executionTime = Date.now() - startTime;
1135
1153
  executorLogger.debug("Step executed successfully", {
1136
1154
  stepId: currentStep.id,
@@ -1187,9 +1205,14 @@ function createExecutorNode(config) {
1187
1205
  function createReplannerNode(config) {
1188
1206
  const {
1189
1207
  model,
1190
- replanThreshold = 0.7,
1208
+ replanThreshold,
1191
1209
  systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
1192
1210
  } = config;
1211
+ if (typeof replanThreshold !== "undefined") {
1212
+ replannerLogger.warn("ReplannerConfig.replanThreshold is currently unsupported and will be ignored", {
1213
+ replanThreshold
1214
+ });
1215
+ }
1193
1216
  return async (state) => {
1194
1217
  const startTime = Date.now();
1195
1218
  try {
@@ -2558,16 +2581,82 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2558
2581
  };
2559
2582
  }
2560
2583
 
2561
- // src/multi-agent/nodes.ts
2584
+ // src/multi-agent/nodes/shared.ts
2562
2585
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
2563
2586
  import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
2564
2587
  var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2588
+ function createGeneratedId(prefix) {
2589
+ return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
2590
+ }
2591
+ function createTaskAssignmentId() {
2592
+ return `task_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
2593
+ }
2594
+ function getLatestTaskContent(state) {
2595
+ return state.messages[state.messages.length - 1]?.content || state.input;
2596
+ }
2597
+ function createTaskAssignments(workerIds, task) {
2598
+ return workerIds.map((workerId) => ({
2599
+ id: createTaskAssignmentId(),
2600
+ workerId,
2601
+ task,
2602
+ priority: 5,
2603
+ assignedAt: Date.now()
2604
+ }));
2605
+ }
2606
+ function createAssignmentMessages(assignments) {
2607
+ return assignments.map((assignment) => ({
2608
+ id: createGeneratedId("msg"),
2609
+ from: "supervisor",
2610
+ to: [assignment.workerId],
2611
+ type: "task_assignment",
2612
+ content: assignment.task,
2613
+ timestamp: Date.now(),
2614
+ metadata: {
2615
+ assignmentId: assignment.id,
2616
+ priority: assignment.priority
2617
+ }
2618
+ }));
2619
+ }
2620
+ function findCurrentAssignment(state, workerId) {
2621
+ return state.activeAssignments.find(
2622
+ (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
2623
+ );
2624
+ }
2565
2625
  function convertWorkerToolsForLangChain(tools) {
2566
2626
  const safeTools = tools ?? [];
2567
- return toLangChainTools2(
2568
- safeTools
2569
- );
2627
+ return toLangChainTools2(safeTools);
2570
2628
  }
2629
+ function serializeModelContent(content) {
2630
+ if (typeof content === "string") {
2631
+ return content;
2632
+ }
2633
+ try {
2634
+ const serialized = JSON.stringify(content);
2635
+ if (typeof serialized !== "string") {
2636
+ const error = new Error(
2637
+ "Failed to serialize model content: JSON.stringify returned undefined"
2638
+ );
2639
+ logger2.error("Model content serialization failed", {
2640
+ errorMessage: error.message,
2641
+ contentType: content === null ? "null" : typeof content
2642
+ });
2643
+ throw error;
2644
+ }
2645
+ return serialized;
2646
+ } catch (error) {
2647
+ const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
2648
+ logger2.error("Model content serialization threw an error", {
2649
+ errorMessage: normalizedError.message,
2650
+ contentType: content === null ? "null" : typeof content
2651
+ });
2652
+ throw normalizedError;
2653
+ }
2654
+ }
2655
+ function createPromptMessages(systemPrompt, task) {
2656
+ return [new SystemMessage5(systemPrompt), new HumanMessage5(task)];
2657
+ }
2658
+
2659
+ // src/multi-agent/nodes/aggregator.ts
2571
2660
  var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
2572
2661
 
2573
2662
  Your job is to:
@@ -2577,12 +2666,124 @@ Your job is to:
2577
2666
  4. Provide a clear, comprehensive final answer
2578
2667
 
2579
2668
  Be concise but thorough in your aggregation.`;
2580
- function createSupervisorNode(config) {
2669
+ function createAggregationPrompt(state) {
2670
+ const taskResults = state.completedTasks.map((task, idx) => {
2671
+ const status = task.success ? "\u2713" : "\u2717";
2672
+ const result = task.success ? task.result : `Error: ${task.error}`;
2673
+ return `${idx + 1}. [${status}] Worker ${task.workerId}:
2674
+ ${result}`;
2675
+ }).join("\n\n");
2676
+ return `Original query: ${state.input}
2677
+
2678
+ Worker results:
2679
+ ${taskResults}
2680
+
2681
+ Please synthesize these results into a comprehensive response that addresses the original query.`;
2682
+ }
2683
+ function createAggregatorNode(config = {}) {
2581
2684
  const {
2582
- strategy,
2583
- verbose = false,
2584
- maxIterations = 10
2685
+ model,
2686
+ systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
2687
+ aggregateFn
2585
2688
  } = config;
2689
+ return async (state) => {
2690
+ try {
2691
+ logger2.info("Aggregator node executing", {
2692
+ completedTasks: state.completedTasks.length,
2693
+ successfulTasks: state.completedTasks.filter((task) => task.success).length,
2694
+ failedTasks: state.completedTasks.filter((task) => !task.success).length
2695
+ });
2696
+ logger2.debug("Combining results from workers");
2697
+ if (aggregateFn) {
2698
+ logger2.debug("Using custom aggregation function");
2699
+ const response2 = await aggregateFn(state);
2700
+ logger2.info("Custom aggregation complete", {
2701
+ responseLength: response2.length
2702
+ });
2703
+ return {
2704
+ response: response2,
2705
+ status: "completed"
2706
+ };
2707
+ }
2708
+ if (state.completedTasks.length === 0) {
2709
+ logger2.warn("No completed tasks to aggregate");
2710
+ return {
2711
+ response: "No tasks were completed.",
2712
+ status: "completed"
2713
+ };
2714
+ }
2715
+ if (!model) {
2716
+ logger2.debug("No model provided, concatenating results");
2717
+ const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2718
+ logger2.info("Simple concatenation complete", {
2719
+ resultLength: combinedResults.length
2720
+ });
2721
+ return {
2722
+ response: combinedResults || "No successful results to aggregate.",
2723
+ status: "completed"
2724
+ };
2725
+ }
2726
+ logger2.debug("Using LLM for intelligent aggregation", {
2727
+ taskCount: state.completedTasks.length
2728
+ });
2729
+ const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
2730
+ logger2.debug("Invoking aggregation LLM");
2731
+ const response = await model.invoke(messages);
2732
+ const aggregatedResponse = serializeModelContent(response.content);
2733
+ logger2.info("Aggregation complete", {
2734
+ responseLength: aggregatedResponse.length
2735
+ });
2736
+ logger2.debug("Aggregation response details", {
2737
+ responseLength: aggregatedResponse.length
2738
+ });
2739
+ logger2.debug("Aggregation complete");
2740
+ return {
2741
+ response: aggregatedResponse,
2742
+ status: "completed"
2743
+ };
2744
+ } catch (error) {
2745
+ const errorMessage = handleNodeError(error, "aggregator", false);
2746
+ logger2.error("Aggregator node error", {
2747
+ error: errorMessage,
2748
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2749
+ completedTasks: state.completedTasks.length
2750
+ });
2751
+ return {
2752
+ status: "failed",
2753
+ error: errorMessage
2754
+ };
2755
+ }
2756
+ };
2757
+ }
2758
+
2759
+ // src/multi-agent/nodes/supervisor.ts
2760
+ function allAssignmentsCompleted(state) {
2761
+ return state.activeAssignments.every(
2762
+ (assignment) => state.completedTasks.some((task) => task.assignmentId === assignment.id)
2763
+ );
2764
+ }
2765
+ function incrementAssignedWorkerLoads(state, assignments) {
2766
+ const updatedWorkers = { ...state.workers };
2767
+ for (const assignment of assignments) {
2768
+ const worker = updatedWorkers[assignment.workerId];
2769
+ if (!worker) {
2770
+ logger2.error("Worker not found in state", {
2771
+ workerId: assignment.workerId,
2772
+ availableWorkers: Object.keys(updatedWorkers)
2773
+ });
2774
+ throw new Error(
2775
+ `Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
2776
+ );
2777
+ }
2778
+ updatedWorkers[assignment.workerId] = {
2779
+ ...worker,
2780
+ currentWorkload: worker.currentWorkload + 1
2781
+ };
2782
+ }
2783
+ return updatedWorkers;
2784
+ }
2785
+ function createSupervisorNode(config) {
2786
+ const { strategy, maxIterations = 10 } = config;
2586
2787
  return async (state) => {
2587
2788
  try {
2588
2789
  logger2.info("Supervisor node executing", {
@@ -2603,9 +2804,7 @@ function createSupervisorNode(config) {
2603
2804
  currentAgent: "aggregator"
2604
2805
  };
2605
2806
  }
2606
- const allCompleted = state.activeAssignments.every(
2607
- (assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2608
- );
2807
+ const allCompleted = allAssignmentsCompleted(state);
2609
2808
  logger2.debug("Checking task completion", {
2610
2809
  activeAssignments: state.activeAssignments.length,
2611
2810
  completedTasks: state.completedTasks.length,
@@ -2643,6 +2842,7 @@ function createSupervisorNode(config) {
2643
2842
  reasoning: decision.reasoning,
2644
2843
  confidence: decision.confidence
2645
2844
  });
2845
+ logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2646
2846
  } else {
2647
2847
  logger2.info("Routing to multiple agents in parallel", {
2648
2848
  targetAgents,
@@ -2650,57 +2850,22 @@ function createSupervisorNode(config) {
2650
2850
  reasoning: decision.reasoning,
2651
2851
  confidence: decision.confidence
2652
2852
  });
2853
+ logger2.debug(
2854
+ `Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
2855
+ );
2653
2856
  }
2654
- if (targetAgents.length === 1) {
2655
- logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2656
- } else {
2657
- logger2.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2658
- }
2659
- const task = state.messages[state.messages.length - 1]?.content || state.input;
2660
- const assignments = targetAgents.map((workerId) => ({
2661
- id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
2662
- workerId,
2663
- task,
2664
- priority: 5,
2665
- assignedAt: Date.now()
2666
- }));
2857
+ const task = getLatestTaskContent(state);
2858
+ const assignments = createTaskAssignments(targetAgents, task);
2667
2859
  logger2.debug("Created task assignments", {
2668
2860
  assignmentCount: assignments.length,
2669
- assignments: assignments.map((a) => ({
2670
- id: a.id,
2671
- workerId: a.workerId,
2672
- taskLength: a.task.length
2861
+ assignments: assignments.map((assignment) => ({
2862
+ id: assignment.id,
2863
+ workerId: assignment.workerId,
2864
+ taskLength: assignment.task.length
2673
2865
  }))
2674
2866
  });
2675
- const messages = assignments.map((assignment) => ({
2676
- id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2677
- from: "supervisor",
2678
- to: [assignment.workerId],
2679
- type: "task_assignment",
2680
- content: assignment.task,
2681
- timestamp: Date.now(),
2682
- metadata: {
2683
- assignmentId: assignment.id,
2684
- priority: assignment.priority
2685
- }
2686
- }));
2687
- const updatedWorkers = { ...state.workers };
2688
- for (const assignment of assignments) {
2689
- const worker = updatedWorkers[assignment.workerId];
2690
- if (!worker) {
2691
- logger2.error("Worker not found in state", {
2692
- workerId: assignment.workerId,
2693
- availableWorkers: Object.keys(updatedWorkers)
2694
- });
2695
- throw new Error(
2696
- `Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
2697
- );
2698
- }
2699
- updatedWorkers[assignment.workerId] = {
2700
- ...worker,
2701
- currentWorkload: worker.currentWorkload + 1
2702
- };
2703
- }
2867
+ const messages = createAssignmentMessages(assignments);
2868
+ const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
2704
2869
  logger2.info("Supervisor routing complete", {
2705
2870
  currentAgent: targetAgents.join(","),
2706
2871
  status: "executing",
@@ -2710,41 +2875,187 @@ function createSupervisorNode(config) {
2710
2875
  });
2711
2876
  return {
2712
2877
  currentAgent: targetAgents.join(","),
2713
- // Store all agents (for backward compat)
2714
2878
  status: "executing",
2715
2879
  routingHistory: [decision],
2716
2880
  activeAssignments: assignments,
2717
- // Multiple assignments for parallel execution!
2718
2881
  messages,
2719
2882
  workers: updatedWorkers,
2720
- // Include updated workload
2721
- // Add 1 to iteration counter (uses additive reducer)
2722
2883
  iteration: 1
2723
2884
  };
2724
2885
  } catch (error) {
2886
+ const errorMessage = handleNodeError(error, "supervisor", false);
2725
2887
  logger2.error("Supervisor node error", {
2726
- error: error instanceof Error ? error.message : String(error),
2888
+ error: errorMessage,
2727
2889
  ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2728
2890
  iteration: state.iteration
2729
2891
  });
2730
2892
  return {
2731
2893
  status: "failed",
2732
- error: error instanceof Error ? error.message : "Unknown error in supervisor"
2894
+ error: errorMessage
2733
2895
  };
2734
2896
  }
2735
2897
  };
2736
2898
  }
2899
+
2900
+ // src/multi-agent/nodes/worker.ts
2901
+ function buildDefaultSystemPrompt(config) {
2902
+ return `You are a specialized worker agent with the following capabilities:
2903
+ Skills: ${config.capabilities.skills.join(", ")}
2904
+ Tools: ${config.capabilities.tools.join(", ")}
2905
+
2906
+ Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
2907
+ }
2908
+ async function invokeWorkerModel(model, config, assignment) {
2909
+ const messages = createPromptMessages(
2910
+ config.systemPrompt || buildDefaultSystemPrompt(config),
2911
+ assignment.task
2912
+ );
2913
+ let modelToUse = model;
2914
+ if (config.tools && config.tools.length > 0 && model.bindTools) {
2915
+ logger2.debug("Binding tools to model", {
2916
+ workerId: config.id,
2917
+ toolCount: config.tools.length,
2918
+ toolNames: config.tools.map((tool) => tool.metadata.name)
2919
+ });
2920
+ modelToUse = model.bindTools(
2921
+ convertWorkerToolsForLangChain(config.tools)
2922
+ );
2923
+ }
2924
+ logger2.debug("Invoking LLM", { workerId: config.id });
2925
+ const response = await modelToUse.invoke(messages);
2926
+ const result = serializeModelContent(response.content);
2927
+ logger2.info("Worker task completed", {
2928
+ workerId: config.id,
2929
+ assignmentId: assignment.id,
2930
+ resultLength: result.length
2931
+ });
2932
+ logger2.debug("Worker result details", {
2933
+ workerId: config.id,
2934
+ assignmentId: assignment.id,
2935
+ resultLength: result.length
2936
+ });
2937
+ const taskResult = {
2938
+ assignmentId: assignment.id,
2939
+ workerId: config.id,
2940
+ success: true,
2941
+ result,
2942
+ completedAt: Date.now(),
2943
+ metadata: {
2944
+ skills_used: config.capabilities.skills
2945
+ }
2946
+ };
2947
+ const message = {
2948
+ id: createGeneratedId("msg"),
2949
+ from: config.id,
2950
+ to: ["supervisor"],
2951
+ type: "task_result",
2952
+ content: result,
2953
+ timestamp: Date.now(),
2954
+ metadata: {
2955
+ assignmentId: assignment.id,
2956
+ success: true
2957
+ }
2958
+ };
2959
+ return {
2960
+ completedTasks: [taskResult],
2961
+ messages: [message]
2962
+ };
2963
+ }
2964
+ function getStateWorkerOrThrow(state, workerId) {
2965
+ const currentWorker = state.workers[workerId];
2966
+ if (!currentWorker) {
2967
+ logger2.error("Attempted to decrement workload for unknown worker", {
2968
+ workerId,
2969
+ availableWorkers: Object.keys(state.workers)
2970
+ });
2971
+ throw new Error(`Worker "${workerId}" not found when decrementing workload.`);
2972
+ }
2973
+ return currentWorker;
2974
+ }
2975
+ function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
2976
+ const workloadFromWorker = workerFromExecution?.currentWorkload;
2977
+ if (typeof workloadFromWorker === "number" && Number.isFinite(workloadFromWorker)) {
2978
+ return workloadFromWorker;
2979
+ }
2980
+ if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
2981
+ return currentWorker.currentWorkload;
2982
+ }
2983
+ logger2.error("Worker workload is not a valid number; cannot decrement", {
2984
+ workerId,
2985
+ workloadFromState: currentWorker.currentWorkload,
2986
+ ...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
2987
+ });
2988
+ throw new Error(
2989
+ `Worker "${workerId}" does not have a valid numeric currentWorkload to decrement.`
2990
+ );
2991
+ }
2992
+ function mergeWorkersWithDecrement(state, workerId, executionResult) {
2993
+ const currentWorker = getStateWorkerOrThrow(state, workerId);
2994
+ const executionResultWorkers = executionResult.workers || {};
2995
+ const baseWorkers = {
2996
+ ...state.workers,
2997
+ ...executionResultWorkers
2998
+ };
2999
+ const workerFromExecution = executionResultWorkers[workerId];
3000
+ const previousWorkload = resolvePreviousWorkload(
3001
+ workerId,
3002
+ currentWorker,
3003
+ workerFromExecution
3004
+ );
3005
+ const hasWorkerOverride = typeof workerFromExecution === "object" && workerFromExecution !== null;
3006
+ const updatedWorker = hasWorkerOverride ? {
3007
+ ...currentWorker,
3008
+ ...workerFromExecution,
3009
+ currentWorkload: Math.max(0, previousWorkload - 1)
3010
+ } : {
3011
+ ...currentWorker,
3012
+ currentWorkload: Math.max(0, previousWorkload - 1)
3013
+ };
3014
+ const updatedWorkers = {
3015
+ ...baseWorkers,
3016
+ [workerId]: updatedWorker
3017
+ };
3018
+ logger2.debug("Worker workload decremented", {
3019
+ workerId,
3020
+ previousWorkload,
3021
+ newWorkload: updatedWorkers[workerId].currentWorkload,
3022
+ hadExecutionResultWorkers: !!executionResult.workers
3023
+ });
3024
+ return updatedWorkers;
3025
+ }
3026
+ function decrementWorkerOnError(state, workerId) {
3027
+ const currentWorker = getStateWorkerOrThrow(state, workerId);
3028
+ const previousWorkload = resolvePreviousWorkload(workerId, currentWorker);
3029
+ const updatedWorkers = {
3030
+ ...state.workers,
3031
+ [workerId]: {
3032
+ ...currentWorker,
3033
+ currentWorkload: Math.max(0, previousWorkload - 1)
3034
+ }
3035
+ };
3036
+ logger2.debug("Worker workload decremented (error path)", {
3037
+ workerId,
3038
+ previousWorkload,
3039
+ newWorkload: updatedWorkers[workerId].currentWorkload
3040
+ });
3041
+ return updatedWorkers;
3042
+ }
3043
+ function createErrorTaskResult(assignment, workerId, errorMessage) {
3044
+ logger2.warn("Creating error result for assignment", {
3045
+ workerId,
3046
+ assignmentId: assignment.id
3047
+ });
3048
+ return {
3049
+ assignmentId: assignment.id,
3050
+ workerId,
3051
+ success: false,
3052
+ result: "",
3053
+ error: errorMessage,
3054
+ completedAt: Date.now()
3055
+ };
3056
+ }
2737
3057
  function createWorkerNode(config) {
2738
- const {
2739
- id,
2740
- capabilities,
2741
- model,
2742
- tools = [],
2743
- systemPrompt,
2744
- verbose = false,
2745
- executeFn,
2746
- agent
2747
- } = config;
3058
+ const { id, model, executeFn, agent } = config;
2748
3059
  return async (state, runConfig) => {
2749
3060
  try {
2750
3061
  logger2.info("Worker node executing", {
@@ -2752,9 +3063,7 @@ function createWorkerNode(config) {
2752
3063
  iteration: state.iteration,
2753
3064
  activeAssignments: state.activeAssignments.length
2754
3065
  });
2755
- const currentAssignment = state.activeAssignments.find(
2756
- (assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
2757
- );
3066
+ const currentAssignment = findCurrentAssignment(state, id);
2758
3067
  if (!currentAssignment) {
2759
3068
  logger2.debug("No active assignment found for worker", {
2760
3069
  workerId: id,
@@ -2766,105 +3075,35 @@ function createWorkerNode(config) {
2766
3075
  logger2.info("Worker processing assignment", {
2767
3076
  workerId: id,
2768
3077
  assignmentId: currentAssignment.id,
2769
- taskLength: currentAssignment.task.length,
2770
- taskPreview: currentAssignment.task.substring(0, 100)
3078
+ taskLength: currentAssignment.task.length
3079
+ });
3080
+ logger2.debug("Worker assignment details", {
3081
+ workerId: id,
3082
+ assignmentId: currentAssignment.id,
3083
+ taskLength: currentAssignment.task.length
2771
3084
  });
2772
- async function executeWithLLM() {
2773
- logger2.debug("Using default LLM execution", {
2774
- workerId: id,
2775
- hasTools: tools.length > 0,
2776
- toolCount: tools.length
2777
- });
2778
- const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2779
- Skills: ${capabilities.skills.join(", ")}
2780
- Tools: ${capabilities.tools.join(", ")}
2781
-
2782
- Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
2783
- const messages = [
2784
- new SystemMessage5(systemPrompt || defaultSystemPrompt),
2785
- new HumanMessage5(currentAssignment.task)
2786
- ];
2787
- let modelToUse = model;
2788
- if (tools.length > 0 && model.bindTools) {
2789
- logger2.debug("Binding tools to model", {
2790
- workerId: id,
2791
- toolCount: tools.length,
2792
- toolNames: tools.map((t) => t.metadata.name)
2793
- });
2794
- const langchainTools = convertWorkerToolsForLangChain(tools);
2795
- modelToUse = model.bindTools(langchainTools);
2796
- }
2797
- logger2.debug("Invoking LLM", { workerId: id });
2798
- const response = await modelToUse.invoke(messages);
2799
- const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2800
- logger2.info("Worker task completed", {
2801
- workerId: id,
2802
- assignmentId: currentAssignment.id,
2803
- resultLength: result.length,
2804
- resultPreview: result.substring(0, 100)
2805
- });
2806
- const taskResult = {
2807
- assignmentId: currentAssignment.id,
2808
- workerId: id,
2809
- success: true,
2810
- result,
2811
- completedAt: Date.now(),
2812
- metadata: {
2813
- skills_used: capabilities.skills
2814
- }
2815
- };
2816
- const message = {
2817
- id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2818
- from: id,
2819
- to: ["supervisor"],
2820
- type: "task_result",
2821
- content: result,
2822
- timestamp: Date.now(),
2823
- metadata: {
2824
- assignmentId: currentAssignment.id,
2825
- success: true
2826
- }
2827
- };
2828
- return {
2829
- completedTasks: [taskResult],
2830
- messages: [message]
2831
- };
2832
- }
2833
3085
  let executionResult;
2834
3086
  if (executeFn) {
2835
3087
  logger2.debug("Using custom execution function", { workerId: id });
2836
3088
  executionResult = await executeFn(state, runConfig);
2837
3089
  } else if (agent && isReActAgent(agent)) {
2838
3090
  logger2.debug("Using ReAct agent", { workerId: id });
2839
- const wrappedFn = wrapReActAgent(id, agent, verbose);
3091
+ const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
2840
3092
  executionResult = await wrappedFn(state, runConfig);
2841
3093
  } else if (model) {
2842
- executionResult = await executeWithLLM();
3094
+ logger2.debug("Using default LLM execution", {
3095
+ workerId: id,
3096
+ hasTools: (config.tools ?? []).length > 0,
3097
+ toolCount: (config.tools ?? []).length
3098
+ });
3099
+ executionResult = await invokeWorkerModel(model, config, currentAssignment);
2843
3100
  } else {
2844
3101
  logger2.error("Worker missing required configuration", { workerId: id });
2845
3102
  throw new Error(
2846
3103
  `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2847
3104
  );
2848
3105
  }
2849
- const currentWorker = state.workers[id];
2850
- const baseWorkers = {
2851
- ...state.workers,
2852
- ...executionResult.workers || {}
2853
- };
2854
- const workerToUpdate = baseWorkers[id] || currentWorker;
2855
- const updatedWorkers = {
2856
- ...baseWorkers,
2857
- [id]: {
2858
- ...workerToUpdate,
2859
- currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
2860
- }
2861
- };
2862
- logger2.debug("Worker workload decremented", {
2863
- workerId: id,
2864
- previousWorkload: workerToUpdate.currentWorkload,
2865
- newWorkload: updatedWorkers[id].currentWorkload,
2866
- hadExecutionResultWorkers: !!executionResult.workers
2867
- });
3106
+ const updatedWorkers = mergeWorkersWithDecrement(state, id, executionResult);
2868
3107
  return {
2869
3108
  ...executionResult,
2870
3109
  workers: updatedWorkers
@@ -2875,41 +3114,29 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2875
3114
  workerId: id,
2876
3115
  error: errorMessage
2877
3116
  });
2878
- const currentWorker = state.workers[id];
2879
- const updatedWorkers = {
2880
- ...state.workers,
2881
- [id]: {
2882
- ...currentWorker,
2883
- currentWorkload: Math.max(0, currentWorker.currentWorkload - 1)
2884
- }
2885
- };
2886
- logger2.debug("Worker workload decremented (error path)", {
2887
- workerId: id,
2888
- previousWorkload: currentWorker.currentWorkload,
2889
- newWorkload: updatedWorkers[id].currentWorkload
2890
- });
3117
+ let updatedWorkers;
3118
+ try {
3119
+ updatedWorkers = decrementWorkerOnError(state, id);
3120
+ } catch (workloadError) {
3121
+ const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
3122
+ logger2.error("Worker error handling failed", {
3123
+ workerId: id,
3124
+ error: workloadErrorMessage
3125
+ });
3126
+ return {
3127
+ status: "failed",
3128
+ error: `${errorMessage}. ${workloadErrorMessage}`
3129
+ };
3130
+ }
2891
3131
  const currentAssignment = state.activeAssignments.find(
2892
3132
  (assignment) => assignment.workerId === id
2893
3133
  );
2894
3134
  if (currentAssignment) {
2895
- logger2.warn("Creating error result for assignment", {
2896
- workerId: id,
2897
- assignmentId: currentAssignment.id
2898
- });
2899
- const errorResult = {
2900
- assignmentId: currentAssignment.id,
2901
- workerId: id,
2902
- success: false,
2903
- result: "",
2904
- error: errorMessage,
2905
- completedAt: Date.now()
2906
- };
2907
3135
  return {
2908
- completedTasks: [errorResult],
3136
+ completedTasks: [createErrorTaskResult(currentAssignment, id, errorMessage)],
2909
3137
  currentAgent: "supervisor",
2910
3138
  status: "routing",
2911
3139
  workers: updatedWorkers
2912
- // Include workload update
2913
3140
  };
2914
3141
  }
2915
3142
  logger2.error("No assignment found for error handling", { workerId: id });
@@ -2917,95 +3144,6 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2917
3144
  status: "failed",
2918
3145
  error: errorMessage,
2919
3146
  workers: updatedWorkers
2920
- // Include workload update even on failure
2921
- };
2922
- }
2923
- };
2924
- }
2925
- function createAggregatorNode(config = {}) {
2926
- const {
2927
- model,
2928
- systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
2929
- aggregateFn,
2930
- verbose = false
2931
- } = config;
2932
- return async (state) => {
2933
- try {
2934
- logger2.info("Aggregator node executing", {
2935
- completedTasks: state.completedTasks.length,
2936
- successfulTasks: state.completedTasks.filter((t) => t.success).length,
2937
- failedTasks: state.completedTasks.filter((t) => !t.success).length
2938
- });
2939
- logger2.debug("Combining results from workers");
2940
- if (aggregateFn) {
2941
- logger2.debug("Using custom aggregation function");
2942
- const response2 = await aggregateFn(state);
2943
- logger2.info("Custom aggregation complete", {
2944
- responseLength: response2.length
2945
- });
2946
- return {
2947
- response: response2,
2948
- status: "completed"
2949
- };
2950
- }
2951
- if (state.completedTasks.length === 0) {
2952
- logger2.warn("No completed tasks to aggregate");
2953
- return {
2954
- response: "No tasks were completed.",
2955
- status: "completed"
2956
- };
2957
- }
2958
- if (!model) {
2959
- logger2.debug("No model provided, concatenating results");
2960
- const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2961
- logger2.info("Simple concatenation complete", {
2962
- resultLength: combinedResults.length
2963
- });
2964
- return {
2965
- response: combinedResults || "No successful results to aggregate.",
2966
- status: "completed"
2967
- };
2968
- }
2969
- logger2.debug("Using LLM for intelligent aggregation", {
2970
- taskCount: state.completedTasks.length
2971
- });
2972
- const taskResults = state.completedTasks.map((task, idx) => {
2973
- const status = task.success ? "\u2713" : "\u2717";
2974
- const result = task.success ? task.result : `Error: ${task.error}`;
2975
- return `${idx + 1}. [${status}] Worker ${task.workerId}:
2976
- ${result}`;
2977
- }).join("\n\n");
2978
- const userPrompt = `Original query: ${state.input}
2979
-
2980
- Worker results:
2981
- ${taskResults}
2982
-
2983
- Please synthesize these results into a comprehensive response that addresses the original query.`;
2984
- const messages = [
2985
- new SystemMessage5(systemPrompt),
2986
- new HumanMessage5(userPrompt)
2987
- ];
2988
- logger2.debug("Invoking aggregation LLM");
2989
- const response = await model.invoke(messages);
2990
- const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2991
- logger2.info("Aggregation complete", {
2992
- responseLength: aggregatedResponse.length,
2993
- responsePreview: aggregatedResponse.substring(0, 100)
2994
- });
2995
- logger2.debug("Aggregation complete");
2996
- return {
2997
- response: aggregatedResponse,
2998
- status: "completed"
2999
- };
3000
- } catch (error) {
3001
- logger2.error("Aggregator node error", {
3002
- error: error instanceof Error ? error.message : String(error),
3003
- ...error instanceof Error && error.stack ? { stack: error.stack } : {},
3004
- completedTasks: state.completedTasks.length
3005
- });
3006
- return {
3007
- status: "failed",
3008
- error: error instanceof Error ? error.message : "Unknown error in aggregator"
3009
3147
  };
3010
3148
  }
3011
3149
  };