@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.js CHANGED
@@ -313,9 +313,9 @@ function stringifyActionArguments(arguments_) {
313
313
  function getLatestThought(thoughts) {
314
314
  return thoughts[thoughts.length - 1]?.content ?? "";
315
315
  }
316
- function debugIfVerbose(logger4, verbose, message, data) {
316
+ function debugIfVerbose(logger5, verbose, message, data) {
317
317
  if (verbose) {
318
- logger4.debug(message, data);
318
+ logger5.debug(message, data);
319
319
  }
320
320
  }
321
321
 
@@ -2293,8 +2293,9 @@ var MultiAgentStateConfig = {
2293
2293
  };
2294
2294
  var MultiAgentState = createStateAnnotation4(MultiAgentStateConfig);
2295
2295
 
2296
- // src/multi-agent/routing.ts
2296
+ // src/multi-agent/routing-internal/llm-routing.ts
2297
2297
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
2298
+ var logger = createPatternLogger("agentforge:patterns:multi-agent:routing");
2298
2299
  var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
2299
2300
 
2300
2301
  Your job is to:
@@ -2327,6 +2328,84 @@ For PARALLEL multi-worker routing:
2327
2328
  }
2328
2329
 
2329
2330
  Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
2331
+ function hasStructuredOutput(model) {
2332
+ return typeof model.withStructuredOutput === "function";
2333
+ }
2334
+ function isRecord(value) {
2335
+ return typeof value === "object" && value !== null;
2336
+ }
2337
+ function isContentCarrier(value) {
2338
+ return isRecord(value) && "content" in value;
2339
+ }
2340
+ function serializeRoutingContent(content) {
2341
+ if (typeof content === "string") {
2342
+ return content;
2343
+ }
2344
+ if (Array.isArray(content)) {
2345
+ const textParts = content.flatMap((part) => {
2346
+ if (typeof part === "string") {
2347
+ return [part];
2348
+ }
2349
+ if (isRecord(part) && typeof part.text === "string") {
2350
+ return [part.text];
2351
+ }
2352
+ return [];
2353
+ });
2354
+ if (textParts.length > 0) {
2355
+ return textParts.join("\n");
2356
+ }
2357
+ return JSON.stringify(content);
2358
+ }
2359
+ return JSON.stringify(content);
2360
+ }
2361
+ function normalizeRoutingDecisionInput(decision) {
2362
+ if (isContentCarrier(decision)) {
2363
+ return JSON.parse(serializeRoutingContent(decision.content));
2364
+ }
2365
+ return decision;
2366
+ }
2367
+ function parseRoutingDecision(decision) {
2368
+ try {
2369
+ return RoutingDecisionSchema.parse(normalizeRoutingDecisionInput(decision));
2370
+ } catch (error) {
2371
+ const message = error instanceof Error ? error.message : String(error);
2372
+ throw new Error(`Invalid LLM routing decision: ${message}`);
2373
+ }
2374
+ }
2375
+ function finalizeLlmRoutingDecision(decision) {
2376
+ const parsed = parseRoutingDecision(decision);
2377
+ return {
2378
+ targetAgent: parsed.targetAgent,
2379
+ targetAgents: parsed.targetAgents,
2380
+ reasoning: parsed.reasoning,
2381
+ confidence: parsed.confidence,
2382
+ strategy: "llm-based",
2383
+ timestamp: Date.now()
2384
+ };
2385
+ }
2386
+ async function invokeStructuredRoutingDecision(model, messages) {
2387
+ let structuredModel;
2388
+ try {
2389
+ structuredModel = model.withStructuredOutput(RoutingDecisionSchema);
2390
+ } catch (error) {
2391
+ logger.warn("Structured output unavailable, using direct routing fallback", {
2392
+ strategy: "llm-based",
2393
+ fallback: "direct-model-invoke",
2394
+ error: error instanceof Error ? error.message : String(error)
2395
+ });
2396
+ const decision2 = await model.invoke(messages);
2397
+ return finalizeLlmRoutingDecision(decision2);
2398
+ }
2399
+ const decision = await structuredModel.invoke(messages);
2400
+ return finalizeLlmRoutingDecision(decision);
2401
+ }
2402
+ async function invokeRoutingDecision(model, messages) {
2403
+ if (hasStructuredOutput(model)) {
2404
+ return invokeStructuredRoutingDecision(model, messages);
2405
+ }
2406
+ const decision = await model.invoke(messages);
2407
+ return finalizeLlmRoutingDecision(decision);
2408
+ }
2330
2409
  var llmBasedRouting = {
2331
2410
  name: "llm-based",
2332
2411
  async route(state, config) {
@@ -2352,64 +2431,119 @@ Select the best worker(s) for this task and explain your reasoning.`;
2352
2431
  new SystemMessage5(systemPrompt),
2353
2432
  new HumanMessage5(userPrompt)
2354
2433
  ];
2355
- const decision = await config.model.invoke(messages);
2434
+ return invokeRoutingDecision(config.model, messages);
2435
+ }
2436
+ };
2437
+
2438
+ // src/multi-agent/routing-internal/load-balanced-routing.ts
2439
+ function getAvailableWorkerLoads(state) {
2440
+ return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
2441
+ }
2442
+ var loadBalancedRouting = {
2443
+ name: "load-balanced",
2444
+ async route(state, _config) {
2445
+ const availableWorkers = getAvailableWorkerLoads(state);
2446
+ if (availableWorkers.length === 0) {
2447
+ throw new Error("No available workers for load-balanced routing");
2448
+ }
2449
+ const targetWorker = availableWorkers[0];
2450
+ const avgWorkload = availableWorkers.reduce((sum, worker) => sum + worker.workload, 0) / availableWorkers.length;
2451
+ const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2356
2452
  return {
2357
- targetAgent: decision.targetAgent,
2358
- targetAgents: decision.targetAgents,
2359
- reasoning: decision.reasoning,
2360
- confidence: decision.confidence,
2361
- strategy: "llm-based",
2453
+ targetAgent: targetWorker.id,
2454
+ targetAgents: null,
2455
+ reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
2456
+ confidence,
2457
+ strategy: "load-balanced",
2362
2458
  timestamp: Date.now()
2363
2459
  };
2364
2460
  }
2365
2461
  };
2462
+
2463
+ // src/multi-agent/routing-internal/worker-selection.ts
2464
+ function getAvailableWorkerIds(state) {
2465
+ return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
2466
+ }
2467
+ function getTaskContent(state) {
2468
+ const lastMessage = state.messages[state.messages.length - 1];
2469
+ return String(lastMessage?.content || state.input);
2470
+ }
2471
+
2472
+ // src/multi-agent/routing-internal/round-robin-routing.ts
2473
+ function createRoundRobinDecision(targetAgent, position, workerCount) {
2474
+ return {
2475
+ targetAgent,
2476
+ targetAgents: null,
2477
+ reasoning: `Round-robin selection: worker ${position} of ${workerCount}`,
2478
+ confidence: 1,
2479
+ strategy: "round-robin",
2480
+ timestamp: Date.now()
2481
+ };
2482
+ }
2366
2483
  var roundRobinRouting = {
2367
2484
  name: "round-robin",
2368
- async route(state, config) {
2369
- const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
2485
+ async route(state, _config) {
2486
+ const availableWorkers = getAvailableWorkerIds(state);
2370
2487
  if (availableWorkers.length === 0) {
2371
2488
  throw new Error("No available workers for round-robin routing");
2372
2489
  }
2373
2490
  const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
2374
- const targetAgent = availableWorkers[lastRoutingIndex];
2491
+ return createRoundRobinDecision(
2492
+ availableWorkers[lastRoutingIndex],
2493
+ lastRoutingIndex + 1,
2494
+ availableWorkers.length
2495
+ );
2496
+ }
2497
+ };
2498
+
2499
+ // src/multi-agent/routing-internal/rule-based-routing.ts
2500
+ var ruleBasedRouting = {
2501
+ name: "rule-based",
2502
+ async route(state, config) {
2503
+ if (!config.routingFn) {
2504
+ throw new Error("Rule-based routing requires a custom routing function");
2505
+ }
2506
+ return config.routingFn(state);
2507
+ }
2508
+ };
2509
+
2510
+ // src/multi-agent/routing-internal/skill-based-routing.ts
2511
+ function scoreWorkers(state, taskContent) {
2512
+ return Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
2513
+ const skillMatches = caps.skills.filter(
2514
+ (skill) => taskContent.includes(skill.toLowerCase())
2515
+ ).length;
2516
+ const toolMatches = caps.tools.filter(
2517
+ (tool) => taskContent.includes(tool.toLowerCase())
2518
+ ).length;
2375
2519
  return {
2376
- targetAgent,
2377
- targetAgents: null,
2378
- reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
2379
- confidence: 1,
2380
- strategy: "round-robin",
2381
- timestamp: Date.now()
2520
+ id,
2521
+ score: skillMatches * 2 + toolMatches,
2522
+ skills: caps.skills
2382
2523
  };
2524
+ }).filter((worker) => worker.score > 0).sort((a, b) => b.score - a.score);
2525
+ }
2526
+ function createFallbackDecision(state) {
2527
+ const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
2528
+ if (!firstAvailable) {
2529
+ throw new Error("No available workers for skill-based routing");
2383
2530
  }
2384
- };
2531
+ return {
2532
+ targetAgent: firstAvailable[0],
2533
+ targetAgents: null,
2534
+ reasoning: "No skill matches found, using first available worker",
2535
+ confidence: 0.5,
2536
+ strategy: "skill-based",
2537
+ timestamp: Date.now()
2538
+ };
2539
+ }
2385
2540
  var skillBasedRouting = {
2386
2541
  name: "skill-based",
2387
- async route(state, config) {
2388
- const lastMessage = state.messages[state.messages.length - 1];
2389
- const taskContent = (lastMessage?.content || state.input).toLowerCase();
2390
- const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
2391
- const skillMatches = caps.skills.filter(
2392
- (skill) => taskContent.includes(skill.toLowerCase())
2393
- ).length;
2394
- const toolMatches = caps.tools.filter(
2395
- (tool) => taskContent.includes(tool.toLowerCase())
2396
- ).length;
2397
- const score = skillMatches * 2 + toolMatches;
2398
- return { id, score, skills: caps.skills, tools: caps.tools };
2399
- }).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
2542
+ async route(state, _config) {
2543
+ const taskContent = getTaskContent(state).toLowerCase();
2544
+ const workerScores = scoreWorkers(state, taskContent);
2400
2545
  if (workerScores.length === 0) {
2401
- const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
2402
- if (!firstAvailable) {
2403
- throw new Error("No available workers for skill-based routing");
2404
- }
2405
- return {
2406
- targetAgent: firstAvailable[0],
2407
- targetAgents: null,
2408
- reasoning: "No skill matches found, using first available worker",
2409
- confidence: 0.5,
2410
- strategy: "skill-based",
2411
- timestamp: Date.now()
2412
- };
2546
+ return createFallbackDecision(state);
2413
2547
  }
2414
2548
  const best = workerScores[0];
2415
2549
  const confidence = Math.min(best.score / 5, 1);
@@ -2423,69 +2557,39 @@ var skillBasedRouting = {
2423
2557
  };
2424
2558
  }
2425
2559
  };
2426
- var loadBalancedRouting = {
2427
- name: "load-balanced",
2428
- async route(state, config) {
2429
- const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
2430
- if (availableWorkers.length === 0) {
2431
- throw new Error("No available workers for load-balanced routing");
2432
- }
2433
- const targetWorker = availableWorkers[0];
2434
- const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
2435
- const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2436
- return {
2437
- targetAgent: targetWorker.id,
2438
- targetAgents: null,
2439
- reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
2440
- confidence,
2441
- strategy: "load-balanced",
2442
- timestamp: Date.now()
2443
- };
2444
- }
2445
- };
2446
- var ruleBasedRouting = {
2447
- name: "rule-based",
2448
- async route(state, config) {
2449
- if (!config.routingFn) {
2450
- throw new Error("Rule-based routing requires a custom routing function");
2451
- }
2452
- return await config.routingFn(state);
2453
- }
2560
+
2561
+ // src/multi-agent/routing.ts
2562
+ var routingStrategies = {
2563
+ "llm-based": llmBasedRouting,
2564
+ "round-robin": roundRobinRouting,
2565
+ "skill-based": skillBasedRouting,
2566
+ "load-balanced": loadBalancedRouting,
2567
+ "rule-based": ruleBasedRouting
2454
2568
  };
2455
2569
  function getRoutingStrategy(name) {
2456
- switch (name) {
2457
- case "llm-based":
2458
- return llmBasedRouting;
2459
- case "round-robin":
2460
- return roundRobinRouting;
2461
- case "skill-based":
2462
- return skillBasedRouting;
2463
- case "load-balanced":
2464
- return loadBalancedRouting;
2465
- case "rule-based":
2466
- return ruleBasedRouting;
2467
- default:
2468
- throw new Error(`Unknown routing strategy: ${name}`);
2570
+ if (!Object.hasOwn(routingStrategies, name)) {
2571
+ throw new Error(`Unknown routing strategy: ${name}`);
2469
2572
  }
2573
+ return routingStrategies[name];
2470
2574
  }
2471
2575
 
2472
2576
  // src/multi-agent/utils.ts
2473
- var logger = createPatternLogger("agentforge:patterns:multi-agent:utils");
2474
- function isRecord(value) {
2577
+ var logger2 = createPatternLogger("agentforge:patterns:multi-agent:utils");
2578
+ function isRecord2(value) {
2475
2579
  return typeof value === "object" && value !== null && !Array.isArray(value);
2476
2580
  }
2477
2581
  function toRunnableConfig(config) {
2478
- if (!isRecord(config)) {
2582
+ if (!isRecord2(config)) {
2479
2583
  return void 0;
2480
2584
  }
2481
2585
  return config;
2482
2586
  }
2483
2587
  function getReActResultShape(value) {
2484
- if (!isRecord(value)) {
2588
+ if (!isRecord2(value)) {
2485
2589
  return {};
2486
2590
  }
2487
- const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord(message)) : void 0;
2488
- const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord(action)) : void 0;
2591
+ const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord2(message)) : void 0;
2592
+ const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord2(action)) : void 0;
2489
2593
  return {
2490
2594
  messages,
2491
2595
  actions,
@@ -2504,7 +2608,7 @@ function safeSerializeContent(content) {
2504
2608
  if (typeof part === "string") {
2505
2609
  return part;
2506
2610
  }
2507
- if (isRecord(part) && typeof part.text === "string" && part.text.length > 0) {
2611
+ if (isRecord2(part) && typeof part.text === "string" && part.text.length > 0) {
2508
2612
  return part.text;
2509
2613
  }
2510
2614
  try {
@@ -2549,17 +2653,17 @@ function isReActAgent(obj) {
2549
2653
  function wrapReActAgent(workerId, agent, verbose = false) {
2550
2654
  return async (state, config) => {
2551
2655
  try {
2552
- logger.debug("Wrapping ReAct agent execution", { workerId });
2656
+ logger2.debug("Wrapping ReAct agent execution", { workerId });
2553
2657
  const runnableConfig = toRunnableConfig(config);
2554
2658
  const currentAssignment = state.activeAssignments.find(
2555
2659
  (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2556
2660
  );
2557
2661
  if (!currentAssignment) {
2558
- logger.debug("No active assignment found", { workerId });
2662
+ logger2.debug("No active assignment found", { workerId });
2559
2663
  return {};
2560
2664
  }
2561
2665
  const task = currentAssignment.task;
2562
- logger.debug("Extracted task from assignment", {
2666
+ logger2.debug("Extracted task from assignment", {
2563
2667
  workerId,
2564
2668
  assignmentId: currentAssignment.id,
2565
2669
  taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
@@ -2572,7 +2676,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2572
2676
  thread_id: workerThreadId
2573
2677
  }
2574
2678
  } : runnableConfig;
2575
- logger.debug("Invoking ReAct agent with worker-specific config", {
2679
+ logger2.debug("Invoking ReAct agent with worker-specific config", {
2576
2680
  workerId,
2577
2681
  ...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
2578
2682
  ...workerThreadId ? { workerThreadId } : {},
@@ -2587,13 +2691,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2587
2691
  );
2588
2692
  const resultShape = getReActResultShape(result);
2589
2693
  const response = extractResponse(resultShape);
2590
- logger.debug("Received response from ReAct agent", {
2694
+ logger2.debug("Received response from ReAct agent", {
2591
2695
  workerId,
2592
2696
  responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
2593
2697
  });
2594
2698
  const uniqueTools = extractToolsUsed(resultShape);
2595
2699
  if (uniqueTools.length > 0) {
2596
- logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2700
+ logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2597
2701
  }
2598
2702
  const taskResult = {
2599
2703
  assignmentId: currentAssignment.id,
@@ -2612,7 +2716,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2612
2716
  };
2613
2717
  } catch (error) {
2614
2718
  const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
2615
- logger.error("Error in ReAct agent execution", {
2719
+ logger2.error("Error in ReAct agent execution", {
2616
2720
  workerId,
2617
2721
  error: errorMessage
2618
2722
  });
@@ -2645,7 +2749,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2645
2749
  // src/multi-agent/nodes/shared.ts
2646
2750
  import { HumanMessage as HumanMessage6, SystemMessage as SystemMessage6 } from "@langchain/core/messages";
2647
2751
  import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
2648
- var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2752
+ var logger3 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2649
2753
  function createGeneratedId(prefix) {
2650
2754
  return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
2651
2755
  }
@@ -2697,7 +2801,7 @@ function serializeModelContent(content) {
2697
2801
  const error = new Error(
2698
2802
  "Failed to serialize model content: JSON.stringify returned undefined"
2699
2803
  );
2700
- logger2.error("Model content serialization failed", {
2804
+ logger3.error("Model content serialization failed", {
2701
2805
  errorMessage: error.message,
2702
2806
  contentType: content === null ? "null" : typeof content
2703
2807
  });
@@ -2706,7 +2810,7 @@ function serializeModelContent(content) {
2706
2810
  return serialized;
2707
2811
  } catch (error) {
2708
2812
  const normalizedError = error instanceof Error ? error : new Error("Unknown error during model content serialization");
2709
- logger2.error("Model content serialization threw an error", {
2813
+ logger3.error("Model content serialization threw an error", {
2710
2814
  errorMessage: normalizedError.message,
2711
2815
  contentType: content === null ? "null" : typeof content
2712
2816
  });
@@ -2749,16 +2853,16 @@ function createAggregatorNode(config = {}) {
2749
2853
  } = config;
2750
2854
  return async (state) => {
2751
2855
  try {
2752
- logger2.info("Aggregator node executing", {
2856
+ logger3.info("Aggregator node executing", {
2753
2857
  completedTasks: state.completedTasks.length,
2754
2858
  successfulTasks: state.completedTasks.filter((task) => task.success).length,
2755
2859
  failedTasks: state.completedTasks.filter((task) => !task.success).length
2756
2860
  });
2757
- logger2.debug("Combining results from workers");
2861
+ logger3.debug("Combining results from workers");
2758
2862
  if (aggregateFn) {
2759
- logger2.debug("Using custom aggregation function");
2863
+ logger3.debug("Using custom aggregation function");
2760
2864
  const response2 = await aggregateFn(state);
2761
- logger2.info("Custom aggregation complete", {
2865
+ logger3.info("Custom aggregation complete", {
2762
2866
  responseLength: response2.length
2763
2867
  });
2764
2868
  return {
@@ -2767,16 +2871,16 @@ function createAggregatorNode(config = {}) {
2767
2871
  };
2768
2872
  }
2769
2873
  if (state.completedTasks.length === 0) {
2770
- logger2.warn("No completed tasks to aggregate");
2874
+ logger3.warn("No completed tasks to aggregate");
2771
2875
  return {
2772
2876
  response: "No tasks were completed.",
2773
2877
  status: "completed"
2774
2878
  };
2775
2879
  }
2776
2880
  if (!model) {
2777
- logger2.debug("No model provided, concatenating results");
2881
+ logger3.debug("No model provided, concatenating results");
2778
2882
  const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2779
- logger2.info("Simple concatenation complete", {
2883
+ logger3.info("Simple concatenation complete", {
2780
2884
  resultLength: combinedResults.length
2781
2885
  });
2782
2886
  return {
@@ -2784,27 +2888,27 @@ function createAggregatorNode(config = {}) {
2784
2888
  status: "completed"
2785
2889
  };
2786
2890
  }
2787
- logger2.debug("Using LLM for intelligent aggregation", {
2891
+ logger3.debug("Using LLM for intelligent aggregation", {
2788
2892
  taskCount: state.completedTasks.length
2789
2893
  });
2790
2894
  const messages = createPromptMessages(systemPrompt, createAggregationPrompt(state));
2791
- logger2.debug("Invoking aggregation LLM");
2895
+ logger3.debug("Invoking aggregation LLM");
2792
2896
  const response = await model.invoke(messages);
2793
2897
  const aggregatedResponse = serializeModelContent(response.content);
2794
- logger2.info("Aggregation complete", {
2898
+ logger3.info("Aggregation complete", {
2795
2899
  responseLength: aggregatedResponse.length
2796
2900
  });
2797
- logger2.debug("Aggregation response details", {
2901
+ logger3.debug("Aggregation response details", {
2798
2902
  responseLength: aggregatedResponse.length
2799
2903
  });
2800
- logger2.debug("Aggregation complete");
2904
+ logger3.debug("Aggregation complete");
2801
2905
  return {
2802
2906
  response: aggregatedResponse,
2803
2907
  status: "completed"
2804
2908
  };
2805
2909
  } catch (error) {
2806
2910
  const errorMessage = handleNodeError(error, "aggregator", false);
2807
- logger2.error("Aggregator node error", {
2911
+ logger3.error("Aggregator node error", {
2808
2912
  error: errorMessage,
2809
2913
  ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2810
2914
  completedTasks: state.completedTasks.length
@@ -2828,7 +2932,7 @@ function incrementAssignedWorkerLoads(state, assignments) {
2828
2932
  for (const assignment of assignments) {
2829
2933
  const worker = updatedWorkers[assignment.workerId];
2830
2934
  if (!worker) {
2831
- logger2.error("Worker not found in state", {
2935
+ logger3.error("Worker not found in state", {
2832
2936
  workerId: assignment.workerId,
2833
2937
  availableWorkers: Object.keys(updatedWorkers)
2834
2938
  });
@@ -2847,45 +2951,45 @@ function createSupervisorNode(config) {
2847
2951
  const { strategy, maxIterations = 10 } = config;
2848
2952
  return async (state) => {
2849
2953
  try {
2850
- logger2.info("Supervisor node executing", {
2954
+ logger3.info("Supervisor node executing", {
2851
2955
  iteration: state.iteration,
2852
2956
  maxIterations,
2853
2957
  activeAssignments: state.activeAssignments.length,
2854
2958
  completedTasks: state.completedTasks.length
2855
2959
  });
2856
- logger2.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
2960
+ logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
2857
2961
  if (state.iteration >= maxIterations) {
2858
- logger2.warn("Max iterations reached", {
2962
+ logger3.warn("Max iterations reached", {
2859
2963
  iteration: state.iteration,
2860
2964
  maxIterations
2861
2965
  });
2862
- logger2.debug("Max iterations reached, moving to aggregation");
2966
+ logger3.debug("Max iterations reached, moving to aggregation");
2863
2967
  return {
2864
2968
  status: "aggregating",
2865
2969
  currentAgent: "aggregator"
2866
2970
  };
2867
2971
  }
2868
2972
  const allCompleted = allAssignmentsCompleted(state);
2869
- logger2.debug("Checking task completion", {
2973
+ logger3.debug("Checking task completion", {
2870
2974
  activeAssignments: state.activeAssignments.length,
2871
2975
  completedTasks: state.completedTasks.length,
2872
2976
  allCompleted
2873
2977
  });
2874
2978
  if (allCompleted && state.activeAssignments.length > 0) {
2875
- logger2.info("All tasks completed, moving to aggregation", {
2979
+ logger3.info("All tasks completed, moving to aggregation", {
2876
2980
  completedCount: state.completedTasks.length
2877
2981
  });
2878
- logger2.debug("All tasks completed, moving to aggregation");
2982
+ logger3.debug("All tasks completed, moving to aggregation");
2879
2983
  return {
2880
2984
  status: "aggregating",
2881
2985
  currentAgent: "aggregator"
2882
2986
  };
2883
2987
  }
2884
- logger2.debug("Getting routing strategy", { strategy });
2988
+ logger3.debug("Getting routing strategy", { strategy });
2885
2989
  const routingImpl = getRoutingStrategy(strategy);
2886
2990
  const decision = await routingImpl.route(state, config);
2887
2991
  const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
2888
- logger2.debug("Target agents determined", {
2992
+ logger3.debug("Target agents determined", {
2889
2993
  targetAgents,
2890
2994
  isParallel: targetAgents.length > 1,
2891
2995
  decision: {
@@ -2894,30 +2998,30 @@ function createSupervisorNode(config) {
2894
2998
  }
2895
2999
  });
2896
3000
  if (targetAgents.length === 0) {
2897
- logger2.error("No target agents specified in routing decision");
3001
+ logger3.error("No target agents specified in routing decision");
2898
3002
  throw new Error("Routing decision must specify at least one target agent");
2899
3003
  }
2900
3004
  if (targetAgents.length === 1) {
2901
- logger2.info("Routing to single agent", {
3005
+ logger3.info("Routing to single agent", {
2902
3006
  targetAgent: targetAgents[0],
2903
3007
  reasoning: decision.reasoning,
2904
3008
  confidence: decision.confidence
2905
3009
  });
2906
- logger2.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
3010
+ logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2907
3011
  } else {
2908
- logger2.info("Routing to multiple agents in parallel", {
3012
+ logger3.info("Routing to multiple agents in parallel", {
2909
3013
  targetAgents,
2910
3014
  count: targetAgents.length,
2911
3015
  reasoning: decision.reasoning,
2912
3016
  confidence: decision.confidence
2913
3017
  });
2914
- logger2.debug(
3018
+ logger3.debug(
2915
3019
  `Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`
2916
3020
  );
2917
3021
  }
2918
3022
  const task = getLatestTaskContent(state);
2919
3023
  const assignments = createTaskAssignments(targetAgents, task);
2920
- logger2.debug("Created task assignments", {
3024
+ logger3.debug("Created task assignments", {
2921
3025
  assignmentCount: assignments.length,
2922
3026
  assignments: assignments.map((assignment) => ({
2923
3027
  id: assignment.id,
@@ -2927,7 +3031,7 @@ function createSupervisorNode(config) {
2927
3031
  });
2928
3032
  const messages = createAssignmentMessages(assignments);
2929
3033
  const updatedWorkers = incrementAssignedWorkerLoads(state, assignments);
2930
- logger2.info("Supervisor routing complete", {
3034
+ logger3.info("Supervisor routing complete", {
2931
3035
  currentAgent: targetAgents.join(","),
2932
3036
  status: "executing",
2933
3037
  assignmentCount: assignments.length,
@@ -2945,7 +3049,7 @@ function createSupervisorNode(config) {
2945
3049
  };
2946
3050
  } catch (error) {
2947
3051
  const errorMessage = handleNodeError(error, "supervisor", false);
2948
- logger2.error("Supervisor node error", {
3052
+ logger3.error("Supervisor node error", {
2949
3053
  error: errorMessage,
2950
3054
  ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2951
3055
  iteration: state.iteration
@@ -2973,7 +3077,7 @@ async function invokeWorkerModel(model, config, assignment) {
2973
3077
  );
2974
3078
  let modelToUse = model;
2975
3079
  if (config.tools && config.tools.length > 0 && model.bindTools) {
2976
- logger2.debug("Binding tools to model", {
3080
+ logger3.debug("Binding tools to model", {
2977
3081
  workerId: config.id,
2978
3082
  toolCount: config.tools.length,
2979
3083
  toolNames: config.tools.map((tool) => tool.metadata.name)
@@ -2982,15 +3086,15 @@ async function invokeWorkerModel(model, config, assignment) {
2982
3086
  convertWorkerToolsForLangChain(config.tools)
2983
3087
  );
2984
3088
  }
2985
- logger2.debug("Invoking LLM", { workerId: config.id });
3089
+ logger3.debug("Invoking LLM", { workerId: config.id });
2986
3090
  const response = await modelToUse.invoke(messages);
2987
3091
  const result = serializeModelContent(response.content);
2988
- logger2.info("Worker task completed", {
3092
+ logger3.info("Worker task completed", {
2989
3093
  workerId: config.id,
2990
3094
  assignmentId: assignment.id,
2991
3095
  resultLength: result.length
2992
3096
  });
2993
- logger2.debug("Worker result details", {
3097
+ logger3.debug("Worker result details", {
2994
3098
  workerId: config.id,
2995
3099
  assignmentId: assignment.id,
2996
3100
  resultLength: result.length
@@ -3025,7 +3129,7 @@ async function invokeWorkerModel(model, config, assignment) {
3025
3129
  function getStateWorkerOrThrow(state, workerId) {
3026
3130
  const currentWorker = state.workers[workerId];
3027
3131
  if (!currentWorker) {
3028
- logger2.error("Attempted to decrement workload for unknown worker", {
3132
+ logger3.error("Attempted to decrement workload for unknown worker", {
3029
3133
  workerId,
3030
3134
  availableWorkers: Object.keys(state.workers)
3031
3135
  });
@@ -3041,7 +3145,7 @@ function resolvePreviousWorkload(workerId, currentWorker, workerFromExecution) {
3041
3145
  if (typeof currentWorker.currentWorkload === "number" && Number.isFinite(currentWorker.currentWorkload)) {
3042
3146
  return currentWorker.currentWorkload;
3043
3147
  }
3044
- logger2.error("Worker workload is not a valid number; cannot decrement", {
3148
+ logger3.error("Worker workload is not a valid number; cannot decrement", {
3045
3149
  workerId,
3046
3150
  workloadFromState: currentWorker.currentWorkload,
3047
3151
  ...typeof workloadFromWorker === "number" ? { workloadFromWorker } : {}
@@ -3076,7 +3180,7 @@ function mergeWorkersWithDecrement(state, workerId, executionResult) {
3076
3180
  ...baseWorkers,
3077
3181
  [workerId]: updatedWorker
3078
3182
  };
3079
- logger2.debug("Worker workload decremented", {
3183
+ logger3.debug("Worker workload decremented", {
3080
3184
  workerId,
3081
3185
  previousWorkload,
3082
3186
  newWorkload: updatedWorkers[workerId].currentWorkload,
@@ -3094,7 +3198,7 @@ function decrementWorkerOnError(state, workerId) {
3094
3198
  currentWorkload: Math.max(0, previousWorkload - 1)
3095
3199
  }
3096
3200
  };
3097
- logger2.debug("Worker workload decremented (error path)", {
3201
+ logger3.debug("Worker workload decremented (error path)", {
3098
3202
  workerId,
3099
3203
  previousWorkload,
3100
3204
  newWorkload: updatedWorkers[workerId].currentWorkload
@@ -3102,7 +3206,7 @@ function decrementWorkerOnError(state, workerId) {
3102
3206
  return updatedWorkers;
3103
3207
  }
3104
3208
  function createErrorTaskResult(assignment, workerId, errorMessage) {
3105
- logger2.warn("Creating error result for assignment", {
3209
+ logger3.warn("Creating error result for assignment", {
3106
3210
  workerId,
3107
3211
  assignmentId: assignment.id
3108
3212
  });
@@ -3119,47 +3223,47 @@ function createWorkerNode(config) {
3119
3223
  const { id, model, executeFn, agent } = config;
3120
3224
  return async (state, runConfig) => {
3121
3225
  try {
3122
- logger2.info("Worker node executing", {
3226
+ logger3.info("Worker node executing", {
3123
3227
  workerId: id,
3124
3228
  iteration: state.iteration,
3125
3229
  activeAssignments: state.activeAssignments.length
3126
3230
  });
3127
3231
  const currentAssignment = findCurrentAssignment(state, id);
3128
3232
  if (!currentAssignment) {
3129
- logger2.debug("No active assignment found for worker", {
3233
+ logger3.debug("No active assignment found for worker", {
3130
3234
  workerId: id,
3131
3235
  totalActiveAssignments: state.activeAssignments.length,
3132
3236
  completedTasks: state.completedTasks.length
3133
3237
  });
3134
3238
  return {};
3135
3239
  }
3136
- logger2.info("Worker processing assignment", {
3240
+ logger3.info("Worker processing assignment", {
3137
3241
  workerId: id,
3138
3242
  assignmentId: currentAssignment.id,
3139
3243
  taskLength: currentAssignment.task.length
3140
3244
  });
3141
- logger2.debug("Worker assignment details", {
3245
+ logger3.debug("Worker assignment details", {
3142
3246
  workerId: id,
3143
3247
  assignmentId: currentAssignment.id,
3144
3248
  taskLength: currentAssignment.task.length
3145
3249
  });
3146
3250
  let executionResult;
3147
3251
  if (executeFn) {
3148
- logger2.debug("Using custom execution function", { workerId: id });
3252
+ logger3.debug("Using custom execution function", { workerId: id });
3149
3253
  executionResult = await executeFn(state, runConfig);
3150
3254
  } else if (agent && isReActAgent(agent)) {
3151
- logger2.debug("Using ReAct agent", { workerId: id });
3255
+ logger3.debug("Using ReAct agent", { workerId: id });
3152
3256
  const wrappedFn = wrapReActAgent(id, agent, config.verbose ?? false);
3153
3257
  executionResult = await wrappedFn(state, runConfig);
3154
3258
  } else if (model) {
3155
- logger2.debug("Using default LLM execution", {
3259
+ logger3.debug("Using default LLM execution", {
3156
3260
  workerId: id,
3157
3261
  hasTools: (config.tools ?? []).length > 0,
3158
3262
  toolCount: (config.tools ?? []).length
3159
3263
  });
3160
3264
  executionResult = await invokeWorkerModel(model, config, currentAssignment);
3161
3265
  } else {
3162
- logger2.error("Worker missing required configuration", { workerId: id });
3266
+ logger3.error("Worker missing required configuration", { workerId: id });
3163
3267
  throw new Error(
3164
3268
  `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
3165
3269
  );
@@ -3171,7 +3275,7 @@ function createWorkerNode(config) {
3171
3275
  };
3172
3276
  } catch (error) {
3173
3277
  const errorMessage = handleNodeError(error, `worker:${id}`, false);
3174
- logger2.error("Worker node error", {
3278
+ logger3.error("Worker node error", {
3175
3279
  workerId: id,
3176
3280
  error: errorMessage
3177
3281
  });
@@ -3180,7 +3284,7 @@ function createWorkerNode(config) {
3180
3284
  updatedWorkers = decrementWorkerOnError(state, id);
3181
3285
  } catch (workloadError) {
3182
3286
  const workloadErrorMessage = workloadError instanceof Error ? workloadError.message : String(workloadError);
3183
- logger2.error("Worker error handling failed", {
3287
+ logger3.error("Worker error handling failed", {
3184
3288
  workerId: id,
3185
3289
  error: workloadErrorMessage
3186
3290
  });
@@ -3200,7 +3304,7 @@ function createWorkerNode(config) {
3200
3304
  workers: updatedWorkers
3201
3305
  };
3202
3306
  }
3203
- logger2.error("No assignment found for error handling", { workerId: id });
3307
+ logger3.error("No assignment found for error handling", { workerId: id });
3204
3308
  return {
3205
3309
  status: "failed",
3206
3310
  error: errorMessage,
@@ -3212,7 +3316,7 @@ function createWorkerNode(config) {
3212
3316
 
3213
3317
  // src/multi-agent/agent.ts
3214
3318
  import { StateGraph as StateGraph4, END as END4 } from "@langchain/langgraph";
3215
- var logger3 = createPatternLogger("agentforge:patterns:multi-agent:system");
3319
+ var logger4 = createPatternLogger("agentforge:patterns:multi-agent:system");
3216
3320
  function getToolName(tool) {
3217
3321
  if (!tool || typeof tool !== "object") {
3218
3322
  return "unknown";
@@ -3237,13 +3341,6 @@ function createMultiAgentSystem(config) {
3237
3341
  } = config;
3238
3342
  const workflow = new StateGraph4(MultiAgentState);
3239
3343
  const supervisorConfig = { ...supervisor, maxIterations, verbose };
3240
- if (supervisor.model) {
3241
- let configuredModel = supervisor.model;
3242
- if (supervisor.strategy === "llm-based") {
3243
- configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
3244
- }
3245
- supervisorConfig.model = configuredModel;
3246
- }
3247
3344
  const supervisorNode = createSupervisorNode(supervisorConfig);
3248
3345
  workflow.addNode("supervisor", supervisorNode);
3249
3346
  const workerIds = [];
@@ -3263,46 +3360,46 @@ function createMultiAgentSystem(config) {
3263
3360
  });
3264
3361
  workflow.addNode("aggregator", aggregatorNode);
3265
3362
  const supervisorRouter = (state) => {
3266
- logger3.debug("Supervisor router executing", {
3363
+ logger4.debug("Supervisor router executing", {
3267
3364
  status: state.status,
3268
3365
  ...state.currentAgent ? { currentAgent: state.currentAgent } : {},
3269
3366
  iteration: state.iteration
3270
3367
  });
3271
3368
  if (state.status === "completed" || state.status === "failed") {
3272
- logger3.info("Supervisor router: ending workflow", { status: state.status });
3369
+ logger4.info("Supervisor router: ending workflow", { status: state.status });
3273
3370
  return END4;
3274
3371
  }
3275
3372
  if (state.status === "aggregating") {
3276
- logger3.info("Supervisor router: routing to aggregator");
3373
+ logger4.info("Supervisor router: routing to aggregator");
3277
3374
  return "aggregator";
3278
3375
  }
3279
3376
  if (state.currentAgent && state.currentAgent !== "supervisor") {
3280
3377
  if (state.currentAgent.includes(",")) {
3281
3378
  const agents = state.currentAgent.split(",").map((a) => a.trim());
3282
- logger3.info("Supervisor router: parallel routing", {
3379
+ logger4.info("Supervisor router: parallel routing", {
3283
3380
  agents,
3284
3381
  count: agents.length
3285
3382
  });
3286
3383
  return agents;
3287
3384
  }
3288
- logger3.info("Supervisor router: single agent routing", {
3385
+ logger4.info("Supervisor router: single agent routing", {
3289
3386
  targetAgent: state.currentAgent
3290
3387
  });
3291
3388
  return state.currentAgent;
3292
3389
  }
3293
- logger3.debug("Supervisor router: staying at supervisor");
3390
+ logger4.debug("Supervisor router: staying at supervisor");
3294
3391
  return "supervisor";
3295
3392
  };
3296
3393
  const workerRouter = (state) => {
3297
- logger3.debug("Worker router executing", {
3394
+ logger4.debug("Worker router executing", {
3298
3395
  iteration: state.iteration,
3299
3396
  completedTasks: state.completedTasks.length
3300
3397
  });
3301
- logger3.debug("Worker router: returning to supervisor");
3398
+ logger4.debug("Worker router: returning to supervisor");
3302
3399
  return "supervisor";
3303
3400
  };
3304
3401
  const aggregatorRouter = (state) => {
3305
- logger3.info("Aggregator router: ending workflow", {
3402
+ logger4.info("Aggregator router: ending workflow", {
3306
3403
  completedTasks: state.completedTasks.length,
3307
3404
  status: state.status
3308
3405
  });