@agentforge/patterns 0.6.3 → 0.7.0

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
@@ -71,11 +71,14 @@ __export(index_exports, {
71
71
  ToolCallSchema: () => ToolCallSchema,
72
72
  ToolResultSchema: () => ToolResultSchema,
73
73
  WorkerCapabilitiesSchema: () => WorkerCapabilitiesSchema,
74
+ buildDeduplicationMetrics: () => buildDeduplicationMetrics,
75
+ calculateDeduplicationSavings: () => calculateDeduplicationSavings,
74
76
  createAggregatorNode: () => createAggregatorNode,
75
77
  createExecutorNode: () => createExecutorNode,
76
78
  createFinisherNode: () => createFinisherNode,
77
79
  createGeneratorNode: () => createGeneratorNode,
78
80
  createMultiAgentSystem: () => createMultiAgentSystem,
81
+ createPatternLogger: () => createPatternLogger,
79
82
  createPlanExecuteAgent: () => createPlanExecuteAgent,
80
83
  createPlannerNode: () => createPlannerNode,
81
84
  createReActAgent: () => createReActAgent,
@@ -87,6 +90,7 @@ __export(index_exports, {
87
90
  createReviserNode: () => createReviserNode,
88
91
  createSupervisorNode: () => createSupervisorNode,
89
92
  createWorkerNode: () => createWorkerNode,
93
+ generateToolCallCacheKey: () => generateToolCallCacheKey,
90
94
  getRoutingStrategy: () => getRoutingStrategy,
91
95
  llmBasedRouting: () => llmBasedRouting,
92
96
  loadBalancedRouting: () => loadBalancedRouting,
@@ -121,7 +125,9 @@ var ToolResultSchema = import_zod.z.object({
121
125
  toolCallId: import_zod.z.string(),
122
126
  result: import_zod.z.any(),
123
127
  error: import_zod.z.string().optional(),
124
- timestamp: import_zod.z.number().optional()
128
+ timestamp: import_zod.z.number().optional(),
129
+ isDuplicate: import_zod.z.boolean().optional()
130
+ // Flag indicating this was a duplicate tool call
125
131
  });
126
132
  var ScratchpadEntrySchema = import_zod.z.object({
127
133
  step: import_zod.z.number(),
@@ -132,15 +138,46 @@ var ScratchpadEntrySchema = import_zod.z.object({
132
138
  });
133
139
 
134
140
  // src/react/state.ts
135
- var import_zod2 = require("zod");
141
+ var import_zod3 = require("zod");
136
142
  var import_core = require("@agentforge/core");
143
+
144
+ // src/shared/state-fields.ts
145
+ var import_zod2 = require("zod");
146
+ var iterationField = {
147
+ schema: import_zod2.z.number().int().nonnegative(),
148
+ reducer: (left, right) => left + right,
149
+ default: () => 0,
150
+ description: "Current iteration number"
151
+ };
152
+ function maxIterationsField(defaultValue) {
153
+ return {
154
+ schema: import_zod2.z.number().int().positive(),
155
+ default: () => defaultValue,
156
+ description: "Maximum number of iterations allowed"
157
+ };
158
+ }
159
+ var errorField = {
160
+ schema: import_zod2.z.string().optional(),
161
+ description: "Error message if execution failed"
162
+ };
163
+ var responseField = {
164
+ schema: import_zod2.z.string().optional(),
165
+ description: "Final response after completion"
166
+ };
167
+ var inputField = {
168
+ schema: import_zod2.z.string(),
169
+ default: () => "",
170
+ description: "Original user input or query"
171
+ };
172
+
173
+ // src/react/state.ts
137
174
  var ReActStateConfig = {
138
175
  /**
139
176
  * Conversation messages
140
177
  * Accumulates all messages in the conversation
141
178
  */
142
179
  messages: {
143
- schema: import_zod2.z.array(MessageSchema),
180
+ schema: import_zod3.z.array(MessageSchema),
144
181
  reducer: (left, right) => [...left, ...right],
145
182
  default: () => [],
146
183
  description: "Conversation message history"
@@ -150,7 +187,7 @@ var ReActStateConfig = {
150
187
  * Accumulates all reasoning steps the agent takes
151
188
  */
152
189
  thoughts: {
153
- schema: import_zod2.z.array(ThoughtSchema),
190
+ schema: import_zod3.z.array(ThoughtSchema),
154
191
  reducer: (left, right) => [...left, ...right],
155
192
  default: () => [],
156
193
  description: "Agent reasoning steps"
@@ -160,7 +197,7 @@ var ReActStateConfig = {
160
197
  * Accumulates all tool calls made by the agent
161
198
  */
162
199
  actions: {
163
- schema: import_zod2.z.array(ToolCallSchema),
200
+ schema: import_zod3.z.array(ToolCallSchema),
164
201
  reducer: (left, right) => [...left, ...right],
165
202
  default: () => [],
166
203
  description: "Tool calls made by the agent"
@@ -170,7 +207,7 @@ var ReActStateConfig = {
170
207
  * Accumulates all observations from tool executions
171
208
  */
172
209
  observations: {
173
- schema: import_zod2.z.array(ToolResultSchema),
210
+ schema: import_zod3.z.array(ToolResultSchema),
174
211
  reducer: (left, right) => [...left, ...right],
175
212
  default: () => [],
176
213
  description: "Results from tool executions"
@@ -180,7 +217,7 @@ var ReActStateConfig = {
180
217
  * Accumulates step-by-step reasoning process
181
218
  */
182
219
  scratchpad: {
183
- schema: import_zod2.z.array(ScratchpadEntrySchema),
220
+ schema: import_zod3.z.array(ScratchpadEntrySchema),
184
221
  reducer: (left, right) => [...left, ...right],
185
222
  default: () => [],
186
223
  description: "Intermediate reasoning scratchpad"
@@ -189,27 +226,19 @@ var ReActStateConfig = {
189
226
  * Current iteration count
190
227
  * Tracks how many thought-action-observation loops have been executed
191
228
  */
192
- iteration: {
193
- schema: import_zod2.z.number(),
194
- reducer: (left, right) => left + right,
195
- default: () => 0,
196
- description: "Current iteration count"
197
- },
229
+ iteration: iterationField,
198
230
  /**
199
231
  * Whether the agent should continue iterating
200
232
  */
201
233
  shouldContinue: {
202
- schema: import_zod2.z.boolean().optional(),
234
+ schema: import_zod3.z.boolean().optional(),
203
235
  default: () => true,
204
236
  description: "Whether to continue the ReAct loop"
205
237
  },
206
238
  /**
207
239
  * Final response (if any)
208
240
  */
209
- response: {
210
- schema: import_zod2.z.string().optional(),
211
- description: "Final response from the agent"
212
- }
241
+ response: responseField
213
242
  };
214
243
  var ReActState = (0, import_core.createStateAnnotation)(ReActStateConfig);
215
244
 
@@ -245,14 +274,68 @@ function formatScratchpad(scratchpad) {
245
274
 
246
275
  // src/react/nodes.ts
247
276
  var import_messages = require("@langchain/core/messages");
277
+ var import_core3 = require("@agentforge/core");
278
+
279
+ // src/shared/deduplication.ts
248
280
  var import_core2 = require("@agentforge/core");
281
+ function generateToolCallCacheKey(toolName, args) {
282
+ const sortedArgs = JSON.stringify(args, Object.keys(args || {}).sort());
283
+ return `${toolName}:${sortedArgs}`;
284
+ }
285
+ function createPatternLogger(name, defaultLevel = "info") {
286
+ const logLevel5 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
287
+ return (0, import_core2.createLogger)(name, { level: logLevel5 });
288
+ }
289
+ function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
290
+ if (duplicatesSkipped === 0) {
291
+ return "0%";
292
+ }
293
+ const total = toolsExecuted + duplicatesSkipped;
294
+ return `${Math.round(duplicatesSkipped / total * 100)}%`;
295
+ }
296
+ function buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, totalObservations) {
297
+ return {
298
+ toolsExecuted,
299
+ duplicatesSkipped,
300
+ totalObservations,
301
+ deduplicationSavings: calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted)
302
+ };
303
+ }
304
+
305
+ // src/shared/error-handling.ts
306
+ function isGraphInterrupt(error) {
307
+ return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
308
+ }
309
+ function handleNodeError(error, context, verbose = false) {
310
+ if (isGraphInterrupt(error)) {
311
+ throw error;
312
+ }
313
+ const errorMessage = error instanceof Error ? error.message : String(error);
314
+ if (verbose) {
315
+ console.error(`[${context}] Error:`, errorMessage);
316
+ if (error instanceof Error && error.stack) {
317
+ console.error(`[${context}] Stack:`, error.stack);
318
+ }
319
+ }
320
+ return errorMessage;
321
+ }
322
+
323
+ // src/react/nodes.ts
324
+ var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
325
+ var actionLogger = createPatternLogger("agentforge:patterns:react:action");
326
+ var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
249
327
  function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
250
- const langchainTools = (0, import_core2.toLangChainTools)(tools);
328
+ const langchainTools = (0, import_core3.toLangChainTools)(tools);
251
329
  const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
252
330
  return async (state) => {
253
- if (verbose) {
254
- console.log(`[reasoning] Iteration ${state.iteration + 1}/${maxIterations}`);
255
- }
331
+ const currentIteration = state.iteration || 0;
332
+ const startTime = Date.now();
333
+ reasoningLogger.debug("Reasoning iteration started", {
334
+ iteration: currentIteration + 1,
335
+ maxIterations,
336
+ observationCount: state.observations?.length || 0,
337
+ hasActions: !!state.actions?.length
338
+ });
256
339
  const stateMessages = state.messages || [];
257
340
  const messages = [
258
341
  new import_messages.SystemMessage(systemPrompt),
@@ -282,8 +365,15 @@ ${scratchpadText}`));
282
365
  });
283
366
  }
284
367
  }
285
- const currentIteration = state.iteration || 0;
286
368
  const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
369
+ reasoningLogger.info("Reasoning complete", {
370
+ iteration: currentIteration + 1,
371
+ thoughtGenerated: !!thought,
372
+ actionCount: toolCalls.length,
373
+ shouldContinue,
374
+ isFinalResponse: toolCalls.length === 0,
375
+ duration: Date.now() - startTime
376
+ });
287
377
  return {
288
378
  messages: [{ role: "assistant", content: thought }],
289
379
  thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
@@ -296,16 +386,71 @@ ${scratchpadText}`));
296
386
  };
297
387
  };
298
388
  }
299
- function createActionNode(tools, verbose = false) {
389
+ function createActionNode(tools, verbose = false, enableDeduplication = true) {
300
390
  const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
301
391
  return async (state) => {
302
392
  const actions = state.actions || [];
303
- if (verbose) {
304
- console.log(`[action] Executing ${actions.length} tool calls`);
305
- }
393
+ const allObservations = state.observations || [];
394
+ const iteration = state.iteration || 0;
395
+ const startTime = Date.now();
396
+ actionLogger.debug("Action node started", {
397
+ actionCount: actions.length,
398
+ iteration,
399
+ cacheEnabled: enableDeduplication
400
+ });
306
401
  const recentActions = actions.slice(-10);
307
402
  const observations = [];
403
+ const executionCache = /* @__PURE__ */ new Map();
404
+ let cacheSize = 0;
405
+ if (enableDeduplication) {
406
+ for (const observation of allObservations) {
407
+ const correspondingAction = actions.find((a) => a.id === observation.toolCallId);
408
+ if (correspondingAction) {
409
+ const cacheKey = generateToolCallCacheKey(correspondingAction.name, correspondingAction.arguments);
410
+ executionCache.set(cacheKey, observation);
411
+ cacheSize++;
412
+ }
413
+ }
414
+ if (cacheSize > 0) {
415
+ actionLogger.debug("Deduplication cache built", {
416
+ cacheSize,
417
+ totalObservations: allObservations.length
418
+ });
419
+ }
420
+ }
421
+ let duplicatesSkipped = 0;
422
+ let toolsExecuted = 0;
308
423
  for (const action of recentActions) {
424
+ const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
425
+ if (existingObservation) {
426
+ actionLogger.debug("Skipping already-processed action", {
427
+ toolName: action.name,
428
+ toolCallId: action.id,
429
+ iteration
430
+ });
431
+ continue;
432
+ }
433
+ if (enableDeduplication) {
434
+ const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
435
+ const cachedResult = executionCache.get(cacheKey);
436
+ if (cachedResult) {
437
+ duplicatesSkipped++;
438
+ actionLogger.info("Duplicate tool call prevented", {
439
+ toolName: action.name,
440
+ arguments: action.arguments,
441
+ iteration,
442
+ cacheHit: true
443
+ });
444
+ observations.push({
445
+ toolCallId: action.id,
446
+ result: cachedResult.result,
447
+ error: cachedResult.error,
448
+ timestamp: Date.now(),
449
+ isDuplicate: true
450
+ });
451
+ continue;
452
+ }
453
+ }
309
454
  const tool = toolMap.get(action.name);
310
455
  if (!tool) {
311
456
  observations.push({
@@ -317,31 +462,48 @@ function createActionNode(tools, verbose = false) {
317
462
  continue;
318
463
  }
319
464
  try {
465
+ const startTime2 = Date.now();
320
466
  const result = await tool.execute(action.arguments);
321
- observations.push({
467
+ const executionTime = Date.now() - startTime2;
468
+ toolsExecuted++;
469
+ actionLogger.debug("Tool executed successfully", {
470
+ toolName: action.name,
471
+ executionTime,
472
+ iteration
473
+ });
474
+ const observation = {
322
475
  toolCallId: action.id,
323
476
  result,
324
477
  timestamp: Date.now()
325
- });
326
- if (verbose) {
327
- console.log(`[action] Tool '${action.name}' executed successfully`);
478
+ };
479
+ observations.push(observation);
480
+ if (enableDeduplication) {
481
+ const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
482
+ executionCache.set(cacheKey, observation);
328
483
  }
329
484
  } catch (error) {
330
- if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
331
- throw error;
332
- }
333
- const errorMessage = error instanceof Error ? error.message : String(error);
485
+ const errorMessage = handleNodeError(error, `action:${action.name}`, false);
486
+ actionLogger.error("Tool execution failed", {
487
+ toolName: action.name,
488
+ error: errorMessage,
489
+ iteration
490
+ });
334
491
  observations.push({
335
492
  toolCallId: action.id,
336
493
  result: null,
337
494
  error: errorMessage,
338
495
  timestamp: Date.now()
339
496
  });
340
- if (verbose) {
341
- console.error(`[action] Tool '${action.name}' failed:`, errorMessage);
342
- }
343
497
  }
344
498
  }
499
+ if (duplicatesSkipped > 0 || toolsExecuted > 0) {
500
+ const metrics = buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, observations.length);
501
+ actionLogger.info("Action node complete", {
502
+ iteration,
503
+ ...metrics,
504
+ duration: Date.now() - startTime
505
+ });
506
+ }
345
507
  return {
346
508
  observations
347
509
  };
@@ -352,9 +514,11 @@ function createObservationNode(verbose = false) {
352
514
  const observations = state.observations || [];
353
515
  const thoughts = state.thoughts || [];
354
516
  const actions = state.actions || [];
355
- if (verbose) {
356
- console.log(`[observation] Processing ${observations.length} observations`);
357
- }
517
+ const iteration = state.iteration || 0;
518
+ observationLogger.debug("Processing observations", {
519
+ observationCount: observations.length,
520
+ iteration
521
+ });
358
522
  const recentObservations = observations.slice(-10);
359
523
  const currentStep = state.iteration;
360
524
  const latestThought = thoughts[thoughts.length - 1]?.content || "";
@@ -380,6 +544,11 @@ function createObservationNode(verbose = false) {
380
544
  name: latestActions.find((a) => a.id === obs.toolCallId)?.name
381
545
  };
382
546
  });
547
+ observationLogger.debug("Observation node complete", {
548
+ iteration,
549
+ scratchpadUpdated: true,
550
+ messageCount: observationMessages.length
551
+ });
383
552
  return {
384
553
  scratchpad: [scratchpadEntry],
385
554
  messages: observationMessages
@@ -389,7 +558,7 @@ function createObservationNode(verbose = false) {
389
558
 
390
559
  // src/react/agent.ts
391
560
  var import_langgraph = require("@langchain/langgraph");
392
- var import_core3 = require("@agentforge/core");
561
+ var import_core4 = require("@agentforge/core");
393
562
  function createReActAgent(config, options) {
394
563
  const {
395
564
  model,
@@ -398,13 +567,15 @@ function createReActAgent(config, options) {
398
567
  maxIterations = 10,
399
568
  returnIntermediateSteps = false,
400
569
  stopCondition,
401
- checkpointer
570
+ checkpointer,
571
+ enableDeduplication = true
572
+ // Enable by default
402
573
  } = config;
403
574
  const {
404
575
  verbose = false,
405
576
  nodeNames = {}
406
577
  } = options || {};
407
- const toolArray = tools instanceof import_core3.ToolRegistry ? tools.getAll() : tools;
578
+ const toolArray = tools instanceof import_core4.ToolRegistry ? tools.getAll() : tools;
408
579
  const REASONING_NODE = nodeNames.reasoning || "reasoning";
409
580
  const ACTION_NODE = nodeNames.action || "action";
410
581
  const OBSERVATION_NODE = nodeNames.observation || "observation";
@@ -415,7 +586,7 @@ function createReActAgent(config, options) {
415
586
  maxIterations,
416
587
  verbose
417
588
  );
418
- const actionNode = createActionNode(toolArray, verbose);
589
+ const actionNode = createActionNode(toolArray, verbose, enableDeduplication);
419
590
  const observationNode = createObservationNode(verbose);
420
591
  const shouldContinue = (state) => {
421
592
  if (stopCondition && stopCondition(state)) {
@@ -550,34 +721,34 @@ function createReActAgentBuilder() {
550
721
  var import_langgraph2 = require("@langchain/langgraph");
551
722
 
552
723
  // src/plan-execute/state.ts
553
- var import_zod4 = require("zod");
554
- var import_core4 = require("@agentforge/core");
724
+ var import_zod5 = require("zod");
725
+ var import_core5 = require("@agentforge/core");
555
726
 
556
727
  // src/plan-execute/schemas.ts
557
- var import_zod3 = require("zod");
558
- var PlanStepSchema = import_zod3.z.object({
728
+ var import_zod4 = require("zod");
729
+ var PlanStepSchema = import_zod4.z.object({
559
730
  /**
560
731
  * Unique identifier for the step
561
732
  */
562
- id: import_zod3.z.string().describe("Unique identifier for the step"),
733
+ id: import_zod4.z.string().describe("Unique identifier for the step"),
563
734
  /**
564
735
  * Description of what this step should accomplish
565
736
  */
566
- description: import_zod3.z.string().describe("Description of what this step should accomplish"),
737
+ description: import_zod4.z.string().describe("Description of what this step should accomplish"),
567
738
  /**
568
739
  * Optional dependencies on other steps (by ID)
569
740
  */
570
- dependencies: import_zod3.z.array(import_zod3.z.string()).optional().describe("IDs of steps that must complete before this one"),
741
+ dependencies: import_zod4.z.array(import_zod4.z.string()).optional().describe("IDs of steps that must complete before this one"),
571
742
  /**
572
743
  * Optional tool to use for this step
573
744
  */
574
- tool: import_zod3.z.string().optional().describe("Name of the tool to use for this step"),
745
+ tool: import_zod4.z.string().optional().describe("Name of the tool to use for this step"),
575
746
  /**
576
747
  * Optional arguments for the tool
577
748
  */
578
- args: import_zod3.z.record(import_zod3.z.any()).optional().describe("Arguments to pass to the tool")
749
+ args: import_zod4.z.record(import_zod4.z.any()).optional().describe("Arguments to pass to the tool")
579
750
  });
580
- var CompletedStepSchema = import_zod3.z.object({
751
+ var CompletedStepSchema = import_zod4.z.object({
581
752
  /**
582
753
  * The step that was executed
583
754
  */
@@ -585,53 +756,53 @@ var CompletedStepSchema = import_zod3.z.object({
585
756
  /**
586
757
  * The result of executing the step
587
758
  */
588
- result: import_zod3.z.any().describe("The result of executing the step"),
759
+ result: import_zod4.z.any().describe("The result of executing the step"),
589
760
  /**
590
761
  * Whether the step succeeded
591
762
  */
592
- success: import_zod3.z.boolean().describe("Whether the step succeeded"),
763
+ success: import_zod4.z.boolean().describe("Whether the step succeeded"),
593
764
  /**
594
765
  * Optional error message if the step failed
595
766
  */
596
- error: import_zod3.z.string().optional().describe("Error message if the step failed"),
767
+ error: import_zod4.z.string().optional().describe("Error message if the step failed"),
597
768
  /**
598
769
  * Timestamp when the step was completed
599
770
  */
600
- timestamp: import_zod3.z.string().datetime().describe("ISO timestamp when the step was completed")
771
+ timestamp: import_zod4.z.string().datetime().describe("ISO timestamp when the step was completed")
601
772
  });
602
- var PlanSchema = import_zod3.z.object({
773
+ var PlanSchema = import_zod4.z.object({
603
774
  /**
604
775
  * List of steps in the plan
605
776
  */
606
- steps: import_zod3.z.array(PlanStepSchema).describe("List of steps in the plan"),
777
+ steps: import_zod4.z.array(PlanStepSchema).describe("List of steps in the plan"),
607
778
  /**
608
779
  * Overall goal of the plan
609
780
  */
610
- goal: import_zod3.z.string().describe("Overall goal of the plan"),
781
+ goal: import_zod4.z.string().describe("Overall goal of the plan"),
611
782
  /**
612
783
  * Timestamp when the plan was created
613
784
  */
614
- createdAt: import_zod3.z.string().datetime().describe("ISO timestamp when the plan was created"),
785
+ createdAt: import_zod4.z.string().datetime().describe("ISO timestamp when the plan was created"),
615
786
  /**
616
787
  * Optional confidence score (0-1)
617
788
  */
618
- confidence: import_zod3.z.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
789
+ confidence: import_zod4.z.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
619
790
  });
620
- var ReplanDecisionSchema = import_zod3.z.object({
791
+ var ReplanDecisionSchema = import_zod4.z.object({
621
792
  /**
622
793
  * Whether to replan
623
794
  */
624
- shouldReplan: import_zod3.z.boolean().describe("Whether to replan based on current results"),
795
+ shouldReplan: import_zod4.z.boolean().describe("Whether to replan based on current results"),
625
796
  /**
626
797
  * Reason for the decision
627
798
  */
628
- reason: import_zod3.z.string().describe("Reason for the replan decision"),
799
+ reason: import_zod4.z.string().describe("Reason for the replan decision"),
629
800
  /**
630
801
  * Optional new goal if replanning
631
802
  */
632
- newGoal: import_zod3.z.string().optional().describe("Updated goal if replanning")
803
+ newGoal: import_zod4.z.string().optional().describe("Updated goal if replanning")
633
804
  });
634
- var ExecutionStatusSchema = import_zod3.z.enum([
805
+ var ExecutionStatusSchema = import_zod4.z.enum([
635
806
  "planning",
636
807
  "executing",
637
808
  "replanning",
@@ -644,11 +815,7 @@ var PlanExecuteStateConfig = {
644
815
  /**
645
816
  * Original user input/query
646
817
  */
647
- input: {
648
- schema: import_zod4.z.string(),
649
- default: () => "",
650
- description: "Original user input or query"
651
- },
818
+ input: inputField,
652
819
  /**
653
820
  * The current plan
654
821
  */
@@ -661,7 +828,7 @@ var PlanExecuteStateConfig = {
661
828
  * Accumulates all completed steps
662
829
  */
663
830
  pastSteps: {
664
- schema: import_zod4.z.array(CompletedStepSchema),
831
+ schema: import_zod5.z.array(CompletedStepSchema),
665
832
  reducer: (left, right) => [...left, ...right],
666
833
  default: () => [],
667
834
  description: "Completed steps with their results"
@@ -670,7 +837,7 @@ var PlanExecuteStateConfig = {
670
837
  * Index of the current step being executed
671
838
  */
672
839
  currentStepIndex: {
673
- schema: import_zod4.z.number().int().nonnegative().optional(),
840
+ schema: import_zod5.z.number().int().nonnegative().optional(),
674
841
  description: "Index of the current step being executed"
675
842
  },
676
843
  /**
@@ -684,36 +851,21 @@ var PlanExecuteStateConfig = {
684
851
  /**
685
852
  * Final response
686
853
  */
687
- response: {
688
- schema: import_zod4.z.string().optional(),
689
- description: "Final response after plan execution"
690
- },
854
+ response: responseField,
691
855
  /**
692
856
  * Error message if execution failed
693
857
  */
694
- error: {
695
- schema: import_zod4.z.string().optional(),
696
- description: "Error message if execution failed"
697
- },
858
+ error: errorField,
698
859
  /**
699
860
  * Iteration counter for replanning
700
861
  */
701
- iteration: {
702
- schema: import_zod4.z.number().int().nonnegative(),
703
- reducer: (left, right) => left + right,
704
- default: () => 0,
705
- description: "Number of planning iterations"
706
- },
862
+ iteration: iterationField,
707
863
  /**
708
864
  * Maximum iterations allowed
709
865
  */
710
- maxIterations: {
711
- schema: import_zod4.z.number().int().positive(),
712
- default: () => 5,
713
- description: "Maximum number of planning iterations allowed"
714
- }
866
+ maxIterations: maxIterationsField(5)
715
867
  };
716
- var PlanExecuteState = (0, import_core4.createStateAnnotation)(PlanExecuteStateConfig);
868
+ var PlanExecuteState = (0, import_core5.createStateAnnotation)(PlanExecuteStateConfig);
717
869
 
718
870
  // src/plan-execute/nodes.ts
719
871
  var import_messages2 = require("@langchain/core/messages");
@@ -778,6 +930,9 @@ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
778
930
  {dependencies}`;
779
931
 
780
932
  // src/plan-execute/nodes.ts
933
+ var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planner");
934
+ var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
935
+ var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
781
936
  function createPlannerNode(config) {
782
937
  const {
783
938
  model,
@@ -786,7 +941,13 @@ function createPlannerNode(config) {
786
941
  includeToolDescriptions = false
787
942
  } = config;
788
943
  return async (state) => {
944
+ const startTime = Date.now();
789
945
  try {
946
+ plannerLogger.debug("Planning started", {
947
+ input: state.input?.substring(0, 100),
948
+ maxSteps,
949
+ includeToolDescriptions
950
+ });
790
951
  let toolDescriptions = "";
791
952
  if (includeToolDescriptions) {
792
953
  toolDescriptions = "";
@@ -811,6 +972,12 @@ function createPlannerNode(config) {
811
972
  } catch (parseError) {
812
973
  throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
813
974
  }
975
+ plannerLogger.info("Plan created", {
976
+ stepCount: plan.steps.length,
977
+ goal: plan.goal.substring(0, 100),
978
+ confidence: plan.confidence,
979
+ duration: Date.now() - startTime
980
+ });
814
981
  return {
815
982
  plan,
816
983
  status: "executing",
@@ -818,6 +985,10 @@ function createPlannerNode(config) {
818
985
  iteration: 1
819
986
  };
820
987
  } catch (error) {
988
+ plannerLogger.error("Planning failed", {
989
+ error: error instanceof Error ? error.message : String(error),
990
+ duration: Date.now() - startTime
991
+ });
821
992
  return {
822
993
  status: "failed",
823
994
  error: error instanceof Error ? error.message : "Unknown error in planner"
@@ -830,11 +1001,18 @@ function createExecutorNode(config) {
830
1001
  tools,
831
1002
  model,
832
1003
  parallel = false,
833
- stepTimeout = 3e4
1004
+ stepTimeout = 3e4,
1005
+ enableDeduplication = true
834
1006
  } = config;
835
1007
  return async (state) => {
1008
+ const { plan, currentStepIndex = 0, pastSteps = [], iteration = 0 } = state;
836
1009
  try {
837
- const { plan, currentStepIndex = 0, pastSteps = [] } = state;
1010
+ executorLogger.debug("Executor node executing", {
1011
+ currentStepIndex,
1012
+ totalSteps: plan?.steps?.length || 0,
1013
+ iteration,
1014
+ deduplicationEnabled: enableDeduplication
1015
+ });
838
1016
  if (!plan || !plan.steps || plan.steps.length === 0) {
839
1017
  return {
840
1018
  status: "completed"
@@ -853,32 +1031,80 @@ function createExecutorNode(config) {
853
1031
  throw new Error(`Unmet dependencies for step ${currentStep.id}: ${unmetDependencies.join(", ")}`);
854
1032
  }
855
1033
  }
1034
+ const executionCache = /* @__PURE__ */ new Map();
1035
+ let cacheSize = 0;
1036
+ if (enableDeduplication && currentStep.tool) {
1037
+ for (const pastStep of pastSteps) {
1038
+ if (pastStep.step.tool) {
1039
+ const cacheKey = generateToolCallCacheKey(pastStep.step.tool, pastStep.step.args || {});
1040
+ executionCache.set(cacheKey, pastStep);
1041
+ cacheSize++;
1042
+ }
1043
+ }
1044
+ if (cacheSize > 0) {
1045
+ executorLogger.debug("Deduplication cache built", {
1046
+ cacheSize,
1047
+ pastStepsCount: pastSteps.length
1048
+ });
1049
+ }
1050
+ }
856
1051
  let result;
857
1052
  let success = true;
858
1053
  let error;
1054
+ let isDuplicate = false;
859
1055
  try {
860
1056
  if (currentStep.tool) {
861
- const tool = tools.find((t) => t.metadata.name === currentStep.tool);
862
- if (!tool) {
863
- throw new Error(`Tool not found: ${currentStep.tool}`);
1057
+ if (enableDeduplication) {
1058
+ const cacheKey = generateToolCallCacheKey(currentStep.tool, currentStep.args || {});
1059
+ const cachedStep = executionCache.get(cacheKey);
1060
+ if (cachedStep) {
1061
+ isDuplicate = true;
1062
+ result = cachedStep.result;
1063
+ success = cachedStep.success;
1064
+ error = cachedStep.error;
1065
+ executorLogger.info("Duplicate step execution prevented", {
1066
+ stepId: currentStep.id,
1067
+ toolName: currentStep.tool,
1068
+ arguments: currentStep.args,
1069
+ iteration,
1070
+ cacheHit: true
1071
+ });
1072
+ }
1073
+ }
1074
+ if (!isDuplicate) {
1075
+ const tool = tools.find((t) => t.metadata.name === currentStep.tool);
1076
+ if (!tool) {
1077
+ throw new Error(`Tool not found: ${currentStep.tool}`);
1078
+ }
1079
+ const startTime = Date.now();
1080
+ const timeoutPromise = new Promise(
1081
+ (_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
1082
+ );
1083
+ result = await Promise.race([
1084
+ tool.execute(currentStep.args || {}),
1085
+ timeoutPromise
1086
+ ]);
1087
+ const executionTime = Date.now() - startTime;
1088
+ executorLogger.debug("Step executed successfully", {
1089
+ stepId: currentStep.id,
1090
+ toolName: currentStep.tool,
1091
+ executionTime,
1092
+ iteration
1093
+ });
864
1094
  }
865
- const timeoutPromise = new Promise(
866
- (_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
867
- );
868
- result = await Promise.race([
869
- tool.execute(currentStep.args || {}),
870
- timeoutPromise
871
- ]);
872
1095
  } else {
873
1096
  result = { message: "Step completed without tool execution" };
874
1097
  }
875
1098
  } catch (execError) {
876
- if (execError && typeof execError === "object" && "constructor" in execError && execError.constructor.name === "GraphInterrupt") {
877
- throw execError;
878
- }
1099
+ error = handleNodeError(execError, `executor:${currentStep.description}`, false);
879
1100
  success = false;
880
- error = execError instanceof Error ? execError.message : "Unknown execution error";
881
1101
  result = null;
1102
+ executorLogger.warn("Step execution failed", {
1103
+ stepId: currentStep.id,
1104
+ toolName: currentStep.tool,
1105
+ error,
1106
+ iteration
1107
+ });
882
1108
  }
883
1109
  const completedStep = {
884
1110
  step: currentStep,
@@ -887,11 +1113,23 @@ function createExecutorNode(config) {
887
1113
  error,
888
1114
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
889
1115
  };
1116
+ executorLogger.info("Executor node complete", {
1117
+ stepId: currentStep.id,
1118
+ stepIndex: currentStepIndex,
1119
+ totalSteps: plan.steps.length,
1120
+ success,
1121
+ isDuplicate,
1122
+ iteration
1123
+ });
890
1124
  return {
891
1125
  pastSteps: [completedStep],
892
1126
  currentStepIndex: currentStepIndex + 1
893
1127
  };
894
1128
  } catch (error) {
1129
+ executorLogger.error("Executor node failed", {
1130
+ error: error instanceof Error ? error.message : "Unknown error",
1131
+ iteration
1132
+ });
895
1133
  return {
896
1134
  status: "failed",
897
1135
  error: error instanceof Error ? error.message : "Unknown error in executor"
@@ -906,11 +1144,18 @@ function createReplannerNode(config) {
906
1144
  systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
907
1145
  } = config;
908
1146
  return async (state) => {
1147
+ const startTime = Date.now();
909
1148
  try {
910
1149
  const { plan, pastSteps = [], currentStepIndex = 0 } = state;
911
1150
  if (!plan) {
912
1151
  return { status: "failed", error: "No plan available for replanning" };
913
1152
  }
1153
+ replannerLogger.debug("Evaluating replanning", {
1154
+ completedSteps: pastSteps.length,
1155
+ remainingSteps: plan.steps.length - currentStepIndex,
1156
+ successfulSteps: pastSteps.filter((ps) => ps.success).length,
1157
+ failedSteps: pastSteps.filter((ps) => !ps.success).length
1158
+ });
914
1159
  const completedStepsText = pastSteps.map(
915
1160
  (ps, idx) => COMPLETED_STEP_TEMPLATE.replace("{stepNumber}", String(idx + 1)).replace("{description}", ps.step.description).replace("{result}", JSON.stringify(ps.result)).replace("{status}", ps.success ? "Success" : `Failed: ${ps.error}`)
916
1161
  ).join("\n\n");
@@ -932,17 +1177,30 @@ function createReplannerNode(config) {
932
1177
  throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
933
1178
  }
934
1179
  if (decision.shouldReplan) {
1180
+ replannerLogger.info("Replanning triggered", {
1181
+ reason: decision.reason,
1182
+ newGoal: decision.newGoal?.substring(0, 100),
1183
+ duration: Date.now() - startTime
1184
+ });
935
1185
  return {
936
1186
  status: "planning",
937
1187
  input: decision.newGoal || plan.goal,
938
1188
  iteration: 1
939
1189
  };
940
1190
  } else {
1191
+ replannerLogger.info("Continuing with current plan", {
1192
+ reason: decision.reason,
1193
+ duration: Date.now() - startTime
1194
+ });
941
1195
  return {
942
1196
  status: "executing"
943
1197
  };
944
1198
  }
945
1199
  } catch (error) {
1200
+ replannerLogger.error("Replanning evaluation failed", {
1201
+ error: error instanceof Error ? error.message : String(error),
1202
+ duration: Date.now() - startTime
1203
+ });
946
1204
  return {
947
1205
  status: "failed",
948
1206
  error: error instanceof Error ? error.message : "Unknown error in replanner"
@@ -1042,46 +1300,46 @@ function createPlanExecuteAgent(config) {
1042
1300
  }
1043
1301
 
1044
1302
  // src/reflection/state.ts
1045
- var import_zod6 = require("zod");
1046
- var import_core5 = require("@agentforge/core");
1303
+ var import_zod7 = require("zod");
1304
+ var import_core6 = require("@agentforge/core");
1047
1305
 
1048
1306
  // src/reflection/schemas.ts
1049
- var import_zod5 = require("zod");
1050
- var ReflectionSchema = import_zod5.z.object({
1307
+ var import_zod6 = require("zod");
1308
+ var ReflectionSchema = import_zod6.z.object({
1051
1309
  /**
1052
1310
  * The critique or feedback on the current response
1053
1311
  */
1054
- critique: import_zod5.z.string().describe("Critique or feedback on the current response"),
1312
+ critique: import_zod6.z.string().describe("Critique or feedback on the current response"),
1055
1313
  /**
1056
1314
  * Specific issues identified
1057
1315
  */
1058
- issues: import_zod5.z.array(import_zod5.z.string()).describe("Specific issues or problems identified"),
1316
+ issues: import_zod6.z.array(import_zod6.z.string()).describe("Specific issues or problems identified"),
1059
1317
  /**
1060
1318
  * Suggestions for improvement
1061
1319
  */
1062
- suggestions: import_zod5.z.array(import_zod5.z.string()).describe("Suggestions for improving the response"),
1320
+ suggestions: import_zod6.z.array(import_zod6.z.string()).describe("Suggestions for improving the response"),
1063
1321
  /**
1064
1322
  * Quality score (0-10)
1065
1323
  */
1066
- score: import_zod5.z.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
1324
+ score: import_zod6.z.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
1067
1325
  /**
1068
1326
  * Whether the response meets quality standards
1069
1327
  */
1070
- meetsStandards: import_zod5.z.boolean().describe("Whether the response meets quality standards"),
1328
+ meetsStandards: import_zod6.z.boolean().describe("Whether the response meets quality standards"),
1071
1329
  /**
1072
1330
  * Timestamp of the reflection
1073
1331
  */
1074
- timestamp: import_zod5.z.date().optional().describe("When this reflection was created")
1332
+ timestamp: import_zod6.z.date().optional().describe("When this reflection was created")
1075
1333
  });
1076
- var RevisionSchema = import_zod5.z.object({
1334
+ var RevisionSchema = import_zod6.z.object({
1077
1335
  /**
1078
1336
  * The revised content
1079
1337
  */
1080
- content: import_zod5.z.string().describe("The revised content"),
1338
+ content: import_zod6.z.string().describe("The revised content"),
1081
1339
  /**
1082
1340
  * Which iteration this revision is from
1083
1341
  */
1084
- iteration: import_zod5.z.number().int().nonnegative().describe("Iteration number"),
1342
+ iteration: import_zod6.z.number().int().nonnegative().describe("Iteration number"),
1085
1343
  /**
1086
1344
  * The reflection that prompted this revision
1087
1345
  */
@@ -1089,9 +1347,9 @@ var RevisionSchema = import_zod5.z.object({
1089
1347
  /**
1090
1348
  * Timestamp of the revision
1091
1349
  */
1092
- timestamp: import_zod5.z.date().optional().describe("When this revision was created")
1350
+ timestamp: import_zod6.z.date().optional().describe("When this revision was created")
1093
1351
  });
1094
- var ReflectionStatusSchema = import_zod5.z.enum([
1352
+ var ReflectionStatusSchema = import_zod6.z.enum([
1095
1353
  "generating",
1096
1354
  // Initial generation
1097
1355
  "reflecting",
@@ -1103,25 +1361,25 @@ var ReflectionStatusSchema = import_zod5.z.enum([
1103
1361
  "failed"
1104
1362
  // Max iterations reached without meeting standards
1105
1363
  ]);
1106
- var QualityCriteriaSchema = import_zod5.z.object({
1364
+ var QualityCriteriaSchema = import_zod6.z.object({
1107
1365
  /**
1108
1366
  * Minimum quality score required (0-10)
1109
1367
  */
1110
- minScore: import_zod5.z.number().min(0).max(10).default(7).describe("Minimum quality score required"),
1368
+ minScore: import_zod6.z.number().min(0).max(10).default(7).describe("Minimum quality score required"),
1111
1369
  /**
1112
1370
  * Specific criteria to evaluate
1113
1371
  */
1114
- criteria: import_zod5.z.array(import_zod5.z.string()).optional().describe("Specific criteria to evaluate"),
1372
+ criteria: import_zod6.z.array(import_zod6.z.string()).optional().describe("Specific criteria to evaluate"),
1115
1373
  /**
1116
1374
  * Whether all criteria must be met
1117
1375
  */
1118
- requireAll: import_zod5.z.boolean().default(true).describe("Whether all criteria must be met")
1376
+ requireAll: import_zod6.z.boolean().default(true).describe("Whether all criteria must be met")
1119
1377
  });
1120
- var ReflectionConfigSchema = import_zod5.z.object({
1378
+ var ReflectionConfigSchema = import_zod6.z.object({
1121
1379
  /**
1122
1380
  * Maximum number of reflection iterations
1123
1381
  */
1124
- maxIterations: import_zod5.z.number().int().positive().default(3).describe("Maximum reflection iterations"),
1382
+ maxIterations: import_zod6.z.number().int().positive().default(3).describe("Maximum reflection iterations"),
1125
1383
  /**
1126
1384
  * Quality criteria for completion
1127
1385
  */
@@ -1129,7 +1387,7 @@ var ReflectionConfigSchema = import_zod5.z.object({
1129
1387
  /**
1130
1388
  * Whether to include previous reflections in context
1131
1389
  */
1132
- includeHistory: import_zod5.z.boolean().default(true).describe("Include previous reflections in context")
1390
+ includeHistory: import_zod6.z.boolean().default(true).describe("Include previous reflections in context")
1133
1391
  });
1134
1392
 
1135
1393
  // src/reflection/state.ts
@@ -1137,16 +1395,12 @@ var ReflectionStateConfig = {
1137
1395
  /**
1138
1396
  * Original user input/task
1139
1397
  */
1140
- input: {
1141
- schema: import_zod6.z.string(),
1142
- default: () => "",
1143
- description: "Original user input or task"
1144
- },
1398
+ input: inputField,
1145
1399
  /**
1146
1400
  * Current response/output
1147
1401
  */
1148
1402
  currentResponse: {
1149
- schema: import_zod6.z.string().optional(),
1403
+ schema: import_zod7.z.string().optional(),
1150
1404
  description: "Current response or output"
1151
1405
  },
1152
1406
  /**
@@ -1154,7 +1408,7 @@ var ReflectionStateConfig = {
1154
1408
  * Accumulates all reflections
1155
1409
  */
1156
1410
  reflections: {
1157
- schema: import_zod6.z.array(ReflectionSchema),
1411
+ schema: import_zod7.z.array(ReflectionSchema),
1158
1412
  reducer: (left, right) => [...left, ...right],
1159
1413
  default: () => [],
1160
1414
  description: "History of all reflections and critiques"
@@ -1164,7 +1418,7 @@ var ReflectionStateConfig = {
1164
1418
  * Accumulates all revisions
1165
1419
  */
1166
1420
  revisions: {
1167
- schema: import_zod6.z.array(RevisionSchema),
1421
+ schema: import_zod7.z.array(RevisionSchema),
1168
1422
  reducer: (left, right) => [...left, ...right],
1169
1423
  default: () => [],
1170
1424
  description: "History of all revisions"
@@ -1172,12 +1426,7 @@ var ReflectionStateConfig = {
1172
1426
  /**
1173
1427
  * Current iteration number
1174
1428
  */
1175
- iteration: {
1176
- schema: import_zod6.z.number().int().nonnegative(),
1177
- reducer: (left, right) => left + right,
1178
- default: () => 0,
1179
- description: "Current iteration number"
1180
- },
1429
+ iteration: iterationField,
1181
1430
  /**
1182
1431
  * Current status
1183
1432
  */
@@ -1196,27 +1445,17 @@ var ReflectionStateConfig = {
1196
1445
  /**
1197
1446
  * Maximum iterations allowed
1198
1447
  */
1199
- maxIterations: {
1200
- schema: import_zod6.z.number().int().positive(),
1201
- default: () => 3,
1202
- description: "Maximum number of reflection iterations allowed"
1203
- },
1448
+ maxIterations: maxIterationsField(3),
1204
1449
  /**
1205
1450
  * Final response (when completed)
1206
1451
  */
1207
- response: {
1208
- schema: import_zod6.z.string().optional(),
1209
- description: "Final response after reflection process"
1210
- },
1452
+ response: responseField,
1211
1453
  /**
1212
1454
  * Error message if failed
1213
1455
  */
1214
- error: {
1215
- schema: import_zod6.z.string().optional(),
1216
- description: "Error message if reflection failed"
1217
- }
1456
+ error: errorField
1218
1457
  };
1219
- var ReflectionState = (0, import_core5.createStateAnnotation)(ReflectionStateConfig);
1458
+ var ReflectionState = (0, import_core6.createStateAnnotation)(ReflectionStateConfig);
1220
1459
 
1221
1460
  // src/reflection/prompts.ts
1222
1461
  var DEFAULT_GENERATOR_SYSTEM_PROMPT = `You are an expert content generator. Your task is to create high-quality responses to user requests.
@@ -1320,6 +1559,9 @@ var REVISION_ENTRY_TEMPLATE = `Iteration {iteration}:
1320
1559
 
1321
1560
  // src/reflection/nodes.ts
1322
1561
  var import_messages3 = require("@langchain/core/messages");
1562
+ var generatorLogger = createPatternLogger("agentforge:patterns:reflection:generator");
1563
+ var reflectorLogger = createPatternLogger("agentforge:patterns:reflection:reflector");
1564
+ var reviserLogger = createPatternLogger("agentforge:patterns:reflection:reviser");
1323
1565
  function createGeneratorNode(config) {
1324
1566
  const {
1325
1567
  model,
@@ -1327,10 +1569,13 @@ function createGeneratorNode(config) {
1327
1569
  verbose = false
1328
1570
  } = config;
1329
1571
  return async (state) => {
1572
+ const startTime = Date.now();
1330
1573
  try {
1331
- if (verbose) {
1332
- console.log("[Generator] Generating initial response...");
1333
- }
1574
+ generatorLogger.debug("Generating response", {
1575
+ attempt: state.iteration + 1,
1576
+ hasFeedback: state.reflections.length > 0,
1577
+ hasExistingResponse: !!state.currentResponse
1578
+ });
1334
1579
  let context = "";
1335
1580
  if (state.iteration > 0 && state.reflections.length > 0) {
1336
1581
  const lastReflection = state.reflections[state.reflections.length - 1];
@@ -1345,19 +1590,27 @@ ${lastReflection.critique}`;
1345
1590
  ];
1346
1591
  const response = await model.invoke(messages);
1347
1592
  const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1348
- if (verbose) {
1349
- console.log("[Generator] Generated response:", content.substring(0, 100) + "...");
1350
- }
1593
+ generatorLogger.info("Response generated", {
1594
+ attempt: state.iteration + 1,
1595
+ responseLength: content.length,
1596
+ isRevision: state.iteration > 0,
1597
+ duration: Date.now() - startTime
1598
+ });
1351
1599
  return {
1352
1600
  currentResponse: content,
1353
1601
  status: "reflecting",
1354
1602
  iteration: 1
1355
1603
  };
1356
1604
  } catch (error) {
1357
- console.error("[Generator] Error:", error);
1605
+ const errorMessage = handleNodeError(error, "generator", false);
1606
+ generatorLogger.error("Response generation failed", {
1607
+ attempt: state.iteration + 1,
1608
+ error: errorMessage,
1609
+ duration: Date.now() - startTime
1610
+ });
1358
1611
  return {
1359
1612
  status: "failed",
1360
- error: error instanceof Error ? error.message : "Unknown error in generator"
1613
+ error: errorMessage
1361
1614
  };
1362
1615
  }
1363
1616
  };
@@ -1370,10 +1623,13 @@ function createReflectorNode(config) {
1370
1623
  verbose = false
1371
1624
  } = config;
1372
1625
  return async (state) => {
1626
+ const startTime = Date.now();
1373
1627
  try {
1374
- if (verbose) {
1375
- console.log("[Reflector] Reflecting on response...");
1376
- }
1628
+ reflectorLogger.debug("Reflecting on response", {
1629
+ attempt: state.iteration,
1630
+ responseLength: state.currentResponse?.length || 0,
1631
+ hasCriteria: !!(qualityCriteria || state.qualityCriteria)
1632
+ });
1377
1633
  if (!state.currentResponse) {
1378
1634
  throw new Error("No current response to reflect on");
1379
1635
  }
@@ -1423,19 +1679,28 @@ function createReflectorNode(config) {
1423
1679
  meetsStandards: false
1424
1680
  };
1425
1681
  }
1426
- if (verbose) {
1427
- console.log("[Reflector] Reflection score:", reflection.score);
1428
- console.log("[Reflector] Meets standards:", reflection.meetsStandards);
1429
- }
1682
+ reflectorLogger.info("Reflection complete", {
1683
+ attempt: state.iteration,
1684
+ score: reflection.score,
1685
+ meetsStandards: reflection.meetsStandards,
1686
+ issueCount: reflection.issues.length,
1687
+ suggestionCount: reflection.suggestions.length,
1688
+ duration: Date.now() - startTime
1689
+ });
1430
1690
  return {
1431
1691
  reflections: [reflection],
1432
1692
  status: reflection.meetsStandards ? "completed" : "revising"
1433
1693
  };
1434
1694
  } catch (error) {
1435
- console.error("[Reflector] Error:", error);
1695
+ const errorMessage = handleNodeError(error, "reflector", false);
1696
+ reflectorLogger.error("Reflection failed", {
1697
+ attempt: state.iteration,
1698
+ error: errorMessage,
1699
+ duration: Date.now() - startTime
1700
+ });
1436
1701
  return {
1437
1702
  status: "failed",
1438
- error: error instanceof Error ? error.message : "Unknown error in reflector"
1703
+ error: errorMessage
1439
1704
  };
1440
1705
  }
1441
1706
  };
@@ -1447,10 +1712,8 @@ function createReviserNode(config) {
1447
1712
  verbose = false
1448
1713
  } = config;
1449
1714
  return async (state) => {
1715
+ const startTime = Date.now();
1450
1716
  try {
1451
- if (verbose) {
1452
- console.log("[Reviser] Revising response...");
1453
- }
1454
1717
  if (!state.currentResponse) {
1455
1718
  throw new Error("No current response to revise");
1456
1719
  }
@@ -1458,6 +1721,12 @@ function createReviserNode(config) {
1458
1721
  throw new Error("No reflections to base revision on");
1459
1722
  }
1460
1723
  const lastReflection = state.reflections[state.reflections.length - 1];
1724
+ reviserLogger.debug("Revising response", {
1725
+ attempt: state.iteration,
1726
+ previousScore: lastReflection.score,
1727
+ issueCount: lastReflection.issues.length,
1728
+ suggestionCount: lastReflection.suggestions.length
1729
+ });
1461
1730
  let historySection = "";
1462
1731
  if (state.revisions.length > 0) {
1463
1732
  const revisionsText = state.revisions.map(
@@ -1474,14 +1743,17 @@ ${revisionsText}`;
1474
1743
  ];
1475
1744
  const response = await model.invoke(messages);
1476
1745
  const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1477
- if (verbose) {
1478
- console.log("[Reviser] Created revision:", content.substring(0, 100) + "...");
1479
- }
1480
1746
  const revision = {
1481
1747
  content,
1482
1748
  iteration: state.iteration,
1483
1749
  basedOn: lastReflection
1484
1750
  };
1751
+ reviserLogger.info("Revision complete", {
1752
+ attempt: state.iteration,
1753
+ revisionLength: content.length,
1754
+ basedOnScore: lastReflection.score,
1755
+ duration: Date.now() - startTime
1756
+ });
1485
1757
  return {
1486
1758
  currentResponse: content,
1487
1759
  revisions: [revision],
@@ -1489,10 +1761,15 @@ ${revisionsText}`;
1489
1761
  iteration: 1
1490
1762
  };
1491
1763
  } catch (error) {
1492
- console.error("[Reviser] Error:", error);
1764
+ const errorMessage = handleNodeError(error, "reviser", false);
1765
+ reviserLogger.error("Revision failed", {
1766
+ attempt: state.iteration,
1767
+ error: errorMessage,
1768
+ duration: Date.now() - startTime
1769
+ });
1493
1770
  return {
1494
1771
  status: "failed",
1495
- error: error instanceof Error ? error.message : "Unknown error in reviser"
1772
+ error: errorMessage
1496
1773
  };
1497
1774
  }
1498
1775
  };
@@ -1578,13 +1855,13 @@ function createReflectionAgent(config) {
1578
1855
  }
1579
1856
 
1580
1857
  // src/multi-agent/state.ts
1581
- var import_zod8 = require("zod");
1582
- var import_core6 = require("@agentforge/core");
1858
+ var import_zod9 = require("zod");
1859
+ var import_core7 = require("@agentforge/core");
1583
1860
 
1584
1861
  // src/multi-agent/schemas.ts
1585
- var import_zod7 = require("zod");
1586
- var AgentRoleSchema = import_zod7.z.enum(["supervisor", "worker"]);
1587
- var MessageTypeSchema = import_zod7.z.enum([
1862
+ var import_zod8 = require("zod");
1863
+ var AgentRoleSchema = import_zod8.z.enum(["supervisor", "worker"]);
1864
+ var MessageTypeSchema = import_zod8.z.enum([
1588
1865
  "user_input",
1589
1866
  // Initial user message
1590
1867
  "task_assignment",
@@ -1598,11 +1875,11 @@ var MessageTypeSchema = import_zod7.z.enum([
1598
1875
  "completion"
1599
1876
  // Final completion message
1600
1877
  ]);
1601
- var AgentMessageSchema = import_zod7.z.object({
1878
+ var AgentMessageSchema = import_zod8.z.object({
1602
1879
  /**
1603
1880
  * Unique identifier for the message
1604
1881
  */
1605
- id: import_zod7.z.string().describe("Unique message identifier"),
1882
+ id: import_zod8.z.string().describe("Unique message identifier"),
1606
1883
  /**
1607
1884
  * Type of message
1608
1885
  */
@@ -1610,25 +1887,25 @@ var AgentMessageSchema = import_zod7.z.object({
1610
1887
  /**
1611
1888
  * Agent that sent the message
1612
1889
  */
1613
- from: import_zod7.z.string().describe("Agent identifier that sent the message"),
1890
+ from: import_zod8.z.string().describe("Agent identifier that sent the message"),
1614
1891
  /**
1615
1892
  * Agent(s) that should receive the message
1616
1893
  */
1617
- to: import_zod7.z.union([import_zod7.z.string(), import_zod7.z.array(import_zod7.z.string())]).describe("Target agent(s)"),
1894
+ to: import_zod8.z.union([import_zod8.z.string(), import_zod8.z.array(import_zod8.z.string())]).describe("Target agent(s)"),
1618
1895
  /**
1619
1896
  * Message content
1620
1897
  */
1621
- content: import_zod7.z.string().describe("Message content"),
1898
+ content: import_zod8.z.string().describe("Message content"),
1622
1899
  /**
1623
1900
  * Optional metadata
1624
1901
  */
1625
- metadata: import_zod7.z.record(import_zod7.z.any()).optional().describe("Additional message metadata"),
1902
+ metadata: import_zod8.z.record(import_zod8.z.any()).optional().describe("Additional message metadata"),
1626
1903
  /**
1627
1904
  * Timestamp when message was created
1628
1905
  */
1629
- timestamp: import_zod7.z.number().describe("Timestamp when message was created")
1906
+ timestamp: import_zod8.z.number().describe("Timestamp when message was created")
1630
1907
  });
1631
- var RoutingStrategySchema = import_zod7.z.enum([
1908
+ var RoutingStrategySchema = import_zod8.z.enum([
1632
1909
  "llm-based",
1633
1910
  // LLM decides which agent to route to
1634
1911
  "rule-based",
@@ -1640,25 +1917,25 @@ var RoutingStrategySchema = import_zod7.z.enum([
1640
1917
  "load-balanced"
1641
1918
  // Route based on agent workload
1642
1919
  ]);
1643
- var RoutingDecisionSchema = import_zod7.z.object({
1920
+ var RoutingDecisionSchema = import_zod8.z.object({
1644
1921
  /**
1645
1922
  * Target agent to route to (single agent routing)
1646
1923
  * @deprecated Use targetAgents for parallel routing support
1647
1924
  */
1648
- targetAgent: import_zod7.z.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
1925
+ targetAgent: import_zod8.z.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
1649
1926
  /**
1650
1927
  * Target agents to route to (parallel routing)
1651
1928
  * When multiple agents are specified, they execute in parallel
1652
1929
  */
1653
- targetAgents: import_zod7.z.array(import_zod7.z.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
1930
+ targetAgents: import_zod8.z.array(import_zod8.z.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
1654
1931
  /**
1655
1932
  * Reasoning for the routing decision
1656
1933
  */
1657
- reasoning: import_zod7.z.string().default("").describe("Explanation for routing decision"),
1934
+ reasoning: import_zod8.z.string().default("").describe("Explanation for routing decision"),
1658
1935
  /**
1659
1936
  * Confidence in the routing decision (0-1)
1660
1937
  */
1661
- confidence: import_zod7.z.number().min(0).max(1).default(0.8).describe("Confidence score"),
1938
+ confidence: import_zod8.z.number().min(0).max(1).default(0.8).describe("Confidence score"),
1662
1939
  /**
1663
1940
  * Strategy used for routing
1664
1941
  */
@@ -1666,86 +1943,86 @@ var RoutingDecisionSchema = import_zod7.z.object({
1666
1943
  /**
1667
1944
  * Timestamp of the routing decision
1668
1945
  */
1669
- timestamp: import_zod7.z.number().default(() => Date.now()).describe("Timestamp of the decision")
1946
+ timestamp: import_zod8.z.number().default(() => Date.now()).describe("Timestamp of the decision")
1670
1947
  }).refine(
1671
1948
  (data) => data.targetAgent || data.targetAgents && data.targetAgents.length > 0,
1672
1949
  { message: "Either targetAgent or targetAgents must be provided" }
1673
1950
  );
1674
- var WorkerCapabilitiesSchema = import_zod7.z.object({
1951
+ var WorkerCapabilitiesSchema = import_zod8.z.object({
1675
1952
  /**
1676
1953
  * Skills/capabilities the agent has
1677
1954
  */
1678
- skills: import_zod7.z.array(import_zod7.z.string()).describe("List of agent skills"),
1955
+ skills: import_zod8.z.array(import_zod8.z.string()).describe("List of agent skills"),
1679
1956
  /**
1680
1957
  * Tools available to the agent
1681
1958
  */
1682
- tools: import_zod7.z.array(import_zod7.z.string()).describe("List of tool names available to agent"),
1959
+ tools: import_zod8.z.array(import_zod8.z.string()).describe("List of tool names available to agent"),
1683
1960
  /**
1684
1961
  * Whether the agent is currently available
1685
1962
  */
1686
- available: import_zod7.z.boolean().default(true).describe("Whether agent is available"),
1963
+ available: import_zod8.z.boolean().default(true).describe("Whether agent is available"),
1687
1964
  /**
1688
1965
  * Current workload (number of active tasks)
1689
1966
  */
1690
- currentWorkload: import_zod7.z.number().int().nonnegative().default(0).describe("Current number of active tasks")
1967
+ currentWorkload: import_zod8.z.number().int().nonnegative().default(0).describe("Current number of active tasks")
1691
1968
  });
1692
- var TaskAssignmentSchema = import_zod7.z.object({
1969
+ var TaskAssignmentSchema = import_zod8.z.object({
1693
1970
  /**
1694
1971
  * Unique assignment identifier
1695
1972
  */
1696
- id: import_zod7.z.string().describe("Unique assignment identifier"),
1973
+ id: import_zod8.z.string().describe("Unique assignment identifier"),
1697
1974
  /**
1698
1975
  * Worker ID assigned to the task
1699
1976
  */
1700
- workerId: import_zod7.z.string().describe("Worker identifier assigned to task"),
1977
+ workerId: import_zod8.z.string().describe("Worker identifier assigned to task"),
1701
1978
  /**
1702
1979
  * Task description
1703
1980
  */
1704
- task: import_zod7.z.string().describe("Description of the task"),
1981
+ task: import_zod8.z.string().describe("Description of the task"),
1705
1982
  /**
1706
1983
  * Task priority (1-10, higher is more urgent)
1707
1984
  */
1708
- priority: import_zod7.z.number().int().min(1).max(10).default(5).describe("Task priority"),
1985
+ priority: import_zod8.z.number().int().min(1).max(10).default(5).describe("Task priority"),
1709
1986
  /**
1710
1987
  * Timestamp when task was assigned
1711
1988
  */
1712
- assignedAt: import_zod7.z.number().describe("Timestamp when task was assigned"),
1989
+ assignedAt: import_zod8.z.number().describe("Timestamp when task was assigned"),
1713
1990
  /**
1714
1991
  * Optional deadline for task completion
1715
1992
  */
1716
- deadline: import_zod7.z.number().optional().describe("Optional task deadline timestamp")
1993
+ deadline: import_zod8.z.number().optional().describe("Optional task deadline timestamp")
1717
1994
  });
1718
- var TaskResultSchema = import_zod7.z.object({
1995
+ var TaskResultSchema = import_zod8.z.object({
1719
1996
  /**
1720
1997
  * Assignment identifier
1721
1998
  */
1722
- assignmentId: import_zod7.z.string().describe("Assignment identifier"),
1999
+ assignmentId: import_zod8.z.string().describe("Assignment identifier"),
1723
2000
  /**
1724
2001
  * Worker that completed the task
1725
2002
  */
1726
- workerId: import_zod7.z.string().describe("Worker that completed the task"),
2003
+ workerId: import_zod8.z.string().describe("Worker that completed the task"),
1727
2004
  /**
1728
2005
  * Whether the task succeeded
1729
2006
  */
1730
- success: import_zod7.z.boolean().describe("Whether the task succeeded"),
2007
+ success: import_zod8.z.boolean().describe("Whether the task succeeded"),
1731
2008
  /**
1732
2009
  * Task result/output
1733
2010
  */
1734
- result: import_zod7.z.string().describe("Task result or output"),
2011
+ result: import_zod8.z.string().describe("Task result or output"),
1735
2012
  /**
1736
2013
  * Optional error message if task failed
1737
2014
  */
1738
- error: import_zod7.z.string().optional().describe("Error message if task failed"),
2015
+ error: import_zod8.z.string().optional().describe("Error message if task failed"),
1739
2016
  /**
1740
2017
  * Timestamp when task was completed
1741
2018
  */
1742
- completedAt: import_zod7.z.number().describe("Timestamp when task was completed"),
2019
+ completedAt: import_zod8.z.number().describe("Timestamp when task was completed"),
1743
2020
  /**
1744
2021
  * Optional metadata about execution
1745
2022
  */
1746
- metadata: import_zod7.z.record(import_zod7.z.any()).optional().describe("Execution metadata")
2023
+ metadata: import_zod8.z.record(import_zod8.z.any()).optional().describe("Execution metadata")
1747
2024
  });
1748
- var MultiAgentStatusSchema = import_zod7.z.enum([
2025
+ var MultiAgentStatusSchema = import_zod8.z.enum([
1749
2026
  "initializing",
1750
2027
  // System is initializing
1751
2028
  "routing",
@@ -1761,27 +2038,27 @@ var MultiAgentStatusSchema = import_zod7.z.enum([
1761
2038
  "failed"
1762
2039
  // Task failed
1763
2040
  ]);
1764
- var HandoffRequestSchema = import_zod7.z.object({
2041
+ var HandoffRequestSchema = import_zod8.z.object({
1765
2042
  /**
1766
2043
  * Agent requesting the handoff
1767
2044
  */
1768
- from: import_zod7.z.string().describe("Agent requesting handoff"),
2045
+ from: import_zod8.z.string().describe("Agent requesting handoff"),
1769
2046
  /**
1770
2047
  * Target agent for handoff
1771
2048
  */
1772
- to: import_zod7.z.string().describe("Target agent for handoff"),
2049
+ to: import_zod8.z.string().describe("Target agent for handoff"),
1773
2050
  /**
1774
2051
  * Reason for handoff
1775
2052
  */
1776
- reason: import_zod7.z.string().describe("Reason for requesting handoff"),
2053
+ reason: import_zod8.z.string().describe("Reason for requesting handoff"),
1777
2054
  /**
1778
2055
  * Context to pass to next agent
1779
2056
  */
1780
- context: import_zod7.z.any().describe("Context to pass to next agent"),
2057
+ context: import_zod8.z.any().describe("Context to pass to next agent"),
1781
2058
  /**
1782
2059
  * Timestamp of handoff request
1783
2060
  */
1784
- timestamp: import_zod7.z.string().datetime().describe("ISO timestamp of handoff request")
2061
+ timestamp: import_zod8.z.string().datetime().describe("ISO timestamp of handoff request")
1785
2062
  });
1786
2063
 
1787
2064
  // src/multi-agent/state.ts
@@ -1789,17 +2066,13 @@ var MultiAgentStateConfig = {
1789
2066
  /**
1790
2067
  * Original user input/query
1791
2068
  */
1792
- input: {
1793
- schema: import_zod8.z.string(),
1794
- default: () => "",
1795
- description: "Original user input or query"
1796
- },
2069
+ input: inputField,
1797
2070
  /**
1798
2071
  * All messages in the multi-agent conversation
1799
2072
  * Accumulates all messages between agents
1800
2073
  */
1801
2074
  messages: {
1802
- schema: import_zod8.z.array(AgentMessageSchema),
2075
+ schema: import_zod9.z.array(AgentMessageSchema),
1803
2076
  reducer: (left, right) => [...left, ...right],
1804
2077
  default: () => [],
1805
2078
  description: "All messages in the multi-agent conversation"
@@ -1808,7 +2081,7 @@ var MultiAgentStateConfig = {
1808
2081
  * Available worker agents and their capabilities
1809
2082
  */
1810
2083
  workers: {
1811
- schema: import_zod8.z.record(import_zod8.z.string(), WorkerCapabilitiesSchema),
2084
+ schema: import_zod9.z.record(import_zod9.z.string(), WorkerCapabilitiesSchema),
1812
2085
  reducer: (left, right) => ({
1813
2086
  ...left,
1814
2087
  ...right
@@ -1820,7 +2093,7 @@ var MultiAgentStateConfig = {
1820
2093
  * Current active agent
1821
2094
  */
1822
2095
  currentAgent: {
1823
- schema: import_zod8.z.string().optional(),
2096
+ schema: import_zod9.z.string().optional(),
1824
2097
  description: "Identifier of the currently active agent"
1825
2098
  },
1826
2099
  /**
@@ -1828,7 +2101,7 @@ var MultiAgentStateConfig = {
1828
2101
  * Accumulates all routing decisions
1829
2102
  */
1830
2103
  routingHistory: {
1831
- schema: import_zod8.z.array(RoutingDecisionSchema),
2104
+ schema: import_zod9.z.array(RoutingDecisionSchema),
1832
2105
  reducer: (left, right) => [...left, ...right],
1833
2106
  default: () => [],
1834
2107
  description: "History of routing decisions"
@@ -1837,7 +2110,7 @@ var MultiAgentStateConfig = {
1837
2110
  * Active task assignments
1838
2111
  */
1839
2112
  activeAssignments: {
1840
- schema: import_zod8.z.array(TaskAssignmentSchema),
2113
+ schema: import_zod9.z.array(TaskAssignmentSchema),
1841
2114
  reducer: (left, right) => [...left, ...right],
1842
2115
  default: () => [],
1843
2116
  description: "Currently active task assignments"
@@ -1847,7 +2120,7 @@ var MultiAgentStateConfig = {
1847
2120
  * Accumulates all completed tasks
1848
2121
  */
1849
2122
  completedTasks: {
1850
- schema: import_zod8.z.array(TaskResultSchema),
2123
+ schema: import_zod9.z.array(TaskResultSchema),
1851
2124
  reducer: (left, right) => [...left, ...right],
1852
2125
  default: () => [],
1853
2126
  description: "Completed task results"
@@ -1857,7 +2130,7 @@ var MultiAgentStateConfig = {
1857
2130
  * Accumulates all handoff requests
1858
2131
  */
1859
2132
  handoffs: {
1860
- schema: import_zod8.z.array(HandoffRequestSchema),
2133
+ schema: import_zod9.z.array(HandoffRequestSchema),
1861
2134
  reducer: (left, right) => [...left, ...right],
1862
2135
  default: () => [],
1863
2136
  description: "Handoff requests between agents"
@@ -1873,44 +2146,40 @@ var MultiAgentStateConfig = {
1873
2146
  /**
1874
2147
  * Iteration counter
1875
2148
  */
1876
- iteration: {
1877
- schema: import_zod8.z.number().int().nonnegative(),
1878
- reducer: (left, right) => left + right,
1879
- default: () => 0,
1880
- description: "Current iteration number"
1881
- },
2149
+ iteration: iterationField,
1882
2150
  /**
1883
2151
  * Maximum iterations allowed
1884
2152
  */
1885
- maxIterations: {
1886
- schema: import_zod8.z.number().int().positive(),
1887
- default: () => 10,
1888
- description: "Maximum number of iterations allowed"
1889
- },
2153
+ maxIterations: maxIterationsField(10),
1890
2154
  /**
1891
2155
  * Final aggregated response
1892
2156
  */
1893
- response: {
1894
- schema: import_zod8.z.string().optional(),
1895
- description: "Final aggregated response"
1896
- },
2157
+ response: responseField,
1897
2158
  /**
1898
2159
  * Error message if execution failed
1899
2160
  */
1900
- error: {
1901
- schema: import_zod8.z.string().optional(),
1902
- description: "Error message if execution failed"
1903
- }
2161
+ error: errorField
1904
2162
  };
1905
- var MultiAgentState = (0, import_core6.createStateAnnotation)(MultiAgentStateConfig);
2163
+ var MultiAgentState = (0, import_core7.createStateAnnotation)(MultiAgentStateConfig);
1906
2164
 
1907
2165
  // src/multi-agent/routing.ts
1908
2166
  var import_messages4 = require("@langchain/core/messages");
2167
+ var import_core8 = require("@agentforge/core");
2168
+ var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core8.LogLevel.INFO;
2169
+ var logger = (0, import_core8.createLogger)("multi-agent:routing", { level: logLevel });
1909
2170
  async function executeTools(toolCalls, tools) {
1910
2171
  const results = [];
2172
+ logger.debug("Executing tools", {
2173
+ toolCallCount: toolCalls.length,
2174
+ toolNames: toolCalls.map((tc) => tc.name)
2175
+ });
1911
2176
  for (const toolCall of toolCalls) {
1912
2177
  const tool = tools.find((t) => t.metadata.name === toolCall.name);
1913
2178
  if (!tool) {
2179
+ logger.warn("Tool not found", {
2180
+ toolName: toolCall.name,
2181
+ availableTools: tools.map((t) => t.metadata.name)
2182
+ });
1914
2183
  results.push(new import_messages4.ToolMessage({
1915
2184
  content: `Error: Tool '${toolCall.name}' not found`,
1916
2185
  tool_call_id: toolCall.id
@@ -1918,19 +2187,41 @@ async function executeTools(toolCalls, tools) {
1918
2187
  continue;
1919
2188
  }
1920
2189
  try {
2190
+ logger.debug("Executing tool", {
2191
+ toolName: toolCall.name,
2192
+ args: toolCall.args
2193
+ });
1921
2194
  const result = await tool.execute(toolCall.args);
1922
2195
  const content = typeof result === "string" ? result : JSON.stringify(result);
2196
+ logger.debug("Tool execution successful", {
2197
+ toolName: toolCall.name,
2198
+ resultLength: content.length
2199
+ });
1923
2200
  results.push(new import_messages4.ToolMessage({
1924
2201
  content,
1925
2202
  tool_call_id: toolCall.id
1926
2203
  }));
1927
2204
  } catch (error) {
2205
+ logger.error("Tool execution failed", {
2206
+ toolName: toolCall.name,
2207
+ error: error.message
2208
+ });
1928
2209
  results.push(new import_messages4.ToolMessage({
1929
2210
  content: `Error executing tool: ${error.message}`,
1930
2211
  tool_call_id: toolCall.id
1931
2212
  }));
1932
2213
  }
1933
2214
  }
2215
+ logger.debug("Tool execution complete", {
2216
+ successCount: results.filter((r) => {
2217
+ const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
2218
+ return !content.startsWith("Error");
2219
+ }).length,
2220
+ errorCount: results.filter((r) => {
2221
+ const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
2222
+ return content.startsWith("Error");
2223
+ }).length
2224
+ });
1934
2225
  return results;
1935
2226
  }
1936
2227
  var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
@@ -1968,6 +2259,10 @@ Choose parallel routing when the task benefits from multiple perspectives or dat
1968
2259
  var llmBasedRouting = {
1969
2260
  name: "llm-based",
1970
2261
  async route(state, config) {
2262
+ logger.info("Starting LLM-based routing", {
2263
+ iteration: state.iteration,
2264
+ availableWorkers: Object.keys(state.workers).length
2265
+ });
1971
2266
  if (!config.model) {
1972
2267
  throw new Error("LLM-based routing requires a model to be configured");
1973
2268
  }
@@ -1980,8 +2275,20 @@ var llmBasedRouting = {
1980
2275
  const available = caps.available ? "available" : "busy";
1981
2276
  return `- ${id}: Skills: [${skills}], Tools: [${tools2}], Status: ${available}, Workload: ${caps.currentWorkload}`;
1982
2277
  }).join("\n");
2278
+ logger.debug("Worker capabilities", {
2279
+ workers: Object.entries(state.workers).map(([id, caps]) => ({
2280
+ id,
2281
+ skills: caps.skills,
2282
+ available: caps.available,
2283
+ workload: caps.currentWorkload
2284
+ }))
2285
+ });
1983
2286
  const lastMessage = state.messages[state.messages.length - 1];
1984
2287
  const taskContext = lastMessage?.content || state.input;
2288
+ logger.debug("Task context", {
2289
+ taskLength: taskContext.length,
2290
+ taskPreview: taskContext.substring(0, 100)
2291
+ });
1985
2292
  const userPrompt = `Current task: ${taskContext}
1986
2293
 
1987
2294
  Available workers:
@@ -1991,6 +2298,11 @@ Select the best worker(s) for this task and explain your reasoning.`;
1991
2298
  const conversationHistory = [];
1992
2299
  let attempt = 0;
1993
2300
  while (attempt < maxRetries) {
2301
+ logger.debug("LLM routing attempt", {
2302
+ attempt: attempt + 1,
2303
+ maxRetries,
2304
+ conversationHistoryLength: conversationHistory.length
2305
+ });
1994
2306
  const messages = [
1995
2307
  new import_messages4.SystemMessage(systemPrompt),
1996
2308
  new import_messages4.HumanMessage(userPrompt),
@@ -1998,6 +2310,10 @@ Select the best worker(s) for this task and explain your reasoning.`;
1998
2310
  ];
1999
2311
  const response = await config.model.invoke(messages);
2000
2312
  if (response.tool_calls && response.tool_calls.length > 0) {
2313
+ logger.info("LLM requested tool calls", {
2314
+ toolCount: response.tool_calls.length,
2315
+ toolNames: response.tool_calls.map((tc) => tc.name)
2316
+ });
2001
2317
  if (tools.length === 0) {
2002
2318
  throw new Error("LLM requested tool calls but no tools are configured");
2003
2319
  }
@@ -2007,24 +2323,42 @@ Select the best worker(s) for this task and explain your reasoning.`;
2007
2323
  ...toolResults
2008
2324
  );
2009
2325
  attempt++;
2326
+ logger.debug("Retrying routing with tool results", { attempt });
2010
2327
  continue;
2011
2328
  }
2329
+ logger.debug("Parsing routing decision from LLM response");
2012
2330
  let decision;
2013
2331
  if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
2332
+ logger.debug("Response is structured output", {
2333
+ hasTargetAgent: "targetAgent" in response,
2334
+ hasTargetAgents: "targetAgents" in response
2335
+ });
2014
2336
  decision = response;
2015
2337
  } else if (response.content) {
2016
2338
  if (typeof response.content === "string") {
2017
2339
  try {
2018
2340
  decision = JSON.parse(response.content);
2341
+ logger.debug("Parsed JSON from string response");
2019
2342
  } catch (error) {
2343
+ logger.error("Failed to parse routing decision", {
2344
+ content: response.content,
2345
+ error: error instanceof Error ? error.message : String(error)
2346
+ });
2020
2347
  throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
2021
2348
  }
2022
2349
  } else if (typeof response.content === "object") {
2350
+ logger.debug("Response content is already an object");
2023
2351
  decision = response.content;
2024
2352
  } else {
2353
+ logger.error("Unexpected response content type", {
2354
+ type: typeof response.content
2355
+ });
2025
2356
  throw new Error(`Unexpected response content type: ${typeof response.content}`);
2026
2357
  }
2027
2358
  } else {
2359
+ logger.error("Unexpected response format", {
2360
+ response: JSON.stringify(response)
2361
+ });
2028
2362
  throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
2029
2363
  }
2030
2364
  const result = {
@@ -2035,20 +2369,41 @@ Select the best worker(s) for this task and explain your reasoning.`;
2035
2369
  strategy: "llm-based",
2036
2370
  timestamp: Date.now()
2037
2371
  };
2372
+ logger.info("LLM routing decision made", {
2373
+ targetAgent: result.targetAgent,
2374
+ targetAgents: result.targetAgents,
2375
+ isParallel: result.targetAgents && result.targetAgents.length > 1,
2376
+ confidence: result.confidence,
2377
+ reasoning: result.reasoning
2378
+ });
2038
2379
  return result;
2039
2380
  }
2381
+ logger.error("Max tool retries exceeded", { maxRetries });
2040
2382
  throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
2041
2383
  }
2042
2384
  };
2043
2385
  var roundRobinRouting = {
2044
2386
  name: "round-robin",
2045
2387
  async route(state, config) {
2388
+ logger.info("Starting round-robin routing", {
2389
+ iteration: state.iteration
2390
+ });
2046
2391
  const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
2392
+ logger.debug("Available workers for round-robin", {
2393
+ count: availableWorkers.length,
2394
+ workers: availableWorkers
2395
+ });
2047
2396
  if (availableWorkers.length === 0) {
2397
+ logger.error("No available workers for round-robin routing");
2048
2398
  throw new Error("No available workers for round-robin routing");
2049
2399
  }
2050
2400
  const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
2051
2401
  const targetAgent = availableWorkers[lastRoutingIndex];
2402
+ logger.info("Round-robin routing decision", {
2403
+ targetAgent,
2404
+ index: lastRoutingIndex + 1,
2405
+ totalWorkers: availableWorkers.length
2406
+ });
2052
2407
  return {
2053
2408
  targetAgent,
2054
2409
  targetAgents: null,
@@ -2062,8 +2417,15 @@ var roundRobinRouting = {
2062
2417
  var skillBasedRouting = {
2063
2418
  name: "skill-based",
2064
2419
  async route(state, config) {
2420
+ logger.info("Starting skill-based routing", {
2421
+ iteration: state.iteration
2422
+ });
2065
2423
  const lastMessage = state.messages[state.messages.length - 1];
2066
2424
  const taskContent = (lastMessage?.content || state.input).toLowerCase();
2425
+ logger.debug("Task content for skill matching", {
2426
+ taskLength: taskContent.length,
2427
+ taskPreview: taskContent.substring(0, 100)
2428
+ });
2067
2429
  const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
2068
2430
  const skillMatches = caps.skills.filter(
2069
2431
  (skill) => taskContent.includes(skill.toLowerCase())
@@ -2074,11 +2436,20 @@ var skillBasedRouting = {
2074
2436
  const score = skillMatches * 2 + toolMatches;
2075
2437
  return { id, score, skills: caps.skills, tools: caps.tools };
2076
2438
  }).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
2439
+ logger.debug("Worker skill scores", {
2440
+ scoredWorkers: workerScores.map((w) => ({ id: w.id, score: w.score }))
2441
+ });
2077
2442
  if (workerScores.length === 0) {
2443
+ logger.warn("No skill matches found, using fallback");
2078
2444
  const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
2079
2445
  if (!firstAvailable) {
2446
+ logger.error("No available workers for skill-based routing");
2080
2447
  throw new Error("No available workers for skill-based routing");
2081
2448
  }
2449
+ logger.info("Skill-based routing fallback decision", {
2450
+ targetAgent: firstAvailable[0],
2451
+ confidence: 0.5
2452
+ });
2082
2453
  return {
2083
2454
  targetAgent: firstAvailable[0],
2084
2455
  targetAgents: null,
@@ -2090,6 +2461,12 @@ var skillBasedRouting = {
2090
2461
  }
2091
2462
  const best = workerScores[0];
2092
2463
  const confidence = Math.min(best.score / 5, 1);
2464
+ logger.info("Skill-based routing decision", {
2465
+ targetAgent: best.id,
2466
+ score: best.score,
2467
+ confidence,
2468
+ matchedSkills: best.skills
2469
+ });
2093
2470
  return {
2094
2471
  targetAgent: best.id,
2095
2472
  targetAgents: null,
@@ -2103,13 +2480,26 @@ var skillBasedRouting = {
2103
2480
  var loadBalancedRouting = {
2104
2481
  name: "load-balanced",
2105
2482
  async route(state, config) {
2483
+ logger.info("Starting load-balanced routing", {
2484
+ iteration: state.iteration
2485
+ });
2106
2486
  const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
2487
+ logger.debug("Worker workloads", {
2488
+ workers: availableWorkers.map((w) => ({ id: w.id, workload: w.workload }))
2489
+ });
2107
2490
  if (availableWorkers.length === 0) {
2491
+ logger.error("No available workers for load-balanced routing");
2108
2492
  throw new Error("No available workers for load-balanced routing");
2109
2493
  }
2110
2494
  const targetWorker = availableWorkers[0];
2111
2495
  const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
2112
2496
  const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2497
+ logger.info("Load-balanced routing decision", {
2498
+ targetAgent: targetWorker.id,
2499
+ workload: targetWorker.workload,
2500
+ avgWorkload: avgWorkload.toFixed(1),
2501
+ confidence
2502
+ });
2113
2503
  return {
2114
2504
  targetAgent: targetWorker.id,
2115
2505
  targetAgents: null,
@@ -2123,13 +2513,24 @@ var loadBalancedRouting = {
2123
2513
  var ruleBasedRouting = {
2124
2514
  name: "rule-based",
2125
2515
  async route(state, config) {
2516
+ logger.info("Starting rule-based routing", {
2517
+ iteration: state.iteration
2518
+ });
2126
2519
  if (!config.routingFn) {
2520
+ logger.error("Rule-based routing requires a custom routing function");
2127
2521
  throw new Error("Rule-based routing requires a custom routing function");
2128
2522
  }
2129
- return await config.routingFn(state);
2523
+ const decision = await config.routingFn(state);
2524
+ logger.info("Rule-based routing decision", {
2525
+ targetAgent: decision.targetAgent,
2526
+ targetAgents: decision.targetAgents,
2527
+ confidence: decision.confidence
2528
+ });
2529
+ return decision;
2130
2530
  }
2131
2531
  };
2132
2532
  function getRoutingStrategy(name) {
2533
+ logger.debug("Getting routing strategy", { name });
2133
2534
  switch (name) {
2134
2535
  case "llm-based":
2135
2536
  return llmBasedRouting;
@@ -2142,14 +2543,15 @@ function getRoutingStrategy(name) {
2142
2543
  case "rule-based":
2143
2544
  return ruleBasedRouting;
2144
2545
  default:
2546
+ logger.error("Unknown routing strategy", { name });
2145
2547
  throw new Error(`Unknown routing strategy: ${name}`);
2146
2548
  }
2147
2549
  }
2148
2550
 
2149
2551
  // src/multi-agent/utils.ts
2150
- var import_core7 = require("@agentforge/core");
2151
- var logLevel = process.env.LOG_LEVEL?.toLowerCase() || import_core7.LogLevel.INFO;
2152
- var logger = (0, import_core7.createLogger)("multi-agent", { level: logLevel });
2552
+ var import_core9 = require("@agentforge/core");
2553
+ var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || import_core9.LogLevel.INFO;
2554
+ var logger2 = (0, import_core9.createLogger)("multi-agent", { level: logLevel2 });
2153
2555
  function isReActAgent(obj) {
2154
2556
  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
2155
2557
  (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
@@ -2157,9 +2559,9 @@ function isReActAgent(obj) {
2157
2559
  function wrapReActAgent(workerId, agent, verbose = false) {
2158
2560
  return async (state, config) => {
2159
2561
  try {
2160
- logger.debug("Wrapping ReAct agent execution", { workerId });
2562
+ logger2.debug("Wrapping ReAct agent execution", { workerId });
2161
2563
  const task = state.messages[state.messages.length - 1]?.content || state.input;
2162
- logger.debug("Extracted task", {
2564
+ logger2.debug("Extracted task", {
2163
2565
  workerId,
2164
2566
  taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
2165
2567
  });
@@ -2167,7 +2569,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2167
2569
  (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2168
2570
  );
2169
2571
  if (!currentAssignment) {
2170
- logger.debug("No active assignment found", { workerId });
2572
+ logger2.debug("No active assignment found", { workerId });
2171
2573
  return {};
2172
2574
  }
2173
2575
  const result = await agent.invoke(
@@ -2178,14 +2580,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2178
2580
  // Pass through the config for checkpointing and interrupt support
2179
2581
  );
2180
2582
  const response = result.messages?.[result.messages.length - 1]?.content || "No response";
2181
- logger.debug("Received response from ReAct agent", {
2583
+ logger2.debug("Received response from ReAct agent", {
2182
2584
  workerId,
2183
2585
  responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
2184
2586
  });
2185
2587
  const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
2186
2588
  const uniqueTools = [...new Set(toolsUsed)];
2187
2589
  if (uniqueTools.length > 0) {
2188
- logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2590
+ logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2189
2591
  }
2190
2592
  const taskResult = {
2191
2593
  assignmentId: currentAssignment.id,
@@ -2203,14 +2605,10 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2203
2605
  completedTasks: [taskResult]
2204
2606
  };
2205
2607
  } catch (error) {
2206
- if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
2207
- logger.debug("GraphInterrupt detected - re-throwing", { workerId });
2208
- throw error;
2209
- }
2210
- logger.error("Error in ReAct agent execution", {
2608
+ const errorMessage = handleNodeError(error, `react-agent:${workerId}`, false);
2609
+ logger2.error("Error in ReAct agent execution", {
2211
2610
  workerId,
2212
- error: error instanceof Error ? error.message : String(error),
2213
- stack: error instanceof Error ? error.stack : void 0
2611
+ error: errorMessage
2214
2612
  });
2215
2613
  const currentAssignment = state.activeAssignments.find(
2216
2614
  (assignment) => assignment.workerId === workerId
@@ -2221,7 +2619,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2221
2619
  workerId,
2222
2620
  success: false,
2223
2621
  result: "",
2224
- error: error instanceof Error ? error.message : "Unknown error in ReAct agent",
2622
+ error: errorMessage,
2225
2623
  completedAt: Date.now()
2226
2624
  };
2227
2625
  return {
@@ -2232,7 +2630,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2232
2630
  }
2233
2631
  return {
2234
2632
  status: "failed",
2235
- error: error instanceof Error ? error.message : `Unknown error in ReAct wrapper for ${workerId}`
2633
+ error: errorMessage
2236
2634
  };
2237
2635
  }
2238
2636
  };
@@ -2240,7 +2638,9 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2240
2638
 
2241
2639
  // src/multi-agent/nodes.ts
2242
2640
  var import_messages5 = require("@langchain/core/messages");
2243
- var import_core8 = require("@agentforge/core");
2641
+ var import_core10 = require("@agentforge/core");
2642
+ var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || import_core10.LogLevel.INFO;
2643
+ var logger3 = (0, import_core10.createLogger)("multi-agent:nodes", { level: logLevel3 });
2244
2644
  var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
2245
2645
 
2246
2646
  Your job is to:
@@ -2258,13 +2658,19 @@ function createSupervisorNode(config) {
2258
2658
  } = config;
2259
2659
  return async (state) => {
2260
2660
  try {
2261
- if (verbose) {
2262
- console.log(`[Supervisor] Routing iteration ${state.iteration}/${maxIterations}`);
2263
- }
2661
+ logger3.info("Supervisor node executing", {
2662
+ iteration: state.iteration,
2663
+ maxIterations,
2664
+ activeAssignments: state.activeAssignments.length,
2665
+ completedTasks: state.completedTasks.length
2666
+ });
2667
+ logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
2264
2668
  if (state.iteration >= maxIterations) {
2265
- if (verbose) {
2266
- console.log("[Supervisor] Max iterations reached, moving to aggregation");
2267
- }
2669
+ logger3.warn("Max iterations reached", {
2670
+ iteration: state.iteration,
2671
+ maxIterations
2672
+ });
2673
+ logger3.debug("Max iterations reached, moving to aggregation");
2268
2674
  return {
2269
2675
  status: "aggregating",
2270
2676
  currentAgent: "aggregator"
@@ -2273,27 +2679,55 @@ function createSupervisorNode(config) {
2273
2679
  const allCompleted = state.activeAssignments.every(
2274
2680
  (assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2275
2681
  );
2682
+ logger3.debug("Checking task completion", {
2683
+ activeAssignments: state.activeAssignments.length,
2684
+ completedTasks: state.completedTasks.length,
2685
+ allCompleted
2686
+ });
2276
2687
  if (allCompleted && state.activeAssignments.length > 0) {
2277
- if (verbose) {
2278
- console.log("[Supervisor] All tasks completed, moving to aggregation");
2279
- }
2688
+ logger3.info("All tasks completed, moving to aggregation", {
2689
+ completedCount: state.completedTasks.length
2690
+ });
2691
+ logger3.debug("All tasks completed, moving to aggregation");
2280
2692
  return {
2281
2693
  status: "aggregating",
2282
2694
  currentAgent: "aggregator"
2283
2695
  };
2284
2696
  }
2697
+ logger3.debug("Getting routing strategy", { strategy });
2285
2698
  const routingImpl = getRoutingStrategy(strategy);
2286
2699
  const decision = await routingImpl.route(state, config);
2287
2700
  const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
2701
+ logger3.debug("Target agents determined", {
2702
+ targetAgents,
2703
+ isParallel: targetAgents.length > 1,
2704
+ decision: {
2705
+ reasoning: decision.reasoning,
2706
+ confidence: decision.confidence
2707
+ }
2708
+ });
2288
2709
  if (targetAgents.length === 0) {
2710
+ logger3.error("No target agents specified in routing decision");
2289
2711
  throw new Error("Routing decision must specify at least one target agent");
2290
2712
  }
2291
- if (verbose) {
2292
- if (targetAgents.length === 1) {
2293
- console.log(`[Supervisor] Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2294
- } else {
2295
- console.log(`[Supervisor] Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2296
- }
2713
+ if (targetAgents.length === 1) {
2714
+ logger3.info("Routing to single agent", {
2715
+ targetAgent: targetAgents[0],
2716
+ reasoning: decision.reasoning,
2717
+ confidence: decision.confidence
2718
+ });
2719
+ } else {
2720
+ logger3.info("Routing to multiple agents in parallel", {
2721
+ targetAgents,
2722
+ count: targetAgents.length,
2723
+ reasoning: decision.reasoning,
2724
+ confidence: decision.confidence
2725
+ });
2726
+ }
2727
+ if (targetAgents.length === 1) {
2728
+ logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2729
+ } else {
2730
+ logger3.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2297
2731
  }
2298
2732
  const task = state.messages[state.messages.length - 1]?.content || state.input;
2299
2733
  const assignments = targetAgents.map((workerId) => ({
@@ -2303,6 +2737,14 @@ function createSupervisorNode(config) {
2303
2737
  priority: 5,
2304
2738
  assignedAt: Date.now()
2305
2739
  }));
2740
+ logger3.debug("Created task assignments", {
2741
+ assignmentCount: assignments.length,
2742
+ assignments: assignments.map((a) => ({
2743
+ id: a.id,
2744
+ workerId: a.workerId,
2745
+ taskLength: a.task.length
2746
+ }))
2747
+ });
2306
2748
  const messages = assignments.map((assignment) => ({
2307
2749
  id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2308
2750
  from: "supervisor",
@@ -2315,6 +2757,12 @@ function createSupervisorNode(config) {
2315
2757
  priority: assignment.priority
2316
2758
  }
2317
2759
  }));
2760
+ logger3.info("Supervisor routing complete", {
2761
+ currentAgent: targetAgents.join(","),
2762
+ status: "executing",
2763
+ assignmentCount: assignments.length,
2764
+ nextIteration: state.iteration + 1
2765
+ });
2318
2766
  return {
2319
2767
  currentAgent: targetAgents.join(","),
2320
2768
  // Store all agents (for backward compat)
@@ -2326,7 +2774,11 @@ function createSupervisorNode(config) {
2326
2774
  iteration: state.iteration + 1
2327
2775
  };
2328
2776
  } catch (error) {
2329
- console.error("[Supervisor] Error:", error);
2777
+ logger3.error("Supervisor node error", {
2778
+ error: error instanceof Error ? error.message : String(error),
2779
+ stack: error instanceof Error ? error.stack : void 0,
2780
+ iteration: state.iteration
2781
+ });
2330
2782
  return {
2331
2783
  status: "failed",
2332
2784
  error: error instanceof Error ? error.message : "Unknown error in supervisor"
@@ -2347,40 +2799,52 @@ function createWorkerNode(config) {
2347
2799
  } = config;
2348
2800
  return async (state, runConfig) => {
2349
2801
  try {
2350
- if (verbose) {
2351
- console.log(`[Worker:${id}] Executing task`);
2352
- }
2802
+ logger3.info("Worker node executing", {
2803
+ workerId: id,
2804
+ iteration: state.iteration,
2805
+ activeAssignments: state.activeAssignments.length
2806
+ });
2353
2807
  const currentAssignment = state.activeAssignments.find(
2354
2808
  (assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
2355
2809
  );
2356
2810
  if (!currentAssignment) {
2357
- if (verbose) {
2358
- console.log(`[Worker:${id}] No active assignment found`);
2359
- }
2811
+ logger3.debug("No active assignment found for worker", {
2812
+ workerId: id,
2813
+ totalActiveAssignments: state.activeAssignments.length,
2814
+ completedTasks: state.completedTasks.length
2815
+ });
2360
2816
  return {};
2361
2817
  }
2818
+ logger3.info("Worker processing assignment", {
2819
+ workerId: id,
2820
+ assignmentId: currentAssignment.id,
2821
+ taskLength: currentAssignment.task.length,
2822
+ taskPreview: currentAssignment.task.substring(0, 100)
2823
+ });
2362
2824
  if (executeFn) {
2363
- if (verbose) {
2364
- console.log(`[Worker:${id}] Using custom executeFn`);
2365
- }
2825
+ logger3.debug("Using custom execution function", { workerId: id });
2366
2826
  return await executeFn(state, runConfig);
2367
2827
  }
2368
2828
  if (agent) {
2369
2829
  if (isReActAgent(agent)) {
2370
- if (verbose) {
2371
- console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
2372
- }
2830
+ logger3.debug("Using ReAct agent", { workerId: id });
2373
2831
  const wrappedFn = wrapReActAgent(id, agent, verbose);
2374
2832
  return await wrappedFn(state, runConfig);
2375
2833
  } else {
2376
- console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
2834
+ logger3.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
2377
2835
  }
2378
2836
  }
2379
2837
  if (!model) {
2838
+ logger3.error("Worker missing required configuration", { workerId: id });
2380
2839
  throw new Error(
2381
2840
  `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2382
2841
  );
2383
2842
  }
2843
+ logger3.debug("Using default LLM execution", {
2844
+ workerId: id,
2845
+ hasTools: tools.length > 0,
2846
+ toolCount: tools.length
2847
+ });
2384
2848
  const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2385
2849
  Skills: ${capabilities.skills.join(", ")}
2386
2850
  Tools: ${capabilities.tools.join(", ")}
@@ -2392,14 +2856,23 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2392
2856
  ];
2393
2857
  let modelToUse = model;
2394
2858
  if (tools.length > 0 && model.bindTools) {
2395
- const langchainTools = (0, import_core8.toLangChainTools)(tools);
2859
+ logger3.debug("Binding tools to model", {
2860
+ workerId: id,
2861
+ toolCount: tools.length,
2862
+ toolNames: tools.map((t) => t.metadata.name)
2863
+ });
2864
+ const langchainTools = (0, import_core10.toLangChainTools)(tools);
2396
2865
  modelToUse = model.bindTools(langchainTools);
2397
2866
  }
2867
+ logger3.debug("Invoking LLM", { workerId: id });
2398
2868
  const response = await modelToUse.invoke(messages);
2399
2869
  const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2400
- if (verbose) {
2401
- console.log(`[Worker:${id}] Task completed:`, result.substring(0, 100) + "...");
2402
- }
2870
+ logger3.info("Worker task completed", {
2871
+ workerId: id,
2872
+ assignmentId: currentAssignment.id,
2873
+ resultLength: result.length,
2874
+ resultPreview: result.substring(0, 100)
2875
+ });
2403
2876
  const taskResult = {
2404
2877
  assignmentId: currentAssignment.id,
2405
2878
  workerId: id,
@@ -2429,26 +2902,35 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2429
2902
  currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
2430
2903
  }
2431
2904
  };
2905
+ logger3.debug("Worker state update", {
2906
+ workerId: id,
2907
+ newWorkload: updatedWorkers[id].currentWorkload
2908
+ });
2432
2909
  return {
2433
2910
  completedTasks: [taskResult],
2434
2911
  messages: [message],
2435
2912
  workers: updatedWorkers
2436
2913
  };
2437
2914
  } catch (error) {
2438
- if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
2439
- throw error;
2440
- }
2441
- console.error(`[Worker:${id}] Error:`, error);
2915
+ const errorMessage = handleNodeError(error, `worker:${id}`, false);
2916
+ logger3.error("Worker node error", {
2917
+ workerId: id,
2918
+ error: errorMessage
2919
+ });
2442
2920
  const currentAssignment = state.activeAssignments.find(
2443
2921
  (assignment) => assignment.workerId === id
2444
2922
  );
2445
2923
  if (currentAssignment) {
2924
+ logger3.warn("Creating error result for assignment", {
2925
+ workerId: id,
2926
+ assignmentId: currentAssignment.id
2927
+ });
2446
2928
  const errorResult = {
2447
2929
  assignmentId: currentAssignment.id,
2448
2930
  workerId: id,
2449
2931
  success: false,
2450
2932
  result: "",
2451
- error: error instanceof Error ? error.message : "Unknown error",
2933
+ error: errorMessage,
2452
2934
  completedAt: Date.now()
2453
2935
  };
2454
2936
  return {
@@ -2457,9 +2939,10 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2457
2939
  status: "routing"
2458
2940
  };
2459
2941
  }
2942
+ logger3.error("No assignment found for error handling", { workerId: id });
2460
2943
  return {
2461
2944
  status: "failed",
2462
- error: error instanceof Error ? error.message : `Unknown error in worker ${id}`
2945
+ error: errorMessage
2463
2946
  };
2464
2947
  }
2465
2948
  };
@@ -2473,29 +2956,44 @@ function createAggregatorNode(config = {}) {
2473
2956
  } = config;
2474
2957
  return async (state) => {
2475
2958
  try {
2476
- if (verbose) {
2477
- console.log("[Aggregator] Combining results from workers");
2478
- }
2959
+ logger3.info("Aggregator node executing", {
2960
+ completedTasks: state.completedTasks.length,
2961
+ successfulTasks: state.completedTasks.filter((t) => t.success).length,
2962
+ failedTasks: state.completedTasks.filter((t) => !t.success).length
2963
+ });
2964
+ logger3.debug("Combining results from workers");
2479
2965
  if (aggregateFn) {
2966
+ logger3.debug("Using custom aggregation function");
2480
2967
  const response2 = await aggregateFn(state);
2968
+ logger3.info("Custom aggregation complete", {
2969
+ responseLength: response2.length
2970
+ });
2481
2971
  return {
2482
2972
  response: response2,
2483
2973
  status: "completed"
2484
2974
  };
2485
2975
  }
2486
2976
  if (state.completedTasks.length === 0) {
2977
+ logger3.warn("No completed tasks to aggregate");
2487
2978
  return {
2488
2979
  response: "No tasks were completed.",
2489
2980
  status: "completed"
2490
2981
  };
2491
2982
  }
2492
2983
  if (!model) {
2984
+ logger3.debug("No model provided, concatenating results");
2493
2985
  const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2986
+ logger3.info("Simple concatenation complete", {
2987
+ resultLength: combinedResults.length
2988
+ });
2494
2989
  return {
2495
2990
  response: combinedResults || "No successful results to aggregate.",
2496
2991
  status: "completed"
2497
2992
  };
2498
2993
  }
2994
+ logger3.debug("Using LLM for intelligent aggregation", {
2995
+ taskCount: state.completedTasks.length
2996
+ });
2499
2997
  const taskResults = state.completedTasks.map((task, idx) => {
2500
2998
  const status = task.success ? "\u2713" : "\u2717";
2501
2999
  const result = task.success ? task.result : `Error: ${task.error}`;
@@ -2512,17 +3010,24 @@ Please synthesize these results into a comprehensive response that addresses the
2512
3010
  new import_messages5.SystemMessage(systemPrompt),
2513
3011
  new import_messages5.HumanMessage(userPrompt)
2514
3012
  ];
3013
+ logger3.debug("Invoking aggregation LLM");
2515
3014
  const response = await model.invoke(messages);
2516
3015
  const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2517
- if (verbose) {
2518
- console.log("[Aggregator] Aggregation complete");
2519
- }
3016
+ logger3.info("Aggregation complete", {
3017
+ responseLength: aggregatedResponse.length,
3018
+ responsePreview: aggregatedResponse.substring(0, 100)
3019
+ });
3020
+ logger3.debug("Aggregation complete");
2520
3021
  return {
2521
3022
  response: aggregatedResponse,
2522
3023
  status: "completed"
2523
3024
  };
2524
3025
  } catch (error) {
2525
- console.error("[Aggregator] Error:", error);
3026
+ logger3.error("Aggregator node error", {
3027
+ error: error instanceof Error ? error.message : String(error),
3028
+ stack: error instanceof Error ? error.stack : void 0,
3029
+ completedTasks: state.completedTasks.length
3030
+ });
2526
3031
  return {
2527
3032
  status: "failed",
2528
3033
  error: error instanceof Error ? error.message : "Unknown error in aggregator"
@@ -2533,7 +3038,9 @@ Please synthesize these results into a comprehensive response that addresses the
2533
3038
 
2534
3039
  // src/multi-agent/agent.ts
2535
3040
  var import_langgraph4 = require("@langchain/langgraph");
2536
- var import_core9 = require("@agentforge/core");
3041
+ var import_core11 = require("@agentforge/core");
3042
+ var logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || import_core11.LogLevel.INFO;
3043
+ var logger4 = (0, import_core11.createLogger)("multi-agent:system", { level: logLevel4 });
2537
3044
  function createMultiAgentSystem(config) {
2538
3045
  const {
2539
3046
  supervisor,
@@ -2551,7 +3058,7 @@ function createMultiAgentSystem(config) {
2551
3058
  configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
2552
3059
  }
2553
3060
  if (supervisor.tools && supervisor.tools.length > 0) {
2554
- const langchainTools = (0, import_core9.toLangChainTools)(supervisor.tools);
3061
+ const langchainTools = (0, import_core11.toLangChainTools)(supervisor.tools);
2555
3062
  configuredModel = configuredModel.bindTools(langchainTools);
2556
3063
  }
2557
3064
  supervisorConfig.model = configuredModel;
@@ -2575,25 +3082,49 @@ function createMultiAgentSystem(config) {
2575
3082
  });
2576
3083
  workflow.addNode("aggregator", aggregatorNode);
2577
3084
  const supervisorRouter = (state) => {
3085
+ logger4.debug("Supervisor router executing", {
3086
+ status: state.status,
3087
+ currentAgent: state.currentAgent,
3088
+ iteration: state.iteration
3089
+ });
2578
3090
  if (state.status === "completed" || state.status === "failed") {
3091
+ logger4.info("Supervisor router: ending workflow", { status: state.status });
2579
3092
  return import_langgraph4.END;
2580
3093
  }
2581
3094
  if (state.status === "aggregating") {
3095
+ logger4.info("Supervisor router: routing to aggregator");
2582
3096
  return "aggregator";
2583
3097
  }
2584
3098
  if (state.currentAgent && state.currentAgent !== "supervisor") {
2585
3099
  if (state.currentAgent.includes(",")) {
2586
3100
  const agents = state.currentAgent.split(",").map((a) => a.trim());
3101
+ logger4.info("Supervisor router: parallel routing", {
3102
+ agents,
3103
+ count: agents.length
3104
+ });
2587
3105
  return agents;
2588
3106
  }
3107
+ logger4.info("Supervisor router: single agent routing", {
3108
+ targetAgent: state.currentAgent
3109
+ });
2589
3110
  return state.currentAgent;
2590
3111
  }
3112
+ logger4.debug("Supervisor router: staying at supervisor");
2591
3113
  return "supervisor";
2592
3114
  };
2593
3115
  const workerRouter = (state) => {
3116
+ logger4.debug("Worker router executing", {
3117
+ iteration: state.iteration,
3118
+ completedTasks: state.completedTasks.length
3119
+ });
3120
+ logger4.debug("Worker router: returning to supervisor");
2594
3121
  return "supervisor";
2595
3122
  };
2596
3123
  const aggregatorRouter = (state) => {
3124
+ logger4.info("Aggregator router: ending workflow", {
3125
+ completedTasks: state.completedTasks.length,
3126
+ status: state.status
3127
+ });
2597
3128
  return import_langgraph4.END;
2598
3129
  };
2599
3130
  workflow.setEntryPoint("supervisor");
@@ -2787,11 +3318,14 @@ function registerWorkers(system, workers) {
2787
3318
  ToolCallSchema,
2788
3319
  ToolResultSchema,
2789
3320
  WorkerCapabilitiesSchema,
3321
+ buildDeduplicationMetrics,
3322
+ calculateDeduplicationSavings,
2790
3323
  createAggregatorNode,
2791
3324
  createExecutorNode,
2792
3325
  createFinisherNode,
2793
3326
  createGeneratorNode,
2794
3327
  createMultiAgentSystem,
3328
+ createPatternLogger,
2795
3329
  createPlanExecuteAgent,
2796
3330
  createPlannerNode,
2797
3331
  createReActAgent,
@@ -2803,6 +3337,7 @@ function registerWorkers(system, workers) {
2803
3337
  createReviserNode,
2804
3338
  createSupervisorNode,
2805
3339
  createWorkerNode,
3340
+ generateToolCallCacheKey,
2806
3341
  getRoutingStrategy,
2807
3342
  llmBasedRouting,
2808
3343
  loadBalancedRouting,