@agentforge/patterns 0.16.31 → 0.16.33

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 CHANGED
@@ -411,9 +411,9 @@ function stringifyActionArguments(arguments_) {
411
411
  function getLatestThought(thoughts) {
412
412
  return thoughts[thoughts.length - 1]?.content ?? "";
413
413
  }
414
- function debugIfVerbose(logger4, verbose, message, data) {
414
+ function debugIfVerbose(logger5, verbose, message, data) {
415
415
  if (verbose) {
416
- logger4.debug(message, data);
416
+ logger5.debug(message, data);
417
417
  }
418
418
  }
419
419
 
@@ -2391,8 +2391,9 @@ var MultiAgentStateConfig = {
2391
2391
  };
2392
2392
  var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateConfig);
2393
2393
 
2394
- // src/multi-agent/routing.ts
2394
+ // src/multi-agent/routing-internal/llm-routing.ts
2395
2395
  var import_messages5 = require("@langchain/core/messages");
2396
+ var logger = createPatternLogger("agentforge:patterns:multi-agent:routing");
2396
2397
  var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
2397
2398
 
2398
2399
  Your job is to:
@@ -2425,6 +2426,84 @@ For PARALLEL multi-worker routing:
2425
2426
  }
2426
2427
 
2427
2428
  Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
2429
+ function hasStructuredOutput(model) {
2430
+ return typeof model.withStructuredOutput === "function";
2431
+ }
2432
+ function isRecord(value) {
2433
+ return typeof value === "object" && value !== null;
2434
+ }
2435
+ function isContentCarrier(value) {
2436
+ return isRecord(value) && "content" in value;
2437
+ }
2438
+ function serializeRoutingContent(content) {
2439
+ if (typeof content === "string") {
2440
+ return content;
2441
+ }
2442
+ if (Array.isArray(content)) {
2443
+ const textParts = content.flatMap((part) => {
2444
+ if (typeof part === "string") {
2445
+ return [part];
2446
+ }
2447
+ if (isRecord(part) && typeof part.text === "string") {
2448
+ return [part.text];
2449
+ }
2450
+ return [];
2451
+ });
2452
+ if (textParts.length > 0) {
2453
+ return textParts.join("\n");
2454
+ }
2455
+ return JSON.stringify(content);
2456
+ }
2457
+ return JSON.stringify(content);
2458
+ }
2459
+ function normalizeRoutingDecisionInput(decision) {
2460
+ if (isContentCarrier(decision)) {
2461
+ return JSON.parse(serializeRoutingContent(decision.content));
2462
+ }
2463
+ return decision;
2464
+ }
2465
+ function parseRoutingDecision(decision) {
2466
+ try {
2467
+ return RoutingDecisionSchema.parse(normalizeRoutingDecisionInput(decision));
2468
+ } catch (error) {
2469
+ const message = error instanceof Error ? error.message : String(error);
2470
+ throw new Error(`Invalid LLM routing decision: ${message}`);
2471
+ }
2472
+ }
2473
+ function finalizeLlmRoutingDecision(decision) {
2474
+ const parsed = parseRoutingDecision(decision);
2475
+ return {
2476
+ targetAgent: parsed.targetAgent,
2477
+ targetAgents: parsed.targetAgents,
2478
+ reasoning: parsed.reasoning,
2479
+ confidence: parsed.confidence,
2480
+ strategy: "llm-based",
2481
+ timestamp: Date.now()
2482
+ };
2483
+ }
2484
+ async function invokeStructuredRoutingDecision(model, messages) {
2485
+ let structuredModel;
2486
+ try {
2487
+ structuredModel = model.withStructuredOutput(RoutingDecisionSchema);
2488
+ } catch (error) {
2489
+ logger.warn("Structured output unavailable, using direct routing fallback", {
2490
+ strategy: "llm-based",
2491
+ fallback: "direct-model-invoke",
2492
+ error: error instanceof Error ? error.message : String(error)
2493
+ });
2494
+ const decision2 = await model.invoke(messages);
2495
+ return finalizeLlmRoutingDecision(decision2);
2496
+ }
2497
+ const decision = await structuredModel.invoke(messages);
2498
+ return finalizeLlmRoutingDecision(decision);
2499
+ }
2500
+ async function invokeRoutingDecision(model, messages) {
2501
+ if (hasStructuredOutput(model)) {
2502
+ return invokeStructuredRoutingDecision(model, messages);
2503
+ }
2504
+ const decision = await model.invoke(messages);
2505
+ return finalizeLlmRoutingDecision(decision);
2506
+ }
2428
2507
  var llmBasedRouting = {
2429
2508
  name: "llm-based",
2430
2509
  async route(state, config) {
@@ -2450,64 +2529,119 @@ Select the best worker(s) for this task and explain your reasoning.`;
2450
2529
  new import_messages5.SystemMessage(systemPrompt),
2451
2530
  new import_messages5.HumanMessage(userPrompt)
2452
2531
  ];
2453
- const decision = await config.model.invoke(messages);
2532
+ return invokeRoutingDecision(config.model, messages);
2533
+ }
2534
+ };
2535
+
2536
+ // src/multi-agent/routing-internal/load-balanced-routing.ts
2537
+ function getAvailableWorkerLoads(state) {
2538
+ return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
2539
+ }
2540
+ var loadBalancedRouting = {
2541
+ name: "load-balanced",
2542
+ async route(state, _config) {
2543
+ const availableWorkers = getAvailableWorkerLoads(state);
2544
+ if (availableWorkers.length === 0) {
2545
+ throw new Error("No available workers for load-balanced routing");
2546
+ }
2547
+ const targetWorker = availableWorkers[0];
2548
+ const avgWorkload = availableWorkers.reduce((sum, worker) => sum + worker.workload, 0) / availableWorkers.length;
2549
+ const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2454
2550
  return {
2455
- targetAgent: decision.targetAgent,
2456
- targetAgents: decision.targetAgents,
2457
- reasoning: decision.reasoning,
2458
- confidence: decision.confidence,
2459
- strategy: "llm-based",
2551
+ targetAgent: targetWorker.id,
2552
+ targetAgents: null,
2553
+ reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
2554
+ confidence,
2555
+ strategy: "load-balanced",
2460
2556
  timestamp: Date.now()
2461
2557
  };
2462
2558
  }
2463
2559
  };
2560
+
2561
+ // src/multi-agent/routing-internal/worker-selection.ts
2562
+ function getAvailableWorkerIds(state) {
2563
+ return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
2564
+ }
2565
+ function getTaskContent(state) {
2566
+ const lastMessage = state.messages[state.messages.length - 1];
2567
+ return String(lastMessage?.content || state.input);
2568
+ }
2569
+
2570
+ // src/multi-agent/routing-internal/round-robin-routing.ts
2571
+ function createRoundRobinDecision(targetAgent, position, workerCount) {
2572
+ return {
2573
+ targetAgent,
2574
+ targetAgents: null,
2575
+ reasoning: `Round-robin selection: worker ${position} of ${workerCount}`,
2576
+ confidence: 1,
2577
+ strategy: "round-robin",
2578
+ timestamp: Date.now()
2579
+ };
2580
+ }
2464
2581
  var roundRobinRouting = {
2465
2582
  name: "round-robin",
2466
- async route(state, config) {
2467
- const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
2583
+ async route(state, _config) {
2584
+ const availableWorkers = getAvailableWorkerIds(state);
2468
2585
  if (availableWorkers.length === 0) {
2469
2586
  throw new Error("No available workers for round-robin routing");
2470
2587
  }
2471
2588
  const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
2472
- const targetAgent = availableWorkers[lastRoutingIndex];
2589
+ return createRoundRobinDecision(
2590
+ availableWorkers[lastRoutingIndex],
2591
+ lastRoutingIndex + 1,
2592
+ availableWorkers.length
2593
+ );
2594
+ }
2595
+ };
2596
+
2597
+ // src/multi-agent/routing-internal/rule-based-routing.ts
2598
+ var ruleBasedRouting = {
2599
+ name: "rule-based",
2600
+ async route(state, config) {
2601
+ if (!config.routingFn) {
2602
+ throw new Error("Rule-based routing requires a custom routing function");
2603
+ }
2604
+ return config.routingFn(state);
2605
+ }
2606
+ };
2607
+
2608
+ // src/multi-agent/routing-internal/skill-based-routing.ts
2609
+ function scoreWorkers(state, taskContent) {
2610
+ return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
2611
+ const skillMatches = caps.skills.filter(
2612
+ (skill) => taskContent.includes(skill.toLowerCase())
2613
+ ).length;
2614
+ const toolMatches = caps.tools.filter(
2615
+ (tool) => taskContent.includes(tool.toLowerCase())
2616
+ ).length;
2473
2617
  return {
2474
- targetAgent,
2475
- targetAgents: null,
2476
- reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
2477
- confidence: 1,
2478
- strategy: "round-robin",
2479
- timestamp: Date.now()
2618
+ id,
2619
+ score: skillMatches * 2 + toolMatches,
2620
+ skills: caps.skills
2480
2621
  };
2622
+ }).filter((worker) => worker.score > 0).sort((a, b) => b.score - a.score);
2623
+ }
2624
+ function createFallbackDecision(state) {
2625
+ const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
2626
+ if (!firstAvailable) {
2627
+ throw new Error("No available workers for skill-based routing");
2481
2628
  }
2482
- };
2629
+ return {
2630
+ targetAgent: firstAvailable[0],
2631
+ targetAgents: null,
2632
+ reasoning: "No skill matches found, using first available worker",
2633
+ confidence: 0.5,
2634
+ strategy: "skill-based",
2635
+ timestamp: Date.now()
2636
+ };
2637
+ }
2483
2638
  var skillBasedRouting = {
2484
2639
  name: "skill-based",
2485
- async route(state, config) {
2486
- const lastMessage = state.messages[state.messages.length - 1];
2487
- const taskContent = (lastMessage?.content || state.input).toLowerCase();
2488
- const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
2489
- const skillMatches = caps.skills.filter(
2490
- (skill) => taskContent.includes(skill.toLowerCase())
2491
- ).length;
2492
- const toolMatches = caps.tools.filter(
2493
- (tool) => taskContent.includes(tool.toLowerCase())
2494
- ).length;
2495
- const score = skillMatches * 2 + toolMatches;
2496
- return { id, score, skills: caps.skills, tools: caps.tools };
2497
- }).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
2640
+ async route(state, _config) {
2641
+ const taskContent = getTaskContent(state).toLowerCase();
2642
+ const workerScores = scoreWorkers(state, taskContent);
2498
2643
  if (workerScores.length === 0) {
2499
- const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
2500
- if (!firstAvailable) {
2501
- throw new Error("No available workers for skill-based routing");
2502
- }
2503
- return {
2504
- targetAgent: firstAvailable[0],
2505
- targetAgents: null,
2506
- reasoning: "No skill matches found, using first available worker",
2507
- confidence: 0.5,
2508
- strategy: "skill-based",
2509
- timestamp: Date.now()
2510
- };
2644
+ return createFallbackDecision(state);
2511
2645
  }
2512
2646
  const best = workerScores[0];
2513
2647
  const confidence = Math.min(best.score / 5, 1);
@@ -2521,69 +2655,39 @@ var skillBasedRouting = {
2521
2655
  };
2522
2656
  }
2523
2657
  };
2524
- var loadBalancedRouting = {
2525
- name: "load-balanced",
2526
- async route(state, config) {
2527
- const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
2528
- if (availableWorkers.length === 0) {
2529
- throw new Error("No available workers for load-balanced routing");
2530
- }
2531
- const targetWorker = availableWorkers[0];
2532
- const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
2533
- const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2534
- return {
2535
- targetAgent: targetWorker.id,
2536
- targetAgents: null,
2537
- reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
2538
- confidence,
2539
- strategy: "load-balanced",
2540
- timestamp: Date.now()
2541
- };
2542
- }
2543
- };
2544
- var ruleBasedRouting = {
2545
- name: "rule-based",
2546
- async route(state, config) {
2547
- if (!config.routingFn) {
2548
- throw new Error("Rule-based routing requires a custom routing function");
2549
- }
2550
- return await config.routingFn(state);
2551
- }
2658
+
2659
+ // src/multi-agent/routing.ts
2660
+ var routingStrategies = {
2661
+ "llm-based": llmBasedRouting,
2662
+ "round-robin": roundRobinRouting,
2663
+ "skill-based": skillBasedRouting,
2664
+ "load-balanced": loadBalancedRouting,
2665
+ "rule-based": ruleBasedRouting
2552
2666
  };
2553
2667
  function getRoutingStrategy(name) {
2554
- switch (name) {
2555
- case "llm-based":
2556
- return llmBasedRouting;
2557
- case "round-robin":
2558
- return roundRobinRouting;
2559
- case "skill-based":
2560
- return skillBasedRouting;
2561
- case "load-balanced":
2562
- return loadBalancedRouting;
2563
- case "rule-based":
2564
- return ruleBasedRouting;
2565
- default:
2566
- throw new Error(`Unknown routing strategy: ${name}`);
2668
+ if (!Object.hasOwn(routingStrategies, name)) {
2669
+ throw new Error(`Unknown routing strategy: ${name}`);
2567
2670
  }
2671
+ return routingStrategies[name];
2568
2672
  }
2569
2673
 
2570
2674
  // src/multi-agent/utils.ts
2571
- var logger = createPatternLogger("agentforge:patterns:multi-agent:utils");
2572
- function isRecord(value) {
2675
+ var logger2 = createPatternLogger("agentforge:patterns:multi-agent:utils");
2676
+ function isRecord2(value) {
2573
2677
  return typeof value === "object" && value !== null && !Array.isArray(value);
2574
2678
  }
2575
2679
  function toRunnableConfig(config) {
2576
- if (!isRecord(config)) {
2680
+ if (!isRecord2(config)) {
2577
2681
  return void 0;
2578
2682
  }
2579
2683
  return config;
2580
2684
  }
2581
2685
  function getReActResultShape(value) {
2582
- if (!isRecord(value)) {
2686
+ if (!isRecord2(value)) {
2583
2687
  return {};
2584
2688
  }
2585
- const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord(message)) : void 0;
2586
- const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord(action)) : void 0;
2689
+ const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord2(message)) : void 0;
2690
+ const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord2(action)) : void 0;
2587
2691
  return {
2588
2692
  messages,
2589
2693
  actions,
@@ -2602,7 +2706,7 @@ function safeSerializeContent(content) {
2602
2706
  if (typeof part === "string") {
2603
2707
  return part;
2604
2708
  }
2605
- if (isRecord(part) && typeof part.text === "string" && part.text.length > 0) {
2709
+ if (isRecord2(part) && typeof part.text === "string" && part.text.length > 0) {
2606
2710
  return part.text;
2607
2711
  }
2608
2712
  try {
@@ -2647,17 +2751,17 @@ function isReActAgent(obj) {
2647
2751
  function wrapReActAgent(workerId, agent, verbose = false) {
2648
2752
  return async (state, config) => {
2649
2753
  try {
2650
- logger.debug("Wrapping ReAct agent execution", { workerId });
2754
+ logger2.debug("Wrapping ReAct agent execution", { workerId });
2651
2755
  const runnableConfig = toRunnableConfig(config);
2652
2756
  const currentAssignment = state.activeAssignments.find(
2653
2757
  (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2654
2758
  );
2655
2759
  if (!currentAssignment) {
2656
- logger.debug("No active assignment found", { workerId });
2760
+ logger2.debug("No active assignment found", { workerId });
2657
2761
  return {};
2658
2762
  }
2659
2763
  const task = currentAssignment.task;
2660
- logger.debug("Extracted task from assignment", {
2764
+ logger2.debug("Extracted task from assignment", {
2661
2765
  workerId,
2662
2766
  assignmentId: currentAssignment.id,
2663
2767
  taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
@@ -2670,7 +2774,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2670
2774
  thread_id: workerThreadId
2671
2775
  }
2672
2776
  } : runnableConfig;
2673
- logger.debug("Invoking ReAct agent with worker-specific config", {
2777
+ logger2.debug("Invoking ReAct agent with worker-specific config", {
2674
2778
  workerId,
2675
2779
  ...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
2676
2780
  ...workerThreadId ? { workerThreadId } : {},
@@ -2685,13 +2789,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2685
2789
  );
2686
2790
  const resultShape = getReActResultShape(result);
2687
2791
  const response = extractResponse(resultShape);
2688
- logger.debug("Received response from ReAct agent", {
2792
+ logger2.debug("Received response from ReAct agent", {
2689
2793
  workerId,
2690
2794
  responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
2691
2795
  });
2692
2796
  const uniqueTools = extractToolsUsed(resultShape);
2693
2797
  if (uniqueTools.length > 0) {
2694
- logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2798
+ logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2695
2799
  }
2696
2800
  const taskResult = {
2697
2801
  assignmentId: currentAssignment.id,
@@ -2710,7 +2814,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2710
2814
  };
2711
2815
  } catch (error) {
2712
2816
  const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
2713
- logger.error("Error in ReAct agent execution", {
2817
+ logger2.error("Error in ReAct agent execution", {
2714
2818
  workerId,
2715
2819
  error: errorMessage
2716
2820
  });
@@ -2743,7 +2847,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2743
2847
  // src/multi-agent/nodes/shared.ts
2744
2848
  var import_messages6 = require("@langchain/core/messages");
2745
2849
  var import_core8 = require("@agentforge/core");
2746
- var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2850
+ var logger3 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2747
2851
  function createGeneratedId(prefix) {
2748
2852
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
2749
2853
  }
@@ -2795,7 +2899,7 @@ function serializeModelContent(content) {
2795
2899
  const error = new Error(
2796
2900
  "Failed to serialize model content: JSON.stringify returned undefined"
2797
2901
  );
2798
- logger2.error("Model content serialization failed", {
2902
+ logger3.error("Model content serialization failed", {
2799
2903
  errorMessage: error.message,
2800
2904
  contentType: content === null ? "null" : typeof content
2801
2905
  });
@@ -2804,7 +2908,7 @@ function serializeModelContent(content) {
2804
2908
  return serialized;
2805
2909
  } catch (error) {
2806
2910
  const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
2807
- logger2.error("Model content serialization threw an error", {
2911
+ logger3.error("Model content serialization threw an error", {
2808
2912
  errorMessage: normalizedError.message,
2809
2913
  contentType: content === null ? "null" : typeof content
2810
2914
  });
@@ -2847,16 +2951,16 @@ function createAggregatorNode(config = {}) {
2847
2951
  } = config;
2848
2952
  return async (state) => {
2849
2953
  try {
2850
- logger2.info("Aggregator node executing", {
2954
+ logger3.info("Aggregator node executing", {
2851
2955
  completedTasks: state.completedTasks.length,
2852
2956
  successfulTasks: state.completedTasks.filter((task) => task.success).length,
2853
2957
  failedTasks: state.completedTasks.filter((task) => !task.success).length
2854
2958
  });
2855
- logger2.debug("Combining results from workers");
2959
+ logger3.debug("Combining results from workers");
2856
2960
  if (aggregateFn) {
2857
- logger2.debug("Using custom aggregation function");
2961
+ logger3.debug("Using custom aggregation function");
2858
2962
  const response2 = await aggregateFn(state);
2859
- logger2.info("Custom aggregation complete", {
2963
+ logger3.info("Custom aggregation complete", {
2860
2964
  responseLength: response2.length
2861
2965
  });
2862
2966
  return {
@@ -2865,16 +2969,16 @@ function createAggregatorNode(config = {}) {
2865
2969
  };
2866
2970
  }
2867
2971
  if (state.completedTasks.length === 0) {
2868
- logger2.warn("No completed tasks to aggregate");
2972
+ logger3.warn("No completed tasks to aggregate");
2869
2973
  return {
2870
2974
  response: "No tasks were completed.",
2871
2975
  status: "completed"
2872
2976
  };
2873
2977
  }
2874
2978
  if (!model) {
2875
- logger2.debug("No model provided, concatenating results");
2979
+ logger3.debug("No model provided, concatenating results");
2876
2980
  const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2877
- logger2.info("Simple concatenation complete", {
2981
+ logger3.info("Simple concatenation complete", {
2878
2982
  resultLength: combinedResults.length
2879
2983
  });
2880
2984
  return {
@@ -2882,27 +2986,27 @@ function createAggregatorNode(config = {}) {
2882
2986
  status: "completed"
2883
2987
  };
2884
2988
  }
2885
- logger2.debug("Using LLM for intelligent aggregation", {
2989
+ logger3.debug("Using LLM for intelligent aggregation", {
2886
2990
  taskCount: state.completedTasks.length
2887
2991
  });
2888
2992
  const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
2889
- logger2.debug("Invoking aggregation LLM");
2993
+ logger3.debug("Invoking aggregation LLM");
2890
2994
  const response = await model.invoke(messages);
2891
2995
  const aggregatedResponse = serializeModelContent(response.content);
2892
- logger2.info("Aggregation complete", {
2996
+ logger3.info("Aggregation complete", {
2893
2997
  responseLength: aggregatedResponse.length
2894
2998
  });
2895
- logger2.debug("Aggregation response details", {
2999
+ logger3.debug("Aggregation response details", {
2896
3000
  responseLength: aggregatedResponse.length
2897
3001
  });
2898
- logger2.debug("Aggregation complete");
3002
+ logger3.debug("Aggregation complete");
2899
3003
  return {
2900
3004
  response: aggregatedResponse,
2901
3005
  status: "completed"
2902
3006
  };
2903
3007
  } catch (error) {
2904
3008
  const errorMessage = handleNodeError(error, "aggregator", false);
2905
- logger2.error("Aggregator node error", {
3009
+ logger3.error("Aggregator node error", {
2906
3010
  error: errorMessage,
2907
3011
  ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2908
3012
  completedTasks: state.completedTasks.length
@@ -2926,7 +3030,7 @@ function incrementAssignedWorkerLoads(state, assignments) {
2926
3030
  for (const assignment of assignments) {
2927
3031
  const worker = updatedWorkers[assignment.workerId];
2928
3032
  if (!worker) {
2929
- logger2.error("Worker not found in state", {
3033
+ logger3.error("Worker not found in state", {
2930
3034
  workerId: assignment.workerId,
2931
3035
  availableWorkers: Object.keys(updatedWorkers)
2932
3036
  });
@@ -2945,45 +3049,45 @@ function createSupervisorNode(config) {
2945
3049
  const { strategy, maxIterations = 10 } = config;
2946
3050
  return async (state) => {
2947
3051
  try {
2948
- logger2.info("Supervisor node executing", {
3052
+ logger3.info("Supervisor node executing", {
2949
3053
  iteration: state.iteration,
2950
3054
  maxIterations,
2951
3055
  activeAssignments: state.activeAssignments.length,
2952
3056
  completedTasks: state.completedTasks.length
2953
3057
  });
2954
- logger2.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
3058
+ logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
2955
3059
  if (state.iteration >= maxIterations) {
2956
- logger2.warn("Max iterations reached", {
3060
+ logger3.warn("Max iterations reached", {
2957
3061
  iteration: state.iteration,
2958
3062
  maxIterations
2959
3063
  });
2960
- logger2.debug("Max iterations reached, moving to aggregation");
3064
+ logger3.debug("Max iterations reached, moving to aggregation");
2961
3065
  return {
2962
3066
  status: "aggregating",
2963
3067
  currentAgent: "aggregator"
2964
3068
  };
2965
3069
  }
2966
3070
  const allCompleted = allAssignmentsCompleted(state);
2967
- logger2.debug("Checking task completion", {
3071
+ logger3.debug("Checking task completion", {
2968
3072
  activeAssignments: state.activeAssignments.length,
2969
3073
  completedTasks: state.completedTasks.length,
2970
3074
  allCompleted
2971
3075
  });
2972
3076
  if (allCompleted && state.activeAssignments.length > 0) {
2973
- logger2.info("All tasks completed, moving to aggregation", {
3077
+ logger3.info("All tasks completed, moving to aggregation", {
2974
3078
  completedCount: state.completedTasks.length
2975
3079
  });
2976
- logger2.debug("All tasks completed, moving to aggregation");
3080
+ logger3.debug("All tasks completed, moving to aggregation");
2977
3081
  return {
2978
3082
  status: "aggregating",
2979
3083
  currentAgent: "aggregator"
2980
3084
  };
2981
3085
  }
2982
- logger2.debug("Getting routing strategy", { strategy });
3086
+ logger3.debug("Getting routing strategy", { strategy });
2983
3087
  const routingImpl = getRoutingStrategy(strategy);
2984
3088
  const decision = await routingImpl.route(state, config);
2985
3089
  const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
2986
- logger2.debug("Target agents determined", {
3090
+ logger3.debug("Target agents determined", {
2987
3091
  targetAgents,
2988
3092
  isParallel: targetAgents.length > 1,
2989
3093
  decision: {
@@ -2992,30 +3096,30 @@ function createSupervisorNode(config) {
2992
3096
  }
2993
3097
  });
2994
3098
  if (targetAgents.length === 0) {
2995
- logger2.error("No target agents specified in routing decision");
3099
+ logger3.error("No target agents specified in routing decision");
2996
3100
  throw new Error("Routing decision must specify at least one target agent");
2997
3101
  }
2998
3102
  if (targetAgents.length === 1) {
2999
- logger2.info("Routing to single agent", {
3103
+ logger3.info("Routing to single agent", {
3000
3104
  targetAgent: targetAgents[0],
3001
3105
  reasoning: decision.reasoning,
3002
3106
  confidence: decision.confidence
3003
3107
  });
3004
- logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
3108
+ logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
3005
3109
  } else {
3006
- logger2.info("Routing to multiple agents in parallel", {
3110
+ logger3.info("Routing to multiple agents in parallel", {
3007
3111
  targetAgents,
3008
3112
  count: targetAgents.length,
3009
3113
  reasoning: decision.reasoning,
3010
3114
  confidence: decision.confidence
3011
3115
  });
3012
- logger2.debug(
3116
+ logger3.debug(
3013
3117
  `Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
3014
3118
  );
3015
3119
  }
3016
3120
  const task = getLatestTaskContent(state);
3017
3121
  const assignments = createTaskAssignments(targetAgents, task);
3018
- logger2.debug("Created task assignments", {
3122
+ logger3.debug("Created task assignments", {
3019
3123
  assignmentCount: assignments.length,
3020
3124
  assignments: assignments.map((assignment) => ({
3021
3125
  id: assignment.id,
@@ -3025,7 +3129,7 @@ function createSupervisorNode(config) {
3025
3129
  });
3026
3130
  const messages = createAssignmentMessages(assignments);
3027
3131
  const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
3028
- logger2.info("Supervisor routing complete", {
3132
+ logger3.info("Supervisor routing complete", {
3029
3133
  currentAgent: targetAgents.join(","),
3030
3134
  status: "executing",
3031
3135
  assignmentCount: assignments.length,
@@ -3043,7 +3147,7 @@ function createSupervisorNode(config) {
3043
3147
  };
3044
3148
  } catch (error) {
3045
3149
  const errorMessage = handleNodeError(error, "supervisor", false);
3046
- logger2.error("Supervisor node error", {
3150
+ logger3.error("Supervisor node error", {
3047
3151
  error: errorMessage,
3048
3152
  ...error instanceof Error && error.stack ? { stack: error.stack } : {},
3049
3153
  iteration: state.iteration
@@ -3071,7 +3175,7 @@ async function invokeWorkerModel(model, config, assignment) {
3071
3175
  );
3072
3176
  let modelToUse = model;
3073
3177
  if (config.tools && config.tools.length > 0 && model.bindTools) {
3074
- logger2.debug("Binding tools to model", {
3178
+ logger3.debug("Binding tools to model", {
3075
3179
  workerId: config.id,
3076
3180
  toolCount: config.tools.length,
3077
3181
  toolNames: config.tools.map((tool) => tool.metadata.name)
@@ -3080,15 +3184,15 @@ async function invokeWorkerModel(model, config, assignment) {
3080
3184
  convertWorkerToolsForLangChain(config.tools)
3081
3185
  );
3082
3186
  }
3083
- logger2.debug("Invoking LLM", { workerId: config.id });
3187
+ logger3.debug("Invoking LLM", { workerId: config.id });
3084
3188
  const response = await modelToUse.invoke(messages);
3085
3189
  const result = serializeModelContent(response.content);
3086
- logger2.info("Worker task completed", {
3190
+ logger3.info("Worker task completed", {
3087
3191
  workerId: config.id,
3088
3192
  assignmentId: assignment.id,
3089
3193
  resultLength: result.length
3090
3194
  });
3091
- logger2.debug("Worker result details", {
3195
+ logger3.debug("Worker result details", {
3092
3196
  workerId: config.id,
3093
3197
  assignmentId: assignment.id,
3094
3198
  resultLength: result.length
@@ -3123,7 +3227,7 @@ async function invokeWorkerModel(model, config, assignment) {
3123
3227
  function getStateWorkerOrThrow(state, workerId) {
3124
3228
  const currentWorker = state.workers[workerId];
3125
3229
  if (!currentWorker) {
3126
- logger2.error("Attempted to decrement workload for unknown worker", {
3230
+ logger3.error("Attempted to decrement workload for unknown worker", {
3127
3231
  workerId,
3128
3232
  availableWorkers: Object.keys(state.workers)
3129
3233
  });
@@ -3139,7 +3243,7 @@ function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
3139
3243
  if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
3140
3244
  return currentWorker.currentWorkload;
3141
3245
  }
3142
- logger2.error("Worker workload is not a valid number; cannot decrement", {
3246
+ logger3.error("Worker workload is not a valid number; cannot decrement", {
3143
3247
  workerId,
3144
3248
  workloadFromState: currentWorker.currentWorkload,
3145
3249
  ...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
@@ -3174,7 +3278,7 @@ function mergeWorkersWithDecrement(state, workerId, executionResult) {
3174
3278
  ...baseWorkers,
3175
3279
  [workerId]: updatedWorker
3176
3280
  };
3177
- logger2.debug("Worker workload decremented", {
3281
+ logger3.debug("Worker workload decremented", {
3178
3282
  workerId,
3179
3283
  previousWorkload,
3180
3284
  newWorkload: updatedWorkers[workerId].currentWorkload,
@@ -3192,7 +3296,7 @@ function decrementWorkerOnError(state, workerId) {
3192
3296
  currentWorkload: Math.max(0, previousWorkload - 1)
3193
3297
  }
3194
3298
  };
3195
- logger2.debug("Worker workload decremented (error path)", {
3299
+ logger3.debug("Worker workload decremented (error path)", {
3196
3300
  workerId,
3197
3301
  previousWorkload,
3198
3302
  newWorkload: updatedWorkers[workerId].currentWorkload
@@ -3200,7 +3304,7 @@ function decrementWorkerOnError(state, workerId) {
3200
3304
  return updatedWorkers;
3201
3305
  }
3202
3306
  function createErrorTaskResult(assignment, workerId, errorMessage) {
3203
- logger2.warn("Creating error result for assignment", {
3307
+ logger3.warn("Creating error result for assignment", {
3204
3308
  workerId,
3205
3309
  assignmentId: assignment.id
3206
3310
  });
@@ -3217,47 +3321,47 @@ function createWorkerNode(config) {
3217
3321
  const { id, model, executeFn, agent } = config;
3218
3322
  return async (state, runConfig) => {
3219
3323
  try {
3220
- logger2.info("Worker node executing", {
3324
+ logger3.info("Worker node executing", {
3221
3325
  workerId: id,
3222
3326
  iteration: state.iteration,
3223
3327
  activeAssignments: state.activeAssignments.length
3224
3328
  });
3225
3329
  const currentAssignment = findCurrentAssignment(state, id);
3226
3330
  if (!currentAssignment) {
3227
- logger2.debug("No active assignment found for worker", {
3331
+ logger3.debug("No active assignment found for worker", {
3228
3332
  workerId: id,
3229
3333
  totalActiveAssignments: state.activeAssignments.length,
3230
3334
  completedTasks: state.completedTasks.length
3231
3335
  });
3232
3336
  return {};
3233
3337
  }
3234
- logger2.info("Worker processing assignment", {
3338
+ logger3.info("Worker processing assignment", {
3235
3339
  workerId: id,
3236
3340
  assignmentId: currentAssignment.id,
3237
3341
  taskLength: currentAssignment.task.length
3238
3342
  });
3239
- logger2.debug("Worker assignment details", {
3343
+ logger3.debug("Worker assignment details", {
3240
3344
  workerId: id,
3241
3345
  assignmentId: currentAssignment.id,
3242
3346
  taskLength: currentAssignment.task.length
3243
3347
  });
3244
3348
  let executionResult;
3245
3349
  if (executeFn) {
3246
- logger2.debug("Using custom execution function", { workerId: id });
3350
+ logger3.debug("Using custom execution function", { workerId: id });
3247
3351
  executionResult = await executeFn(state, runConfig);
3248
3352
  } else if (agent && isReActAgent(agent)) {
3249
- logger2.debug("Using ReAct agent", { workerId: id });
3353
+ logger3.debug("Using ReAct agent", { workerId: id });
3250
3354
  const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
3251
3355
  executionResult = await wrappedFn(state, runConfig);
3252
3356
  } else if (model) {
3253
- logger2.debug("Using default LLM execution", {
3357
+ logger3.debug("Using default LLM execution", {
3254
3358
  workerId: id,
3255
3359
  hasTools: (config.tools ?? []).length > 0,
3256
3360
  toolCount: (config.tools ?? []).length
3257
3361
  });
3258
3362
  executionResult = await invokeWorkerModel(model, config, currentAssignment);
3259
3363
  } else {
3260
- logger2.error("Worker missing required configuration", { workerId: id });
3364
+ logger3.error("Worker missing required configuration", { workerId: id });
3261
3365
  throw new Error(
3262
3366
  `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
3263
3367
  );
@@ -3269,7 +3373,7 @@ function createWorkerNode(config) {
3269
3373
  };
3270
3374
  } catch (error) {
3271
3375
  const errorMessage = handleNodeError(error, `worker:${id}`, false);
3272
- logger2.error("Worker node error", {
3376
+ logger3.error("Worker node error", {
3273
3377
  workerId: id,
3274
3378
  error: errorMessage
3275
3379
  });
@@ -3278,7 +3382,7 @@ function createWorkerNode(config) {
3278
3382
  updatedWorkers = decrementWorkerOnError(state, id);
3279
3383
  } catch (workloadError) {
3280
3384
  const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
3281
- logger2.error("Worker error handling failed", {
3385
+ logger3.error("Worker error handling failed", {
3282
3386
  workerId: id,
3283
3387
  error: workloadErrorMessage
3284
3388
  });
@@ -3298,7 +3402,7 @@ function createWorkerNode(config) {
3298
3402
  workers: updatedWorkers
3299
3403
  };
3300
3404
  }
3301
- logger2.error("No assignment found for error handling", { workerId: id });
3405
+ logger3.error("No assignment found for error handling", { workerId: id });
3302
3406
  return {
3303
3407
  status: "failed",
3304
3408
  error: errorMessage,
@@ -3310,7 +3414,7 @@ function createWorkerNode(config) {
3310
3414
 
3311
3415
  // src/multi-agent/agent.ts
3312
3416
  var import_langgraph4 = require("@langchain/langgraph");
3313
- var logger3 = createPatternLogger("agentforge:patterns:multi-agent:system");
3417
+ var logger4 = createPatternLogger("agentforge:patterns:multi-agent:system");
3314
3418
  function getToolName(tool) {
3315
3419
  if (!tool || typeof tool !== "object") {
3316
3420
  return "unknown";
@@ -3335,13 +3439,6 @@ function createMultiAgentSystem(config) {
3335
3439
  } = config;
3336
3440
  const workflow = new import_langgraph4.StateGraph(MultiAgentState);
3337
3441
  const supervisorConfig = { ...supervisor, maxIterations, verbose };
3338
- if (supervisor.model) {
3339
- let configuredModel = supervisor.model;
3340
- if (supervisor.strategy === "llm-based") {
3341
- configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
3342
- }
3343
- supervisorConfig.model = configuredModel;
3344
- }
3345
3442
  const supervisorNode = createSupervisorNode(supervisorConfig);
3346
3443
  workflow.addNode("supervisor", supervisorNode);
3347
3444
  const workerIds = [];
@@ -3361,46 +3458,46 @@ function createMultiAgentSystem(config) {
3361
3458
  });
3362
3459
  workflow.addNode("aggregator", aggregatorNode);
3363
3460
  const supervisorRouter = (state) => {
3364
- logger3.debug("Supervisor router executing", {
3461
+ logger4.debug("Supervisor router executing", {
3365
3462
  status: state.status,
3366
3463
  ...state.currentAgent ? { currentAgent: state.currentAgent } : {},
3367
3464
  iteration: state.iteration
3368
3465
  });
3369
3466
  if (state.status === "completed" || state.status === "failed") {
3370
- logger3.info("Supervisor router: ending workflow", { status: state.status });
3467
+ logger4.info("Supervisor router: ending workflow", { status: state.status });
3371
3468
  return import_langgraph4.END;
3372
3469
  }
3373
3470
  if (state.status === "aggregating") {
3374
- logger3.info("Supervisor router: routing to aggregator");
3471
+ logger4.info("Supervisor router: routing to aggregator");
3375
3472
  return "aggregator";
3376
3473
  }
3377
3474
  if (state.currentAgent && state.currentAgent !== "supervisor") {
3378
3475
  if (state.currentAgent.includes(",")) {
3379
3476
  const agents = state.currentAgent.split(",").map((a) => a.trim());
3380
- logger3.info("Supervisor router: parallel routing", {
3477
+ logger4.info("Supervisor router: parallel routing", {
3381
3478
  agents,
3382
3479
  count: agents.length
3383
3480
  });
3384
3481
  return agents;
3385
3482
  }
3386
- logger3.info("Supervisor router: single agent routing", {
3483
+ logger4.info("Supervisor router: single agent routing", {
3387
3484
  targetAgent: state.currentAgent
3388
3485
  });
3389
3486
  return state.currentAgent;
3390
3487
  }
3391
- logger3.debug("Supervisor router: staying at supervisor");
3488
+ logger4.debug("Supervisor router: staying at supervisor");
3392
3489
  return "supervisor";
3393
3490
  };
3394
3491
  const workerRouter = (state) => {
3395
- logger3.debug("Worker router executing", {
3492
+ logger4.debug("Worker router executing", {
3396
3493
  iteration: state.iteration,
3397
3494
  completedTasks: state.completedTasks.length
3398
3495
  });
3399
- logger3.debug("Worker router: returning to supervisor");
3496
+ logger4.debug("Worker router: returning to supervisor");
3400
3497
  return "supervisor";
3401
3498
  };
3402
3499
  const aggregatorRouter = (state) => {
3403
- logger3.info("Aggregator router: ending workflow", {
3500
+ logger4.info("Aggregator router: ending workflow", {
3404
3501
  completedTasks: state.completedTasks.length,
3405
3502
  status: state.status
3406
3503
  });