@agentforge/patterns 0.16.1 → 0.16.3

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
@@ -2581,16 +2581,82 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2581
2581
  };
2582
2582
  }
2583
2583
 
2584
- // src/multi-agent/nodes.ts
2584
+ // src/multi-agent/nodes/shared.ts
2585
2585
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
2586
2586
  import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
2587
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
+ }
2588
2625
  function convertWorkerToolsForLangChain(tools) {
2589
2626
  const safeTools = tools ?? [];
2590
- return toLangChainTools2(
2591
- safeTools
2592
- );
2627
+ return toLangChainTools2(safeTools);
2593
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
2594
2660
  var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
2595
2661
 
2596
2662
  Your job is to:
@@ -2600,12 +2666,124 @@ Your job is to:
2600
2666
  4. Provide a clear, comprehensive final answer
2601
2667
 
2602
2668
  Be concise but thorough in your aggregation.`;
2603
- 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 = {}) {
2604
2684
  const {
2605
- strategy,
2606
- verbose = false,
2607
- maxIterations = 10
2685
+ model,
2686
+ systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
2687
+ aggregateFn
2608
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;
2609
2787
  return async (state) => {
2610
2788
  try {
2611
2789
  logger2.info("Supervisor node executing", {
@@ -2626,9 +2804,7 @@ function createSupervisorNode(config) {
2626
2804
  currentAgent: "aggregator"
2627
2805
  };
2628
2806
  }
2629
- const allCompleted = state.activeAssignments.every(
2630
- (assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2631
- );
2807
+ const allCompleted = allAssignmentsCompleted(state);
2632
2808
  logger2.debug("Checking task completion", {
2633
2809
  activeAssignments: state.activeAssignments.length,
2634
2810
  completedTasks: state.completedTasks.length,
@@ -2666,6 +2842,7 @@ function createSupervisorNode(config) {
2666
2842
  reasoning: decision.reasoning,
2667
2843
  confidence: decision.confidence
2668
2844
  });
2845
+ logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2669
2846
  } else {
2670
2847
  logger2.info("Routing to multiple agents in parallel", {
2671
2848
  targetAgents,
@@ -2673,57 +2850,22 @@ function createSupervisorNode(config) {
2673
2850
  reasoning: decision.reasoning,
2674
2851
  confidence: decision.confidence
2675
2852
  });
2853
+ logger2.debug(
2854
+ `Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
2855
+ );
2676
2856
  }
2677
- if (targetAgents.length === 1) {
2678
- logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2679
- } else {
2680
- logger2.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2681
- }
2682
- const task = state.messages[state.messages.length - 1]?.content || state.input;
2683
- const assignments = targetAgents.map((workerId) => ({
2684
- id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
2685
- workerId,
2686
- task,
2687
- priority: 5,
2688
- assignedAt: Date.now()
2689
- }));
2857
+ const task = getLatestTaskContent(state);
2858
+ const assignments = createTaskAssignments(targetAgents, task);
2690
2859
  logger2.debug("Created task assignments", {
2691
2860
  assignmentCount: assignments.length,
2692
- assignments: assignments.map((a) => ({
2693
- id: a.id,
2694
- workerId: a.workerId,
2695
- taskLength: a.task.length
2861
+ assignments: assignments.map((assignment) => ({
2862
+ id: assignment.id,
2863
+ workerId: assignment.workerId,
2864
+ taskLength: assignment.task.length
2696
2865
  }))
2697
2866
  });
2698
- const messages = assignments.map((assignment) => ({
2699
- id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2700
- from: "supervisor",
2701
- to: [assignment.workerId],
2702
- type: "task_assignment",
2703
- content: assignment.task,
2704
- timestamp: Date.now(),
2705
- metadata: {
2706
- assignmentId: assignment.id,
2707
- priority: assignment.priority
2708
- }
2709
- }));
2710
- const updatedWorkers = { ...state.workers };
2711
- for (const assignment of assignments) {
2712
- const worker = updatedWorkers[assignment.workerId];
2713
- if (!worker) {
2714
- logger2.error("Worker not found in state", {
2715
- workerId: assignment.workerId,
2716
- availableWorkers: Object.keys(updatedWorkers)
2717
- });
2718
- throw new Error(
2719
- `Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
2720
- );
2721
- }
2722
- updatedWorkers[assignment.workerId] = {
2723
- ...worker,
2724
- currentWorkload: worker.currentWorkload + 1
2725
- };
2726
- }
2867
+ const messages = createAssignmentMessages(assignments);
2868
+ const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
2727
2869
  logger2.info("Supervisor routing complete", {
2728
2870
  currentAgent: targetAgents.join(","),
2729
2871
  status: "executing",
@@ -2733,41 +2875,187 @@ function createSupervisorNode(config) {
2733
2875
  });
2734
2876
  return {
2735
2877
  currentAgent: targetAgents.join(","),
2736
- // Store all agents (for backward compat)
2737
2878
  status: "executing",
2738
2879
  routingHistory: [decision],
2739
2880
  activeAssignments: assignments,
2740
- // Multiple assignments for parallel execution!
2741
2881
  messages,
2742
2882
  workers: updatedWorkers,
2743
- // Include updated workload
2744
- // Add 1 to iteration counter (uses additive reducer)
2745
2883
  iteration: 1
2746
2884
  };
2747
2885
  } catch (error) {
2886
+ const errorMessage = handleNodeError(error, "supervisor", false);
2748
2887
  logger2.error("Supervisor node error", {
2749
- error: error instanceof Error ? error.message : String(error),
2888
+ error: errorMessage,
2750
2889
  ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2751
2890
  iteration: state.iteration
2752
2891
  });
2753
2892
  return {
2754
2893
  status: "failed",
2755
- error: error instanceof Error ? error.message : "Unknown error in supervisor"
2894
+ error: errorMessage
2756
2895
  };
2757
2896
  }
2758
2897
  };
2759
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
+ }
2760
3057
  function createWorkerNode(config) {
2761
- const {
2762
- id,
2763
- capabilities,
2764
- model,
2765
- tools = [],
2766
- systemPrompt,
2767
- verbose = false,
2768
- executeFn,
2769
- agent
2770
- } = config;
3058
+ const { id, model, executeFn, agent } = config;
2771
3059
  return async (state, runConfig) => {
2772
3060
  try {
2773
3061
  logger2.info("Worker node executing", {
@@ -2775,9 +3063,7 @@ function createWorkerNode(config) {
2775
3063
  iteration: state.iteration,
2776
3064
  activeAssignments: state.activeAssignments.length
2777
3065
  });
2778
- const currentAssignment = state.activeAssignments.find(
2779
- (assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
2780
- );
3066
+ const currentAssignment = findCurrentAssignment(state, id);
2781
3067
  if (!currentAssignment) {
2782
3068
  logger2.debug("No active assignment found for worker", {
2783
3069
  workerId: id,
@@ -2789,105 +3075,35 @@ function createWorkerNode(config) {
2789
3075
  logger2.info("Worker processing assignment", {
2790
3076
  workerId: id,
2791
3077
  assignmentId: currentAssignment.id,
2792
- taskLength: currentAssignment.task.length,
2793
- 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
2794
3084
  });
2795
- async function executeWithLLM() {
2796
- logger2.debug("Using default LLM execution", {
2797
- workerId: id,
2798
- hasTools: tools.length > 0,
2799
- toolCount: tools.length
2800
- });
2801
- const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2802
- Skills: ${capabilities.skills.join(", ")}
2803
- Tools: ${capabilities.tools.join(", ")}
2804
-
2805
- Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
2806
- const messages = [
2807
- new SystemMessage5(systemPrompt || defaultSystemPrompt),
2808
- new HumanMessage5(currentAssignment.task)
2809
- ];
2810
- let modelToUse = model;
2811
- if (tools.length > 0 && model.bindTools) {
2812
- logger2.debug("Binding tools to model", {
2813
- workerId: id,
2814
- toolCount: tools.length,
2815
- toolNames: tools.map((t) => t.metadata.name)
2816
- });
2817
- const langchainTools = convertWorkerToolsForLangChain(tools);
2818
- modelToUse = model.bindTools(langchainTools);
2819
- }
2820
- logger2.debug("Invoking LLM", { workerId: id });
2821
- const response = await modelToUse.invoke(messages);
2822
- const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2823
- logger2.info("Worker task completed", {
2824
- workerId: id,
2825
- assignmentId: currentAssignment.id,
2826
- resultLength: result.length,
2827
- resultPreview: result.substring(0, 100)
2828
- });
2829
- const taskResult = {
2830
- assignmentId: currentAssignment.id,
2831
- workerId: id,
2832
- success: true,
2833
- result,
2834
- completedAt: Date.now(),
2835
- metadata: {
2836
- skills_used: capabilities.skills
2837
- }
2838
- };
2839
- const message = {
2840
- id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2841
- from: id,
2842
- to: ["supervisor"],
2843
- type: "task_result",
2844
- content: result,
2845
- timestamp: Date.now(),
2846
- metadata: {
2847
- assignmentId: currentAssignment.id,
2848
- success: true
2849
- }
2850
- };
2851
- return {
2852
- completedTasks: [taskResult],
2853
- messages: [message]
2854
- };
2855
- }
2856
3085
  let executionResult;
2857
3086
  if (executeFn) {
2858
3087
  logger2.debug("Using custom execution function", { workerId: id });
2859
3088
  executionResult = await executeFn(state, runConfig);
2860
3089
  } else if (agent && isReActAgent(agent)) {
2861
3090
  logger2.debug("Using ReAct agent", { workerId: id });
2862
- const wrappedFn = wrapReActAgent(id, agent, verbose);
3091
+ const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
2863
3092
  executionResult = await wrappedFn(state, runConfig);
2864
3093
  } else if (model) {
2865
- 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);
2866
3100
  } else {
2867
3101
  logger2.error("Worker missing required configuration", { workerId: id });
2868
3102
  throw new Error(
2869
3103
  `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2870
3104
  );
2871
3105
  }
2872
- const currentWorker = state.workers[id];
2873
- const baseWorkers = {
2874
- ...state.workers,
2875
- ...executionResult.workers || {}
2876
- };
2877
- const workerToUpdate = baseWorkers[id] || currentWorker;
2878
- const updatedWorkers = {
2879
- ...baseWorkers,
2880
- [id]: {
2881
- ...workerToUpdate,
2882
- currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
2883
- }
2884
- };
2885
- logger2.debug("Worker workload decremented", {
2886
- workerId: id,
2887
- previousWorkload: workerToUpdate.currentWorkload,
2888
- newWorkload: updatedWorkers[id].currentWorkload,
2889
- hadExecutionResultWorkers: !!executionResult.workers
2890
- });
3106
+ const updatedWorkers = mergeWorkersWithDecrement(state, id, executionResult);
2891
3107
  return {
2892
3108
  ...executionResult,
2893
3109
  workers: updatedWorkers
@@ -2898,41 +3114,29 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2898
3114
  workerId: id,
2899
3115
  error: errorMessage
2900
3116
  });
2901
- const currentWorker = state.workers[id];
2902
- const updatedWorkers = {
2903
- ...state.workers,
2904
- [id]: {
2905
- ...currentWorker,
2906
- currentWorkload: Math.max(0, currentWorker.currentWorkload - 1)
2907
- }
2908
- };
2909
- logger2.debug("Worker workload decremented (error path)", {
2910
- workerId: id,
2911
- previousWorkload: currentWorker.currentWorkload,
2912
- newWorkload: updatedWorkers[id].currentWorkload
2913
- });
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
+ }
2914
3131
  const currentAssignment = state.activeAssignments.find(
2915
3132
  (assignment) => assignment.workerId === id
2916
3133
  );
2917
3134
  if (currentAssignment) {
2918
- logger2.warn("Creating error result for assignment", {
2919
- workerId: id,
2920
- assignmentId: currentAssignment.id
2921
- });
2922
- const errorResult = {
2923
- assignmentId: currentAssignment.id,
2924
- workerId: id,
2925
- success: false,
2926
- result: "",
2927
- error: errorMessage,
2928
- completedAt: Date.now()
2929
- };
2930
3135
  return {
2931
- completedTasks: [errorResult],
3136
+ completedTasks: [createErrorTaskResult(currentAssignment, id, errorMessage)],
2932
3137
  currentAgent: "supervisor",
2933
3138
  status: "routing",
2934
3139
  workers: updatedWorkers
2935
- // Include workload update
2936
3140
  };
2937
3141
  }
2938
3142
  logger2.error("No assignment found for error handling", { workerId: id });
@@ -2940,95 +3144,6 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2940
3144
  status: "failed",
2941
3145
  error: errorMessage,
2942
3146
  workers: updatedWorkers
2943
- // Include workload update even on failure
2944
- };
2945
- }
2946
- };
2947
- }
2948
- function createAggregatorNode(config = {}) {
2949
- const {
2950
- model,
2951
- systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
2952
- aggregateFn,
2953
- verbose = false
2954
- } = config;
2955
- return async (state) => {
2956
- try {
2957
- logger2.info("Aggregator node executing", {
2958
- completedTasks: state.completedTasks.length,
2959
- successfulTasks: state.completedTasks.filter((t) => t.success).length,
2960
- failedTasks: state.completedTasks.filter((t) => !t.success).length
2961
- });
2962
- logger2.debug("Combining results from workers");
2963
- if (aggregateFn) {
2964
- logger2.debug("Using custom aggregation function");
2965
- const response2 = await aggregateFn(state);
2966
- logger2.info("Custom aggregation complete", {
2967
- responseLength: response2.length
2968
- });
2969
- return {
2970
- response: response2,
2971
- status: "completed"
2972
- };
2973
- }
2974
- if (state.completedTasks.length === 0) {
2975
- logger2.warn("No completed tasks to aggregate");
2976
- return {
2977
- response: "No tasks were completed.",
2978
- status: "completed"
2979
- };
2980
- }
2981
- if (!model) {
2982
- logger2.debug("No model provided, concatenating results");
2983
- const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2984
- logger2.info("Simple concatenation complete", {
2985
- resultLength: combinedResults.length
2986
- });
2987
- return {
2988
- response: combinedResults || "No successful results to aggregate.",
2989
- status: "completed"
2990
- };
2991
- }
2992
- logger2.debug("Using LLM for intelligent aggregation", {
2993
- taskCount: state.completedTasks.length
2994
- });
2995
- const taskResults = state.completedTasks.map((task, idx) => {
2996
- const status = task.success ? "\u2713" : "\u2717";
2997
- const result = task.success ? task.result : `Error: ${task.error}`;
2998
- return `${idx + 1}. [${status}] Worker ${task.workerId}:
2999
- ${result}`;
3000
- }).join("\n\n");
3001
- const userPrompt = `Original query: ${state.input}
3002
-
3003
- Worker results:
3004
- ${taskResults}
3005
-
3006
- Please synthesize these results into a comprehensive response that addresses the original query.`;
3007
- const messages = [
3008
- new SystemMessage5(systemPrompt),
3009
- new HumanMessage5(userPrompt)
3010
- ];
3011
- logger2.debug("Invoking aggregation LLM");
3012
- const response = await model.invoke(messages);
3013
- const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
3014
- logger2.info("Aggregation complete", {
3015
- responseLength: aggregatedResponse.length,
3016
- responsePreview: aggregatedResponse.substring(0, 100)
3017
- });
3018
- logger2.debug("Aggregation complete");
3019
- return {
3020
- response: aggregatedResponse,
3021
- status: "completed"
3022
- };
3023
- } catch (error) {
3024
- logger2.error("Aggregator node error", {
3025
- error: error instanceof Error ? error.message : String(error),
3026
- ...error instanceof Error && error.stack ? { stack: error.stack } : {},
3027
- completedTasks: state.completedTasks.length
3028
- });
3029
- return {
3030
- status: "failed",
3031
- error: error instanceof Error ? error.message : "Unknown error in aggregator"
3032
3147
  };
3033
3148
  }
3034
3149
  };