@agentforge/patterns 0.15.4 → 0.15.6

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
@@ -302,8 +302,8 @@ function generateToolCallCacheKey(toolName, args) {
302
302
  return `${toolName}:${sortedArgs}`;
303
303
  }
304
304
  function createPatternLogger(name, defaultLevel = "info") {
305
- const logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
306
- return (0, import_core2.createLogger)(name, { level: logLevel4 });
305
+ const logLevel = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
306
+ return (0, import_core2.createLogger)(name, { level: logLevel });
307
307
  }
308
308
  function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
309
309
  if (duplicatesSkipped === 0) {
@@ -2403,17 +2403,87 @@ function getRoutingStrategy(name) {
2403
2403
  }
2404
2404
 
2405
2405
  // src/multi-agent/utils.ts
2406
- var import_core8 = require("@agentforge/core");
2407
- var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core8.LogLevel.INFO;
2408
- var logger = (0, import_core8.createLogger)("multi-agent", { level: logLevel });
2406
+ var logger = createPatternLogger("agentforge:patterns:multi-agent:utils");
2407
+ function isRecord(value) {
2408
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2409
+ }
2410
+ function toRunnableConfig(config) {
2411
+ if (!isRecord(config)) {
2412
+ return void 0;
2413
+ }
2414
+ return config;
2415
+ }
2416
+ function getReActResultShape(value) {
2417
+ if (!isRecord(value)) {
2418
+ return {};
2419
+ }
2420
+ const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord(message)) : void 0;
2421
+ const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord(action)) : void 0;
2422
+ return {
2423
+ messages,
2424
+ actions,
2425
+ iteration: value.iteration
2426
+ };
2427
+ }
2428
+ function safeSerializeContent(content) {
2429
+ if (content === null || content === void 0) {
2430
+ return void 0;
2431
+ }
2432
+ if (typeof content === "string") {
2433
+ return content.length > 0 ? content : void 0;
2434
+ }
2435
+ if (Array.isArray(content)) {
2436
+ const parts = content.map((part) => {
2437
+ if (typeof part === "string") {
2438
+ return part;
2439
+ }
2440
+ if (isRecord(part) && typeof part.text === "string" && part.text.length > 0) {
2441
+ return part.text;
2442
+ }
2443
+ try {
2444
+ return JSON.stringify(part);
2445
+ } catch {
2446
+ return String(part);
2447
+ }
2448
+ }).filter((part) => part.length > 0);
2449
+ return parts.length > 0 ? parts.join("\n") : void 0;
2450
+ }
2451
+ try {
2452
+ const serialized = JSON.stringify(content);
2453
+ if (typeof serialized === "string" && serialized.length > 0 && serialized !== "null") {
2454
+ return serialized;
2455
+ }
2456
+ } catch {
2457
+ }
2458
+ const fallback = String(content);
2459
+ return fallback.length > 0 ? fallback : void 0;
2460
+ }
2461
+ function extractResponse(resultShape) {
2462
+ const { messages } = resultShape;
2463
+ const lastMessage = messages?.[messages.length - 1];
2464
+ const serialized = safeSerializeContent(lastMessage?.content);
2465
+ return serialized ?? "No response";
2466
+ }
2467
+ function extractToolsUsed(resultShape) {
2468
+ const { actions } = resultShape;
2469
+ const names = actions?.map((action) => action.name).filter((name) => typeof name === "string" && name.length > 0) || [];
2470
+ return [...new Set(names)];
2471
+ }
2472
+ function extractIteration(resultShape) {
2473
+ const { iteration } = resultShape;
2474
+ return typeof iteration === "number" && Number.isFinite(iteration) ? iteration : 0;
2475
+ }
2409
2476
  function isReActAgent(obj) {
2410
- return obj && typeof obj === "object" && typeof obj.invoke === "function" && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
2411
- (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
2477
+ return Boolean(
2478
+ obj && typeof obj === "object" && "invoke" in obj && typeof obj.invoke === "function" && "stream" in obj && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
2479
+ (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph")
2480
+ );
2412
2481
  }
2413
2482
  function wrapReActAgent(workerId, agent, verbose = false) {
2414
2483
  return async (state, config) => {
2415
2484
  try {
2416
2485
  logger.debug("Wrapping ReAct agent execution", { workerId });
2486
+ const runnableConfig = toRunnableConfig(config);
2417
2487
  const currentAssignment = state.activeAssignments.find(
2418
2488
  (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2419
2489
  );
@@ -2427,17 +2497,17 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2427
2497
  assignmentId: currentAssignment.id,
2428
2498
  taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
2429
2499
  });
2430
- const workerThreadId = config?.configurable?.thread_id ? `${config.configurable.thread_id}:worker:${workerId}` : void 0;
2500
+ const workerThreadId = runnableConfig?.configurable?.thread_id ? `${runnableConfig.configurable.thread_id}:worker:${workerId}` : void 0;
2431
2501
  const workerConfig = workerThreadId ? {
2432
- ...config,
2502
+ ...runnableConfig,
2433
2503
  configurable: {
2434
- ...config.configurable,
2504
+ ...runnableConfig?.configurable ?? {},
2435
2505
  thread_id: workerThreadId
2436
2506
  }
2437
- } : config;
2507
+ } : runnableConfig;
2438
2508
  logger.debug("Invoking ReAct agent with worker-specific config", {
2439
2509
  workerId,
2440
- parentThreadId: config?.configurable?.thread_id,
2510
+ parentThreadId: runnableConfig?.configurable?.thread_id,
2441
2511
  workerThreadId,
2442
2512
  hasConfig: !!workerConfig
2443
2513
  });
@@ -2448,13 +2518,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2448
2518
  workerConfig
2449
2519
  // Worker-specific config with unique thread_id
2450
2520
  );
2451
- const response = result.messages?.[result.messages.length - 1]?.content || "No response";
2521
+ const resultShape = getReActResultShape(result);
2522
+ const response = extractResponse(resultShape);
2452
2523
  logger.debug("Received response from ReAct agent", {
2453
2524
  workerId,
2454
2525
  responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
2455
2526
  });
2456
- const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
2457
- const uniqueTools = [...new Set(toolsUsed)];
2527
+ const uniqueTools = extractToolsUsed(resultShape);
2458
2528
  if (uniqueTools.length > 0) {
2459
2529
  logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2460
2530
  }
@@ -2466,7 +2536,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2466
2536
  success: true,
2467
2537
  metadata: {
2468
2538
  agent_type: "react",
2469
- iterations: result.iteration || 0,
2539
+ iterations: extractIteration(resultShape),
2470
2540
  tools_used: uniqueTools
2471
2541
  }
2472
2542
  };
@@ -2474,7 +2544,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2474
2544
  completedTasks: [taskResult]
2475
2545
  };
2476
2546
  } catch (error) {
2477
- const errorMessage = handleNodeError(error, `react-agent:${workerId}`, false);
2547
+ const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
2478
2548
  logger.error("Error in ReAct agent execution", {
2479
2549
  workerId,
2480
2550
  error: errorMessage
@@ -2507,9 +2577,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2507
2577
 
2508
2578
  // src/multi-agent/nodes.ts
2509
2579
  var import_messages5 = require("@langchain/core/messages");
2510
- var import_core9 = require("@agentforge/core");
2511
- var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || import_core9.LogLevel.INFO;
2512
- var logger2 = (0, import_core9.createLogger)("multi-agent:nodes", { level: logLevel2 });
2580
+ var import_core8 = require("@agentforge/core");
2581
+ var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2582
+ function convertWorkerToolsForLangChain(tools) {
2583
+ const safeTools = tools ?? [];
2584
+ return (0, import_core8.toLangChainTools)(
2585
+ safeTools
2586
+ );
2587
+ }
2513
2588
  var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
2514
2589
 
2515
2590
  Your job is to:
@@ -2733,7 +2808,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2733
2808
  toolCount: tools.length,
2734
2809
  toolNames: tools.map((t) => t.metadata.name)
2735
2810
  });
2736
- const langchainTools = (0, import_core9.toLangChainTools)(tools);
2811
+ const langchainTools = convertWorkerToolsForLangChain(tools);
2737
2812
  modelToUse = model.bindTools(langchainTools);
2738
2813
  }
2739
2814
  logger2.debug("Invoking LLM", { workerId: id });
@@ -2955,15 +3030,17 @@ Please synthesize these results into a comprehensive response that addresses the
2955
3030
 
2956
3031
  // src/multi-agent/agent.ts
2957
3032
  var import_langgraph4 = require("@langchain/langgraph");
2958
- var import_core10 = require("@agentforge/core");
2959
- var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || import_core10.LogLevel.INFO;
2960
- var logger3 = (0, import_core10.createLogger)("multi-agent:system", { level: logLevel3 });
3033
+ var logger3 = createPatternLogger("agentforge:patterns:multi-agent:system");
2961
3034
  function getToolName(tool) {
2962
- if (tool.metadata?.name) {
2963
- return tool.metadata.name;
3035
+ if (!tool || typeof tool !== "object") {
3036
+ return "unknown";
2964
3037
  }
2965
- if (tool.name) {
2966
- return tool.name;
3038
+ const candidate = tool;
3039
+ if (typeof candidate.metadata?.name === "string") {
3040
+ return candidate.metadata.name;
3041
+ }
3042
+ if (typeof candidate.name === "string") {
3043
+ return candidate.name;
2967
3044
  }
2968
3045
  return "unknown";
2969
3046
  }
@@ -3061,7 +3138,7 @@ function createMultiAgentSystem(config) {
3061
3138
  workflow.addConditionalEdges("aggregator", aggregatorRouter, [import_langgraph4.END]);
3062
3139
  const compiled = workflow.compile(checkpointer ? { checkpointer } : void 0);
3063
3140
  const originalInvoke = compiled.invoke.bind(compiled);
3064
- compiled.invoke = async function(input, config2) {
3141
+ compiled.invoke = (async function(input, config2) {
3065
3142
  const mergedInput = {
3066
3143
  ...input,
3067
3144
  workers: {
@@ -3069,10 +3146,13 @@ function createMultiAgentSystem(config) {
3069
3146
  ...input.workers || {}
3070
3147
  }
3071
3148
  };
3072
- return originalInvoke(mergedInput, config2);
3073
- };
3149
+ return originalInvoke(
3150
+ mergedInput,
3151
+ config2
3152
+ );
3153
+ });
3074
3154
  const originalStream = compiled.stream.bind(compiled);
3075
- compiled.stream = async function(input, config2) {
3155
+ compiled.stream = (async function(input, config2) {
3076
3156
  const mergedInput = {
3077
3157
  ...input,
3078
3158
  workers: {
@@ -3080,8 +3160,11 @@ function createMultiAgentSystem(config) {
3080
3160
  ...input.workers || {}
3081
3161
  }
3082
3162
  };
3083
- return originalStream(mergedInput, config2);
3084
- };
3163
+ return originalStream(
3164
+ mergedInput,
3165
+ config2
3166
+ );
3167
+ });
3085
3168
  return compiled;
3086
3169
  }
3087
3170
  var MultiAgentSystemBuilder = class {
@@ -3160,7 +3243,7 @@ var MultiAgentSystemBuilder = class {
3160
3243
  };
3161
3244
  function registerWorkers(system, workers) {
3162
3245
  console.warn(
3163
- "[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/agentforge/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
3246
+ "[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/TVScoundrel/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
3164
3247
  );
3165
3248
  if (!system._workerRegistry) {
3166
3249
  system._workerRegistry = {};
@@ -3175,7 +3258,7 @@ function registerWorkers(system, workers) {
3175
3258
  }
3176
3259
  if (!system._originalInvoke) {
3177
3260
  system._originalInvoke = system.invoke.bind(system);
3178
- system.invoke = async function(input, config) {
3261
+ system.invoke = (async function(input, config) {
3179
3262
  const mergedInput = {
3180
3263
  ...input,
3181
3264
  workers: {
@@ -3183,12 +3266,15 @@ function registerWorkers(system, workers) {
3183
3266
  ...input.workers || {}
3184
3267
  }
3185
3268
  };
3186
- return system._originalInvoke(mergedInput, config);
3187
- };
3269
+ return system._originalInvoke(
3270
+ mergedInput,
3271
+ config
3272
+ );
3273
+ });
3188
3274
  }
3189
3275
  if (!system._originalStream) {
3190
3276
  system._originalStream = system.stream.bind(system);
3191
- system.stream = async function(input, config) {
3277
+ system.stream = (async function(input, config) {
3192
3278
  const mergedInput = {
3193
3279
  ...input,
3194
3280
  workers: {
@@ -3196,8 +3282,11 @@ function registerWorkers(system, workers) {
3196
3282
  ...input.workers || {}
3197
3283
  }
3198
3284
  };
3199
- return system._originalStream(mergedInput, config);
3200
- };
3285
+ return system._originalStream(
3286
+ mergedInput,
3287
+ config
3288
+ );
3289
+ });
3201
3290
  }
3202
3291
  }
3203
3292
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.d.cts CHANGED
@@ -4,6 +4,7 @@ import { BaseCheckpointSaver, CompiledStateGraph } from '@langchain/langgraph';
4
4
  import { BaseChatModel } from '@langchain/core/language_models/chat_models';
5
5
  import * as _agentforge_core from '@agentforge/core';
6
6
  import { ToolRegistry, Tool, LogLevel } from '@agentforge/core';
7
+ import { RunnableConfig } from '@langchain/core/runnables';
7
8
 
8
9
  /**
9
10
  * Zod schemas for ReAct pattern state
@@ -2526,6 +2527,14 @@ type MultiAgentStateType = {
2526
2527
  * @module patterns/multi-agent/types
2527
2528
  */
2528
2529
 
2530
+ type WorkerTool = Tool<never, unknown>;
2531
+ /**
2532
+ * Runtime config passed to worker execution functions.
2533
+ *
2534
+ * Includes LangGraph's RunnableConfig while remaining open to caller-defined
2535
+ * keys used by integrations.
2536
+ */
2537
+ type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
2529
2538
  /**
2530
2539
  * Configuration for the supervisor node
2531
2540
  */
@@ -2582,7 +2591,7 @@ interface WorkerConfig {
2582
2591
  /**
2583
2592
  * Available tools for this worker
2584
2593
  */
2585
- tools?: Tool<any, any>[];
2594
+ tools?: WorkerTool[];
2586
2595
  /**
2587
2596
  * System prompt for the worker
2588
2597
  */
@@ -2600,7 +2609,7 @@ interface WorkerConfig {
2600
2609
  * The config parameter contains LangGraph runtime configuration including
2601
2610
  * thread_id for checkpointing, which is required for interrupt functionality.
2602
2611
  */
2603
- executeFn?: (state: MultiAgentStateType, config?: any) => Promise<Partial<MultiAgentStateType>>;
2612
+ executeFn?: (state: MultiAgentStateType, config?: WorkerExecutionConfig) => Promise<Partial<MultiAgentStateType>>;
2604
2613
  /**
2605
2614
  * ReAct agent instance
2606
2615
  *
@@ -2623,7 +2632,7 @@ interface WorkerConfig {
2623
2632
  * });
2624
2633
  * ```
2625
2634
  */
2626
- agent?: CompiledStateGraph<any, any>;
2635
+ agent?: CompiledStateGraph<unknown, unknown>;
2627
2636
  }
2628
2637
  /**
2629
2638
  * Configuration for the aggregator node
@@ -2737,7 +2746,7 @@ type MultiAgentNode = 'supervisor' | 'aggregator' | string;
2737
2746
  /**
2738
2747
  * Route type for multi-agent graph
2739
2748
  */
2740
- type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string;
2749
+ type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string | string[];
2741
2750
  /**
2742
2751
  * Router function type
2743
2752
  */
@@ -2985,9 +2994,9 @@ declare class MultiAgentSystemBuilder {
2985
2994
  name: string;
2986
2995
  description?: string;
2987
2996
  capabilities: string[];
2988
- tools?: any[];
2997
+ tools?: WorkerConfig['tools'];
2989
2998
  systemPrompt?: string;
2990
- model?: any;
2999
+ model?: WorkerConfig['model'];
2991
3000
  }>): this;
2992
3001
  /**
2993
3002
  * Build and compile the multi-agent system
@@ -2999,10 +3008,10 @@ declare class MultiAgentSystemBuilder {
2999
3008
  /**
3000
3009
  * Extended multi-agent system with worker registration support
3001
3010
  */
3002
- interface MultiAgentSystemWithRegistry extends CompiledStateGraph<any, any, any> {
3011
+ interface MultiAgentSystemWithRegistry extends CompiledStateGraph<MultiAgentStateType, unknown> {
3003
3012
  _workerRegistry?: Record<string, WorkerCapabilities>;
3004
- _originalInvoke?: typeof CompiledStateGraph.prototype.invoke;
3005
- _originalStream?: typeof CompiledStateGraph.prototype.stream;
3013
+ _originalInvoke?: MultiAgentSystemWithRegistry['invoke'];
3014
+ _originalStream?: MultiAgentSystemWithRegistry['stream'];
3006
3015
  }
3007
3016
  /**
3008
3017
  * Register workers with a compiled multi-agent system
@@ -3035,7 +3044,7 @@ declare function registerWorkers(system: MultiAgentSystemWithRegistry, workers:
3035
3044
  name: string;
3036
3045
  description?: string;
3037
3046
  capabilities: string[];
3038
- tools?: any[];
3047
+ tools?: WorkerConfig['tools'];
3039
3048
  systemPrompt?: string;
3040
3049
  }>): void;
3041
3050
 
package/dist/index.d.ts CHANGED
@@ -4,6 +4,7 @@ import { BaseCheckpointSaver, CompiledStateGraph } from '@langchain/langgraph';
4
4
  import { BaseChatModel } from '@langchain/core/language_models/chat_models';
5
5
  import * as _agentforge_core from '@agentforge/core';
6
6
  import { ToolRegistry, Tool, LogLevel } from '@agentforge/core';
7
+ import { RunnableConfig } from '@langchain/core/runnables';
7
8
 
8
9
  /**
9
10
  * Zod schemas for ReAct pattern state
@@ -2526,6 +2527,14 @@ type MultiAgentStateType = {
2526
2527
  * @module patterns/multi-agent/types
2527
2528
  */
2528
2529
 
2530
+ type WorkerTool = Tool<never, unknown>;
2531
+ /**
2532
+ * Runtime config passed to worker execution functions.
2533
+ *
2534
+ * Includes LangGraph's RunnableConfig while remaining open to caller-defined
2535
+ * keys used by integrations.
2536
+ */
2537
+ type WorkerExecutionConfig = RunnableConfig | Record<string, unknown>;
2529
2538
  /**
2530
2539
  * Configuration for the supervisor node
2531
2540
  */
@@ -2582,7 +2591,7 @@ interface WorkerConfig {
2582
2591
  /**
2583
2592
  * Available tools for this worker
2584
2593
  */
2585
- tools?: Tool<any, any>[];
2594
+ tools?: WorkerTool[];
2586
2595
  /**
2587
2596
  * System prompt for the worker
2588
2597
  */
@@ -2600,7 +2609,7 @@ interface WorkerConfig {
2600
2609
  * The config parameter contains LangGraph runtime configuration including
2601
2610
  * thread_id for checkpointing, which is required for interrupt functionality.
2602
2611
  */
2603
- executeFn?: (state: MultiAgentStateType, config?: any) => Promise<Partial<MultiAgentStateType>>;
2612
+ executeFn?: (state: MultiAgentStateType, config?: WorkerExecutionConfig) => Promise<Partial<MultiAgentStateType>>;
2604
2613
  /**
2605
2614
  * ReAct agent instance
2606
2615
  *
@@ -2623,7 +2632,7 @@ interface WorkerConfig {
2623
2632
  * });
2624
2633
  * ```
2625
2634
  */
2626
- agent?: CompiledStateGraph<any, any>;
2635
+ agent?: CompiledStateGraph<unknown, unknown>;
2627
2636
  }
2628
2637
  /**
2629
2638
  * Configuration for the aggregator node
@@ -2737,7 +2746,7 @@ type MultiAgentNode = 'supervisor' | 'aggregator' | string;
2737
2746
  /**
2738
2747
  * Route type for multi-agent graph
2739
2748
  */
2740
- type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string;
2749
+ type MultiAgentRoute = 'continue' | 'aggregate' | 'end' | string | string[];
2741
2750
  /**
2742
2751
  * Router function type
2743
2752
  */
@@ -2985,9 +2994,9 @@ declare class MultiAgentSystemBuilder {
2985
2994
  name: string;
2986
2995
  description?: string;
2987
2996
  capabilities: string[];
2988
- tools?: any[];
2997
+ tools?: WorkerConfig['tools'];
2989
2998
  systemPrompt?: string;
2990
- model?: any;
2999
+ model?: WorkerConfig['model'];
2991
3000
  }>): this;
2992
3001
  /**
2993
3002
  * Build and compile the multi-agent system
@@ -2999,10 +3008,10 @@ declare class MultiAgentSystemBuilder {
2999
3008
  /**
3000
3009
  * Extended multi-agent system with worker registration support
3001
3010
  */
3002
- interface MultiAgentSystemWithRegistry extends CompiledStateGraph<any, any, any> {
3011
+ interface MultiAgentSystemWithRegistry extends CompiledStateGraph<MultiAgentStateType, unknown> {
3003
3012
  _workerRegistry?: Record<string, WorkerCapabilities>;
3004
- _originalInvoke?: typeof CompiledStateGraph.prototype.invoke;
3005
- _originalStream?: typeof CompiledStateGraph.prototype.stream;
3013
+ _originalInvoke?: MultiAgentSystemWithRegistry['invoke'];
3014
+ _originalStream?: MultiAgentSystemWithRegistry['stream'];
3006
3015
  }
3007
3016
  /**
3008
3017
  * Register workers with a compiled multi-agent system
@@ -3035,7 +3044,7 @@ declare function registerWorkers(system: MultiAgentSystemWithRegistry, workers:
3035
3044
  name: string;
3036
3045
  description?: string;
3037
3046
  capabilities: string[];
3038
- tools?: any[];
3047
+ tools?: WorkerConfig['tools'];
3039
3048
  systemPrompt?: string;
3040
3049
  }>): void;
3041
3050
 
package/dist/index.js CHANGED
@@ -199,8 +199,8 @@ function generateToolCallCacheKey(toolName, args) {
199
199
  return `${toolName}:${sortedArgs}`;
200
200
  }
201
201
  function createPatternLogger(name, defaultLevel = "info") {
202
- const logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
203
- return createLogger(name, { level: logLevel4 });
202
+ const logLevel = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
203
+ return createLogger(name, { level: logLevel });
204
204
  }
205
205
  function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
206
206
  if (duplicatesSkipped === 0) {
@@ -2300,17 +2300,87 @@ function getRoutingStrategy(name) {
2300
2300
  }
2301
2301
 
2302
2302
  // src/multi-agent/utils.ts
2303
- import { createLogger as createLogger2, LogLevel } from "@agentforge/core";
2304
- var logLevel = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
2305
- var logger = createLogger2("multi-agent", { level: logLevel });
2303
+ var logger = createPatternLogger("agentforge:patterns:multi-agent:utils");
2304
+ function isRecord(value) {
2305
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2306
+ }
2307
+ function toRunnableConfig(config) {
2308
+ if (!isRecord(config)) {
2309
+ return void 0;
2310
+ }
2311
+ return config;
2312
+ }
2313
+ function getReActResultShape(value) {
2314
+ if (!isRecord(value)) {
2315
+ return {};
2316
+ }
2317
+ const messages = Array.isArray(value.messages) ? value.messages.filter((message) => isRecord(message)) : void 0;
2318
+ const actions = Array.isArray(value.actions) ? value.actions.filter((action) => isRecord(action)) : void 0;
2319
+ return {
2320
+ messages,
2321
+ actions,
2322
+ iteration: value.iteration
2323
+ };
2324
+ }
2325
+ function safeSerializeContent(content) {
2326
+ if (content === null || content === void 0) {
2327
+ return void 0;
2328
+ }
2329
+ if (typeof content === "string") {
2330
+ return content.length > 0 ? content : void 0;
2331
+ }
2332
+ if (Array.isArray(content)) {
2333
+ const parts = content.map((part) => {
2334
+ if (typeof part === "string") {
2335
+ return part;
2336
+ }
2337
+ if (isRecord(part) && typeof part.text === "string" && part.text.length > 0) {
2338
+ return part.text;
2339
+ }
2340
+ try {
2341
+ return JSON.stringify(part);
2342
+ } catch {
2343
+ return String(part);
2344
+ }
2345
+ }).filter((part) => part.length > 0);
2346
+ return parts.length > 0 ? parts.join("\n") : void 0;
2347
+ }
2348
+ try {
2349
+ const serialized = JSON.stringify(content);
2350
+ if (typeof serialized === "string" && serialized.length > 0 && serialized !== "null") {
2351
+ return serialized;
2352
+ }
2353
+ } catch {
2354
+ }
2355
+ const fallback = String(content);
2356
+ return fallback.length > 0 ? fallback : void 0;
2357
+ }
2358
+ function extractResponse(resultShape) {
2359
+ const { messages } = resultShape;
2360
+ const lastMessage = messages?.[messages.length - 1];
2361
+ const serialized = safeSerializeContent(lastMessage?.content);
2362
+ return serialized ?? "No response";
2363
+ }
2364
+ function extractToolsUsed(resultShape) {
2365
+ const { actions } = resultShape;
2366
+ const names = actions?.map((action) => action.name).filter((name) => typeof name === "string" && name.length > 0) || [];
2367
+ return [...new Set(names)];
2368
+ }
2369
+ function extractIteration(resultShape) {
2370
+ const { iteration } = resultShape;
2371
+ return typeof iteration === "number" && Number.isFinite(iteration) ? iteration : 0;
2372
+ }
2306
2373
  function isReActAgent(obj) {
2307
- return obj && typeof obj === "object" && typeof obj.invoke === "function" && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
2308
- (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
2374
+ return Boolean(
2375
+ obj && typeof obj === "object" && "invoke" in obj && typeof obj.invoke === "function" && "stream" in obj && typeof obj.stream === "function" && // Additional check to ensure it's not just any object with invoke/stream
2376
+ (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph")
2377
+ );
2309
2378
  }
2310
2379
  function wrapReActAgent(workerId, agent, verbose = false) {
2311
2380
  return async (state, config) => {
2312
2381
  try {
2313
2382
  logger.debug("Wrapping ReAct agent execution", { workerId });
2383
+ const runnableConfig = toRunnableConfig(config);
2314
2384
  const currentAssignment = state.activeAssignments.find(
2315
2385
  (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2316
2386
  );
@@ -2324,17 +2394,17 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2324
2394
  assignmentId: currentAssignment.id,
2325
2395
  taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
2326
2396
  });
2327
- const workerThreadId = config?.configurable?.thread_id ? `${config.configurable.thread_id}:worker:${workerId}` : void 0;
2397
+ const workerThreadId = runnableConfig?.configurable?.thread_id ? `${runnableConfig.configurable.thread_id}:worker:${workerId}` : void 0;
2328
2398
  const workerConfig = workerThreadId ? {
2329
- ...config,
2399
+ ...runnableConfig,
2330
2400
  configurable: {
2331
- ...config.configurable,
2401
+ ...runnableConfig?.configurable ?? {},
2332
2402
  thread_id: workerThreadId
2333
2403
  }
2334
- } : config;
2404
+ } : runnableConfig;
2335
2405
  logger.debug("Invoking ReAct agent with worker-specific config", {
2336
2406
  workerId,
2337
- parentThreadId: config?.configurable?.thread_id,
2407
+ parentThreadId: runnableConfig?.configurable?.thread_id,
2338
2408
  workerThreadId,
2339
2409
  hasConfig: !!workerConfig
2340
2410
  });
@@ -2345,13 +2415,13 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2345
2415
  workerConfig
2346
2416
  // Worker-specific config with unique thread_id
2347
2417
  );
2348
- const response = result.messages?.[result.messages.length - 1]?.content || "No response";
2418
+ const resultShape = getReActResultShape(result);
2419
+ const response = extractResponse(resultShape);
2349
2420
  logger.debug("Received response from ReAct agent", {
2350
2421
  workerId,
2351
2422
  responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
2352
2423
  });
2353
- const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
2354
- const uniqueTools = [...new Set(toolsUsed)];
2424
+ const uniqueTools = extractToolsUsed(resultShape);
2355
2425
  if (uniqueTools.length > 0) {
2356
2426
  logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2357
2427
  }
@@ -2363,7 +2433,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2363
2433
  success: true,
2364
2434
  metadata: {
2365
2435
  agent_type: "react",
2366
- iterations: result.iteration || 0,
2436
+ iterations: extractIteration(resultShape),
2367
2437
  tools_used: uniqueTools
2368
2438
  }
2369
2439
  };
@@ -2371,7 +2441,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2371
2441
  completedTasks: [taskResult]
2372
2442
  };
2373
2443
  } catch (error) {
2374
- const errorMessage = handleNodeError(error, `react-agent:${workerId}`, false);
2444
+ const errorMessage = handleNodeError(error, `react-agent:${workerId}`, verbose);
2375
2445
  logger.error("Error in ReAct agent execution", {
2376
2446
  workerId,
2377
2447
  error: errorMessage
@@ -2404,9 +2474,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2404
2474
 
2405
2475
  // src/multi-agent/nodes.ts
2406
2476
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
2407
- import { toLangChainTools as toLangChainTools2, createLogger as createLogger3, LogLevel as LogLevel2 } from "@agentforge/core";
2408
- var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel2.INFO;
2409
- var logger2 = createLogger3("multi-agent:nodes", { level: logLevel2 });
2477
+ import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
2478
+ var logger2 = createPatternLogger("agentforge:patterns:multi-agent:nodes");
2479
+ function convertWorkerToolsForLangChain(tools) {
2480
+ const safeTools = tools ?? [];
2481
+ return toLangChainTools2(
2482
+ safeTools
2483
+ );
2484
+ }
2410
2485
  var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
2411
2486
 
2412
2487
  Your job is to:
@@ -2630,7 +2705,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2630
2705
  toolCount: tools.length,
2631
2706
  toolNames: tools.map((t) => t.metadata.name)
2632
2707
  });
2633
- const langchainTools = toLangChainTools2(tools);
2708
+ const langchainTools = convertWorkerToolsForLangChain(tools);
2634
2709
  modelToUse = model.bindTools(langchainTools);
2635
2710
  }
2636
2711
  logger2.debug("Invoking LLM", { workerId: id });
@@ -2852,15 +2927,17 @@ Please synthesize these results into a comprehensive response that addresses the
2852
2927
 
2853
2928
  // src/multi-agent/agent.ts
2854
2929
  import { StateGraph as StateGraph4, END as END4 } from "@langchain/langgraph";
2855
- import { createLogger as createLogger4, LogLevel as LogLevel3 } from "@agentforge/core";
2856
- var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel3.INFO;
2857
- var logger3 = createLogger4("multi-agent:system", { level: logLevel3 });
2930
+ var logger3 = createPatternLogger("agentforge:patterns:multi-agent:system");
2858
2931
  function getToolName(tool) {
2859
- if (tool.metadata?.name) {
2860
- return tool.metadata.name;
2932
+ if (!tool || typeof tool !== "object") {
2933
+ return "unknown";
2934
+ }
2935
+ const candidate = tool;
2936
+ if (typeof candidate.metadata?.name === "string") {
2937
+ return candidate.metadata.name;
2861
2938
  }
2862
- if (tool.name) {
2863
- return tool.name;
2939
+ if (typeof candidate.name === "string") {
2940
+ return candidate.name;
2864
2941
  }
2865
2942
  return "unknown";
2866
2943
  }
@@ -2958,7 +3035,7 @@ function createMultiAgentSystem(config) {
2958
3035
  workflow.addConditionalEdges("aggregator", aggregatorRouter, [END4]);
2959
3036
  const compiled = workflow.compile(checkpointer ? { checkpointer } : void 0);
2960
3037
  const originalInvoke = compiled.invoke.bind(compiled);
2961
- compiled.invoke = async function(input, config2) {
3038
+ compiled.invoke = (async function(input, config2) {
2962
3039
  const mergedInput = {
2963
3040
  ...input,
2964
3041
  workers: {
@@ -2966,10 +3043,13 @@ function createMultiAgentSystem(config) {
2966
3043
  ...input.workers || {}
2967
3044
  }
2968
3045
  };
2969
- return originalInvoke(mergedInput, config2);
2970
- };
3046
+ return originalInvoke(
3047
+ mergedInput,
3048
+ config2
3049
+ );
3050
+ });
2971
3051
  const originalStream = compiled.stream.bind(compiled);
2972
- compiled.stream = async function(input, config2) {
3052
+ compiled.stream = (async function(input, config2) {
2973
3053
  const mergedInput = {
2974
3054
  ...input,
2975
3055
  workers: {
@@ -2977,8 +3057,11 @@ function createMultiAgentSystem(config) {
2977
3057
  ...input.workers || {}
2978
3058
  }
2979
3059
  };
2980
- return originalStream(mergedInput, config2);
2981
- };
3060
+ return originalStream(
3061
+ mergedInput,
3062
+ config2
3063
+ );
3064
+ });
2982
3065
  return compiled;
2983
3066
  }
2984
3067
  var MultiAgentSystemBuilder = class {
@@ -3057,7 +3140,7 @@ var MultiAgentSystemBuilder = class {
3057
3140
  };
3058
3141
  function registerWorkers(system, workers) {
3059
3142
  console.warn(
3060
- "[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/agentforge/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
3143
+ "[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/TVScoundrel/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
3061
3144
  );
3062
3145
  if (!system._workerRegistry) {
3063
3146
  system._workerRegistry = {};
@@ -3072,7 +3155,7 @@ function registerWorkers(system, workers) {
3072
3155
  }
3073
3156
  if (!system._originalInvoke) {
3074
3157
  system._originalInvoke = system.invoke.bind(system);
3075
- system.invoke = async function(input, config) {
3158
+ system.invoke = (async function(input, config) {
3076
3159
  const mergedInput = {
3077
3160
  ...input,
3078
3161
  workers: {
@@ -3080,12 +3163,15 @@ function registerWorkers(system, workers) {
3080
3163
  ...input.workers || {}
3081
3164
  }
3082
3165
  };
3083
- return system._originalInvoke(mergedInput, config);
3084
- };
3166
+ return system._originalInvoke(
3167
+ mergedInput,
3168
+ config
3169
+ );
3170
+ });
3085
3171
  }
3086
3172
  if (!system._originalStream) {
3087
3173
  system._originalStream = system.stream.bind(system);
3088
- system.stream = async function(input, config) {
3174
+ system.stream = (async function(input, config) {
3089
3175
  const mergedInput = {
3090
3176
  ...input,
3091
3177
  workers: {
@@ -3093,8 +3179,11 @@ function registerWorkers(system, workers) {
3093
3179
  ...input.workers || {}
3094
3180
  }
3095
3181
  };
3096
- return system._originalStream(mergedInput, config);
3097
- };
3182
+ return system._originalStream(
3183
+ mergedInput,
3184
+ config
3185
+ );
3186
+ });
3098
3187
  }
3099
3188
  }
3100
3189
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.15.4",
3
+ "version": "0.15.6",
4
4
  "description": "Production-ready agent workflow patterns for TypeScript including ReAct and Planner-Executor, built on LangGraph.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -41,13 +41,13 @@
41
41
  "url": "https://github.com/TVScoundrel/agentforge/issues"
42
42
  },
43
43
  "dependencies": {
44
- "@agentforge/core": "0.15.4",
44
+ "@agentforge/core": "0.15.6",
45
45
  "@langchain/core": "^1.1.17",
46
46
  "@langchain/langgraph": "^1.1.2",
47
47
  "zod": "^3.23.8"
48
48
  },
49
49
  "devDependencies": {
50
- "@agentforge/testing": "0.15.4",
50
+ "@agentforge/testing": "0.15.6",
51
51
  "@eslint/js": "^9.17.0",
52
52
  "@types/node": "^22.10.2",
53
53
  "eslint": "^9.17.0",