@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.js CHANGED
@@ -22,7 +22,9 @@ var ToolResultSchema = z.object({
22
22
  toolCallId: z.string(),
23
23
  result: z.any(),
24
24
  error: z.string().optional(),
25
- timestamp: z.number().optional()
25
+ timestamp: z.number().optional(),
26
+ isDuplicate: z.boolean().optional()
27
+ // Flag indicating this was a duplicate tool call
26
28
  });
27
29
  var ScratchpadEntrySchema = z.object({
28
30
  step: z.number(),
@@ -33,15 +35,46 @@ var ScratchpadEntrySchema = z.object({
33
35
  });
34
36
 
35
37
  // src/react/state.ts
36
- import { z as z2 } from "zod";
38
+ import { z as z3 } from "zod";
37
39
  import { createStateAnnotation } from "@agentforge/core";
40
+
41
+ // src/shared/state-fields.ts
42
+ import { z as z2 } from "zod";
43
+ var iterationField = {
44
+ schema: z2.number().int().nonnegative(),
45
+ reducer: (left, right) => left + right,
46
+ default: () => 0,
47
+ description: "Current iteration number"
48
+ };
49
+ function maxIterationsField(defaultValue) {
50
+ return {
51
+ schema: z2.number().int().positive(),
52
+ default: () => defaultValue,
53
+ description: "Maximum number of iterations allowed"
54
+ };
55
+ }
56
+ var errorField = {
57
+ schema: z2.string().optional(),
58
+ description: "Error message if execution failed"
59
+ };
60
+ var responseField = {
61
+ schema: z2.string().optional(),
62
+ description: "Final response after completion"
63
+ };
64
+ var inputField = {
65
+ schema: z2.string(),
66
+ default: () => "",
67
+ description: "Original user input or query"
68
+ };
69
+
70
+ // src/react/state.ts
38
71
  var ReActStateConfig = {
39
72
  /**
40
73
  * Conversation messages
41
74
  * Accumulates all messages in the conversation
42
75
  */
43
76
  messages: {
44
- schema: z2.array(MessageSchema),
77
+ schema: z3.array(MessageSchema),
45
78
  reducer: (left, right) => [...left, ...right],
46
79
  default: () => [],
47
80
  description: "Conversation message history"
@@ -51,7 +84,7 @@ var ReActStateConfig = {
51
84
  * Accumulates all reasoning steps the agent takes
52
85
  */
53
86
  thoughts: {
54
- schema: z2.array(ThoughtSchema),
87
+ schema: z3.array(ThoughtSchema),
55
88
  reducer: (left, right) => [...left, ...right],
56
89
  default: () => [],
57
90
  description: "Agent reasoning steps"
@@ -61,7 +94,7 @@ var ReActStateConfig = {
61
94
  * Accumulates all tool calls made by the agent
62
95
  */
63
96
  actions: {
64
- schema: z2.array(ToolCallSchema),
97
+ schema: z3.array(ToolCallSchema),
65
98
  reducer: (left, right) => [...left, ...right],
66
99
  default: () => [],
67
100
  description: "Tool calls made by the agent"
@@ -71,7 +104,7 @@ var ReActStateConfig = {
71
104
  * Accumulates all observations from tool executions
72
105
  */
73
106
  observations: {
74
- schema: z2.array(ToolResultSchema),
107
+ schema: z3.array(ToolResultSchema),
75
108
  reducer: (left, right) => [...left, ...right],
76
109
  default: () => [],
77
110
  description: "Results from tool executions"
@@ -81,7 +114,7 @@ var ReActStateConfig = {
81
114
  * Accumulates step-by-step reasoning process
82
115
  */
83
116
  scratchpad: {
84
- schema: z2.array(ScratchpadEntrySchema),
117
+ schema: z3.array(ScratchpadEntrySchema),
85
118
  reducer: (left, right) => [...left, ...right],
86
119
  default: () => [],
87
120
  description: "Intermediate reasoning scratchpad"
@@ -90,27 +123,19 @@ var ReActStateConfig = {
90
123
  * Current iteration count
91
124
  * Tracks how many thought-action-observation loops have been executed
92
125
  */
93
- iteration: {
94
- schema: z2.number(),
95
- reducer: (left, right) => left + right,
96
- default: () => 0,
97
- description: "Current iteration count"
98
- },
126
+ iteration: iterationField,
99
127
  /**
100
128
  * Whether the agent should continue iterating
101
129
  */
102
130
  shouldContinue: {
103
- schema: z2.boolean().optional(),
131
+ schema: z3.boolean().optional(),
104
132
  default: () => true,
105
133
  description: "Whether to continue the ReAct loop"
106
134
  },
107
135
  /**
108
136
  * Final response (if any)
109
137
  */
110
- response: {
111
- schema: z2.string().optional(),
112
- description: "Final response from the agent"
113
- }
138
+ response: responseField
114
139
  };
115
140
  var ReActState = createStateAnnotation(ReActStateConfig);
116
141
 
@@ -147,13 +172,67 @@ function formatScratchpad(scratchpad) {
147
172
  // src/react/nodes.ts
148
173
  import { HumanMessage, AIMessage, SystemMessage } from "@langchain/core/messages";
149
174
  import { toLangChainTools } from "@agentforge/core";
175
+
176
+ // src/shared/deduplication.ts
177
+ import { createLogger } from "@agentforge/core";
178
+ function generateToolCallCacheKey(toolName, args) {
179
+ const sortedArgs = JSON.stringify(args, Object.keys(args || {}).sort());
180
+ return `${toolName}:${sortedArgs}`;
181
+ }
182
+ function createPatternLogger(name, defaultLevel = "info") {
183
+ const logLevel5 = process.env.LOG_LEVEL?.toLowerCase() || defaultLevel;
184
+ return createLogger(name, { level: logLevel5 });
185
+ }
186
+ function calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted) {
187
+ if (duplicatesSkipped === 0) {
188
+ return "0%";
189
+ }
190
+ const total = toolsExecuted + duplicatesSkipped;
191
+ return `${Math.round(duplicatesSkipped / total * 100)}%`;
192
+ }
193
+ function buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, totalObservations) {
194
+ return {
195
+ toolsExecuted,
196
+ duplicatesSkipped,
197
+ totalObservations,
198
+ deduplicationSavings: calculateDeduplicationSavings(duplicatesSkipped, toolsExecuted)
199
+ };
200
+ }
201
+
202
+ // src/shared/error-handling.ts
203
+ function isGraphInterrupt(error) {
204
+ return error !== null && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt";
205
+ }
206
+ function handleNodeError(error, context, verbose = false) {
207
+ if (isGraphInterrupt(error)) {
208
+ throw error;
209
+ }
210
+ const errorMessage = error instanceof Error ? error.message : String(error);
211
+ if (verbose) {
212
+ console.error(`[${context}] Error:`, errorMessage);
213
+ if (error instanceof Error && error.stack) {
214
+ console.error(`[${context}] Stack:`, error.stack);
215
+ }
216
+ }
217
+ return errorMessage;
218
+ }
219
+
220
+ // src/react/nodes.ts
221
+ var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
222
+ var actionLogger = createPatternLogger("agentforge:patterns:react:action");
223
+ var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
150
224
  function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
151
225
  const langchainTools = toLangChainTools(tools);
152
226
  const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
153
227
  return async (state) => {
154
- if (verbose) {
155
- console.log(`[reasoning] Iteration ${state.iteration + 1}/${maxIterations}`);
156
- }
228
+ const currentIteration = state.iteration || 0;
229
+ const startTime = Date.now();
230
+ reasoningLogger.debug("Reasoning iteration started", {
231
+ iteration: currentIteration + 1,
232
+ maxIterations,
233
+ observationCount: state.observations?.length || 0,
234
+ hasActions: !!state.actions?.length
235
+ });
157
236
  const stateMessages = state.messages || [];
158
237
  const messages = [
159
238
  new SystemMessage(systemPrompt),
@@ -183,8 +262,15 @@ ${scratchpadText}`));
183
262
  });
184
263
  }
185
264
  }
186
- const currentIteration = state.iteration || 0;
187
265
  const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
266
+ reasoningLogger.info("Reasoning complete", {
267
+ iteration: currentIteration + 1,
268
+ thoughtGenerated: !!thought,
269
+ actionCount: toolCalls.length,
270
+ shouldContinue,
271
+ isFinalResponse: toolCalls.length === 0,
272
+ duration: Date.now() - startTime
273
+ });
188
274
  return {
189
275
  messages: [{ role: "assistant", content: thought }],
190
276
  thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
@@ -197,16 +283,71 @@ ${scratchpadText}`));
197
283
  };
198
284
  };
199
285
  }
200
- function createActionNode(tools, verbose = false) {
286
+ function createActionNode(tools, verbose = false, enableDeduplication = true) {
201
287
  const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
202
288
  return async (state) => {
203
289
  const actions = state.actions || [];
204
- if (verbose) {
205
- console.log(`[action] Executing ${actions.length} tool calls`);
206
- }
290
+ const allObservations = state.observations || [];
291
+ const iteration = state.iteration || 0;
292
+ const startTime = Date.now();
293
+ actionLogger.debug("Action node started", {
294
+ actionCount: actions.length,
295
+ iteration,
296
+ cacheEnabled: enableDeduplication
297
+ });
207
298
  const recentActions = actions.slice(-10);
208
299
  const observations = [];
300
+ const executionCache = /* @__PURE__ */ new Map();
301
+ let cacheSize = 0;
302
+ if (enableDeduplication) {
303
+ for (const observation of allObservations) {
304
+ const correspondingAction = actions.find((a) => a.id === observation.toolCallId);
305
+ if (correspondingAction) {
306
+ const cacheKey = generateToolCallCacheKey(correspondingAction.name, correspondingAction.arguments);
307
+ executionCache.set(cacheKey, observation);
308
+ cacheSize++;
309
+ }
310
+ }
311
+ if (cacheSize > 0) {
312
+ actionLogger.debug("Deduplication cache built", {
313
+ cacheSize,
314
+ totalObservations: allObservations.length
315
+ });
316
+ }
317
+ }
318
+ let duplicatesSkipped = 0;
319
+ let toolsExecuted = 0;
209
320
  for (const action of recentActions) {
321
+ const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
322
+ if (existingObservation) {
323
+ actionLogger.debug("Skipping already-processed action", {
324
+ toolName: action.name,
325
+ toolCallId: action.id,
326
+ iteration
327
+ });
328
+ continue;
329
+ }
330
+ if (enableDeduplication) {
331
+ const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
332
+ const cachedResult = executionCache.get(cacheKey);
333
+ if (cachedResult) {
334
+ duplicatesSkipped++;
335
+ actionLogger.info("Duplicate tool call prevented", {
336
+ toolName: action.name,
337
+ arguments: action.arguments,
338
+ iteration,
339
+ cacheHit: true
340
+ });
341
+ observations.push({
342
+ toolCallId: action.id,
343
+ result: cachedResult.result,
344
+ error: cachedResult.error,
345
+ timestamp: Date.now(),
346
+ isDuplicate: true
347
+ });
348
+ continue;
349
+ }
350
+ }
210
351
  const tool = toolMap.get(action.name);
211
352
  if (!tool) {
212
353
  observations.push({
@@ -218,31 +359,48 @@ function createActionNode(tools, verbose = false) {
218
359
  continue;
219
360
  }
220
361
  try {
362
+ const startTime2 = Date.now();
221
363
  const result = await tool.execute(action.arguments);
222
- observations.push({
364
+ const executionTime = Date.now() - startTime2;
365
+ toolsExecuted++;
366
+ actionLogger.debug("Tool executed successfully", {
367
+ toolName: action.name,
368
+ executionTime,
369
+ iteration
370
+ });
371
+ const observation = {
223
372
  toolCallId: action.id,
224
373
  result,
225
374
  timestamp: Date.now()
226
- });
227
- if (verbose) {
228
- console.log(`[action] Tool '${action.name}' executed successfully`);
375
+ };
376
+ observations.push(observation);
377
+ if (enableDeduplication) {
378
+ const cacheKey = generateToolCallCacheKey(action.name, action.arguments);
379
+ executionCache.set(cacheKey, observation);
229
380
  }
230
381
  } catch (error) {
231
- if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
232
- throw error;
233
- }
234
- const errorMessage = error instanceof Error ? error.message : String(error);
382
+ const errorMessage = handleNodeError(error, `action:${action.name}`, false);
383
+ actionLogger.error("Tool execution failed", {
384
+ toolName: action.name,
385
+ error: errorMessage,
386
+ iteration
387
+ });
235
388
  observations.push({
236
389
  toolCallId: action.id,
237
390
  result: null,
238
391
  error: errorMessage,
239
392
  timestamp: Date.now()
240
393
  });
241
- if (verbose) {
242
- console.error(`[action] Tool '${action.name}' failed:`, errorMessage);
243
- }
244
394
  }
245
395
  }
396
+ if (duplicatesSkipped > 0 || toolsExecuted > 0) {
397
+ const metrics = buildDeduplicationMetrics(toolsExecuted, duplicatesSkipped, observations.length);
398
+ actionLogger.info("Action node complete", {
399
+ iteration,
400
+ ...metrics,
401
+ duration: Date.now() - startTime
402
+ });
403
+ }
246
404
  return {
247
405
  observations
248
406
  };
@@ -253,9 +411,11 @@ function createObservationNode(verbose = false) {
253
411
  const observations = state.observations || [];
254
412
  const thoughts = state.thoughts || [];
255
413
  const actions = state.actions || [];
256
- if (verbose) {
257
- console.log(`[observation] Processing ${observations.length} observations`);
258
- }
414
+ const iteration = state.iteration || 0;
415
+ observationLogger.debug("Processing observations", {
416
+ observationCount: observations.length,
417
+ iteration
418
+ });
259
419
  const recentObservations = observations.slice(-10);
260
420
  const currentStep = state.iteration;
261
421
  const latestThought = thoughts[thoughts.length - 1]?.content || "";
@@ -281,6 +441,11 @@ function createObservationNode(verbose = false) {
281
441
  name: latestActions.find((a) => a.id === obs.toolCallId)?.name
282
442
  };
283
443
  });
444
+ observationLogger.debug("Observation node complete", {
445
+ iteration,
446
+ scratchpadUpdated: true,
447
+ messageCount: observationMessages.length
448
+ });
284
449
  return {
285
450
  scratchpad: [scratchpadEntry],
286
451
  messages: observationMessages
@@ -299,7 +464,9 @@ function createReActAgent(config, options) {
299
464
  maxIterations = 10,
300
465
  returnIntermediateSteps = false,
301
466
  stopCondition,
302
- checkpointer
467
+ checkpointer,
468
+ enableDeduplication = true
469
+ // Enable by default
303
470
  } = config;
304
471
  const {
305
472
  verbose = false,
@@ -316,7 +483,7 @@ function createReActAgent(config, options) {
316
483
  maxIterations,
317
484
  verbose
318
485
  );
319
- const actionNode = createActionNode(toolArray, verbose);
486
+ const actionNode = createActionNode(toolArray, verbose, enableDeduplication);
320
487
  const observationNode = createObservationNode(verbose);
321
488
  const shouldContinue = (state) => {
322
489
  if (stopCondition && stopCondition(state)) {
@@ -451,34 +618,34 @@ function createReActAgentBuilder() {
451
618
  import { StateGraph as StateGraph2, END as END2 } from "@langchain/langgraph";
452
619
 
453
620
  // src/plan-execute/state.ts
454
- import { z as z4 } from "zod";
621
+ import { z as z5 } from "zod";
455
622
  import { createStateAnnotation as createStateAnnotation2 } from "@agentforge/core";
456
623
 
457
624
  // src/plan-execute/schemas.ts
458
- import { z as z3 } from "zod";
459
- var PlanStepSchema = z3.object({
625
+ import { z as z4 } from "zod";
626
+ var PlanStepSchema = z4.object({
460
627
  /**
461
628
  * Unique identifier for the step
462
629
  */
463
- id: z3.string().describe("Unique identifier for the step"),
630
+ id: z4.string().describe("Unique identifier for the step"),
464
631
  /**
465
632
  * Description of what this step should accomplish
466
633
  */
467
- description: z3.string().describe("Description of what this step should accomplish"),
634
+ description: z4.string().describe("Description of what this step should accomplish"),
468
635
  /**
469
636
  * Optional dependencies on other steps (by ID)
470
637
  */
471
- dependencies: z3.array(z3.string()).optional().describe("IDs of steps that must complete before this one"),
638
+ dependencies: z4.array(z4.string()).optional().describe("IDs of steps that must complete before this one"),
472
639
  /**
473
640
  * Optional tool to use for this step
474
641
  */
475
- tool: z3.string().optional().describe("Name of the tool to use for this step"),
642
+ tool: z4.string().optional().describe("Name of the tool to use for this step"),
476
643
  /**
477
644
  * Optional arguments for the tool
478
645
  */
479
- args: z3.record(z3.any()).optional().describe("Arguments to pass to the tool")
646
+ args: z4.record(z4.any()).optional().describe("Arguments to pass to the tool")
480
647
  });
481
- var CompletedStepSchema = z3.object({
648
+ var CompletedStepSchema = z4.object({
482
649
  /**
483
650
  * The step that was executed
484
651
  */
@@ -486,53 +653,53 @@ var CompletedStepSchema = z3.object({
486
653
  /**
487
654
  * The result of executing the step
488
655
  */
489
- result: z3.any().describe("The result of executing the step"),
656
+ result: z4.any().describe("The result of executing the step"),
490
657
  /**
491
658
  * Whether the step succeeded
492
659
  */
493
- success: z3.boolean().describe("Whether the step succeeded"),
660
+ success: z4.boolean().describe("Whether the step succeeded"),
494
661
  /**
495
662
  * Optional error message if the step failed
496
663
  */
497
- error: z3.string().optional().describe("Error message if the step failed"),
664
+ error: z4.string().optional().describe("Error message if the step failed"),
498
665
  /**
499
666
  * Timestamp when the step was completed
500
667
  */
501
- timestamp: z3.string().datetime().describe("ISO timestamp when the step was completed")
668
+ timestamp: z4.string().datetime().describe("ISO timestamp when the step was completed")
502
669
  });
503
- var PlanSchema = z3.object({
670
+ var PlanSchema = z4.object({
504
671
  /**
505
672
  * List of steps in the plan
506
673
  */
507
- steps: z3.array(PlanStepSchema).describe("List of steps in the plan"),
674
+ steps: z4.array(PlanStepSchema).describe("List of steps in the plan"),
508
675
  /**
509
676
  * Overall goal of the plan
510
677
  */
511
- goal: z3.string().describe("Overall goal of the plan"),
678
+ goal: z4.string().describe("Overall goal of the plan"),
512
679
  /**
513
680
  * Timestamp when the plan was created
514
681
  */
515
- createdAt: z3.string().datetime().describe("ISO timestamp when the plan was created"),
682
+ createdAt: z4.string().datetime().describe("ISO timestamp when the plan was created"),
516
683
  /**
517
684
  * Optional confidence score (0-1)
518
685
  */
519
- confidence: z3.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
686
+ confidence: z4.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
520
687
  });
521
- var ReplanDecisionSchema = z3.object({
688
+ var ReplanDecisionSchema = z4.object({
522
689
  /**
523
690
  * Whether to replan
524
691
  */
525
- shouldReplan: z3.boolean().describe("Whether to replan based on current results"),
692
+ shouldReplan: z4.boolean().describe("Whether to replan based on current results"),
526
693
  /**
527
694
  * Reason for the decision
528
695
  */
529
- reason: z3.string().describe("Reason for the replan decision"),
696
+ reason: z4.string().describe("Reason for the replan decision"),
530
697
  /**
531
698
  * Optional new goal if replanning
532
699
  */
533
- newGoal: z3.string().optional().describe("Updated goal if replanning")
700
+ newGoal: z4.string().optional().describe("Updated goal if replanning")
534
701
  });
535
- var ExecutionStatusSchema = z3.enum([
702
+ var ExecutionStatusSchema = z4.enum([
536
703
  "planning",
537
704
  "executing",
538
705
  "replanning",
@@ -545,11 +712,7 @@ var PlanExecuteStateConfig = {
545
712
  /**
546
713
  * Original user input/query
547
714
  */
548
- input: {
549
- schema: z4.string(),
550
- default: () => "",
551
- description: "Original user input or query"
552
- },
715
+ input: inputField,
553
716
  /**
554
717
  * The current plan
555
718
  */
@@ -562,7 +725,7 @@ var PlanExecuteStateConfig = {
562
725
  * Accumulates all completed steps
563
726
  */
564
727
  pastSteps: {
565
- schema: z4.array(CompletedStepSchema),
728
+ schema: z5.array(CompletedStepSchema),
566
729
  reducer: (left, right) => [...left, ...right],
567
730
  default: () => [],
568
731
  description: "Completed steps with their results"
@@ -571,7 +734,7 @@ var PlanExecuteStateConfig = {
571
734
  * Index of the current step being executed
572
735
  */
573
736
  currentStepIndex: {
574
- schema: z4.number().int().nonnegative().optional(),
737
+ schema: z5.number().int().nonnegative().optional(),
575
738
  description: "Index of the current step being executed"
576
739
  },
577
740
  /**
@@ -585,34 +748,19 @@ var PlanExecuteStateConfig = {
585
748
  /**
586
749
  * Final response
587
750
  */
588
- response: {
589
- schema: z4.string().optional(),
590
- description: "Final response after plan execution"
591
- },
751
+ response: responseField,
592
752
  /**
593
753
  * Error message if execution failed
594
754
  */
595
- error: {
596
- schema: z4.string().optional(),
597
- description: "Error message if execution failed"
598
- },
755
+ error: errorField,
599
756
  /**
600
757
  * Iteration counter for replanning
601
758
  */
602
- iteration: {
603
- schema: z4.number().int().nonnegative(),
604
- reducer: (left, right) => left + right,
605
- default: () => 0,
606
- description: "Number of planning iterations"
607
- },
759
+ iteration: iterationField,
608
760
  /**
609
761
  * Maximum iterations allowed
610
762
  */
611
- maxIterations: {
612
- schema: z4.number().int().positive(),
613
- default: () => 5,
614
- description: "Maximum number of planning iterations allowed"
615
- }
763
+ maxIterations: maxIterationsField(5)
616
764
  };
617
765
  var PlanExecuteState = createStateAnnotation2(PlanExecuteStateConfig);
618
766
 
@@ -679,6 +827,9 @@ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
679
827
  {dependencies}`;
680
828
 
681
829
  // src/plan-execute/nodes.ts
830
+ var plannerLogger = createPatternLogger("agentforge:patterns:plan-execute:planner");
831
+ var executorLogger = createPatternLogger("agentforge:patterns:plan-execute:executor");
832
+ var replannerLogger = createPatternLogger("agentforge:patterns:plan-execute:replanner");
682
833
  function createPlannerNode(config) {
683
834
  const {
684
835
  model,
@@ -687,7 +838,13 @@ function createPlannerNode(config) {
687
838
  includeToolDescriptions = false
688
839
  } = config;
689
840
  return async (state) => {
841
+ const startTime = Date.now();
690
842
  try {
843
+ plannerLogger.debug("Planning started", {
844
+ input: state.input?.substring(0, 100),
845
+ maxSteps,
846
+ includeToolDescriptions
847
+ });
691
848
  let toolDescriptions = "";
692
849
  if (includeToolDescriptions) {
693
850
  toolDescriptions = "";
@@ -712,6 +869,12 @@ function createPlannerNode(config) {
712
869
  } catch (parseError) {
713
870
  throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
714
871
  }
872
+ plannerLogger.info("Plan created", {
873
+ stepCount: plan.steps.length,
874
+ goal: plan.goal.substring(0, 100),
875
+ confidence: plan.confidence,
876
+ duration: Date.now() - startTime
877
+ });
715
878
  return {
716
879
  plan,
717
880
  status: "executing",
@@ -719,6 +882,10 @@ function createPlannerNode(config) {
719
882
  iteration: 1
720
883
  };
721
884
  } catch (error) {
885
+ plannerLogger.error("Planning failed", {
886
+ error: error instanceof Error ? error.message : String(error),
887
+ duration: Date.now() - startTime
888
+ });
722
889
  return {
723
890
  status: "failed",
724
891
  error: error instanceof Error ? error.message : "Unknown error in planner"
@@ -731,11 +898,18 @@ function createExecutorNode(config) {
731
898
  tools,
732
899
  model,
733
900
  parallel = false,
734
- stepTimeout = 3e4
901
+ stepTimeout = 3e4,
902
+ enableDeduplication = true
735
903
  } = config;
736
904
  return async (state) => {
905
+ const { plan, currentStepIndex = 0, pastSteps = [], iteration = 0 } = state;
737
906
  try {
738
- const { plan, currentStepIndex = 0, pastSteps = [] } = state;
907
+ executorLogger.debug("Executor node executing", {
908
+ currentStepIndex,
909
+ totalSteps: plan?.steps?.length || 0,
910
+ iteration,
911
+ deduplicationEnabled: enableDeduplication
912
+ });
739
913
  if (!plan || !plan.steps || plan.steps.length === 0) {
740
914
  return {
741
915
  status: "completed"
@@ -754,32 +928,80 @@ function createExecutorNode(config) {
754
928
  throw new Error(`Unmet dependencies for step ${currentStep.id}: ${unmetDependencies.join(", ")}`);
755
929
  }
756
930
  }
931
+ const executionCache = /* @__PURE__ */ new Map();
932
+ let cacheSize = 0;
933
+ if (enableDeduplication && currentStep.tool) {
934
+ for (const pastStep of pastSteps) {
935
+ if (pastStep.step.tool) {
936
+ const cacheKey = generateToolCallCacheKey(pastStep.step.tool, pastStep.step.args || {});
937
+ executionCache.set(cacheKey, pastStep);
938
+ cacheSize++;
939
+ }
940
+ }
941
+ if (cacheSize > 0) {
942
+ executorLogger.debug("Deduplication cache built", {
943
+ cacheSize,
944
+ pastStepsCount: pastSteps.length
945
+ });
946
+ }
947
+ }
757
948
  let result;
758
949
  let success = true;
759
950
  let error;
951
+ let isDuplicate = false;
760
952
  try {
761
953
  if (currentStep.tool) {
762
- const tool = tools.find((t) => t.metadata.name === currentStep.tool);
763
- if (!tool) {
764
- throw new Error(`Tool not found: ${currentStep.tool}`);
954
+ if (enableDeduplication) {
955
+ const cacheKey = generateToolCallCacheKey(currentStep.tool, currentStep.args || {});
956
+ const cachedStep = executionCache.get(cacheKey);
957
+ if (cachedStep) {
958
+ isDuplicate = true;
959
+ result = cachedStep.result;
960
+ success = cachedStep.success;
961
+ error = cachedStep.error;
962
+ executorLogger.info("Duplicate step execution prevented", {
963
+ stepId: currentStep.id,
964
+ toolName: currentStep.tool,
965
+ arguments: currentStep.args,
966
+ iteration,
967
+ cacheHit: true
968
+ });
969
+ }
970
+ }
971
+ if (!isDuplicate) {
972
+ const tool = tools.find((t) => t.metadata.name === currentStep.tool);
973
+ if (!tool) {
974
+ throw new Error(`Tool not found: ${currentStep.tool}`);
975
+ }
976
+ const startTime = Date.now();
977
+ const timeoutPromise = new Promise(
978
+ (_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
979
+ );
980
+ result = await Promise.race([
981
+ tool.execute(currentStep.args || {}),
982
+ timeoutPromise
983
+ ]);
984
+ const executionTime = Date.now() - startTime;
985
+ executorLogger.debug("Step executed successfully", {
986
+ stepId: currentStep.id,
987
+ toolName: currentStep.tool,
988
+ executionTime,
989
+ iteration
990
+ });
765
991
  }
766
- const timeoutPromise = new Promise(
767
- (_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
768
- );
769
- result = await Promise.race([
770
- tool.execute(currentStep.args || {}),
771
- timeoutPromise
772
- ]);
773
992
  } else {
774
993
  result = { message: "Step completed without tool execution" };
775
994
  }
776
995
  } catch (execError) {
777
- if (execError && typeof execError === "object" && "constructor" in execError && execError.constructor.name === "GraphInterrupt") {
778
- throw execError;
779
- }
996
+ error = handleNodeError(execError, `executor:${currentStep.description}`, false);
780
997
  success = false;
781
- error = execError instanceof Error ? execError.message : "Unknown execution error";
782
998
  result = null;
999
+ executorLogger.warn("Step execution failed", {
1000
+ stepId: currentStep.id,
1001
+ toolName: currentStep.tool,
1002
+ error,
1003
+ iteration
1004
+ });
783
1005
  }
784
1006
  const completedStep = {
785
1007
  step: currentStep,
@@ -788,11 +1010,23 @@ function createExecutorNode(config) {
788
1010
  error,
789
1011
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
790
1012
  };
1013
+ executorLogger.info("Executor node complete", {
1014
+ stepId: currentStep.id,
1015
+ stepIndex: currentStepIndex,
1016
+ totalSteps: plan.steps.length,
1017
+ success,
1018
+ isDuplicate,
1019
+ iteration
1020
+ });
791
1021
  return {
792
1022
  pastSteps: [completedStep],
793
1023
  currentStepIndex: currentStepIndex + 1
794
1024
  };
795
1025
  } catch (error) {
1026
+ executorLogger.error("Executor node failed", {
1027
+ error: error instanceof Error ? error.message : "Unknown error",
1028
+ iteration
1029
+ });
796
1030
  return {
797
1031
  status: "failed",
798
1032
  error: error instanceof Error ? error.message : "Unknown error in executor"
@@ -807,11 +1041,18 @@ function createReplannerNode(config) {
807
1041
  systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
808
1042
  } = config;
809
1043
  return async (state) => {
1044
+ const startTime = Date.now();
810
1045
  try {
811
1046
  const { plan, pastSteps = [], currentStepIndex = 0 } = state;
812
1047
  if (!plan) {
813
1048
  return { status: "failed", error: "No plan available for replanning" };
814
1049
  }
1050
+ replannerLogger.debug("Evaluating replanning", {
1051
+ completedSteps: pastSteps.length,
1052
+ remainingSteps: plan.steps.length - currentStepIndex,
1053
+ successfulSteps: pastSteps.filter((ps) => ps.success).length,
1054
+ failedSteps: pastSteps.filter((ps) => !ps.success).length
1055
+ });
815
1056
  const completedStepsText = pastSteps.map(
816
1057
  (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}`)
817
1058
  ).join("\n\n");
@@ -833,17 +1074,30 @@ function createReplannerNode(config) {
833
1074
  throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
834
1075
  }
835
1076
  if (decision.shouldReplan) {
1077
+ replannerLogger.info("Replanning triggered", {
1078
+ reason: decision.reason,
1079
+ newGoal: decision.newGoal?.substring(0, 100),
1080
+ duration: Date.now() - startTime
1081
+ });
836
1082
  return {
837
1083
  status: "planning",
838
1084
  input: decision.newGoal || plan.goal,
839
1085
  iteration: 1
840
1086
  };
841
1087
  } else {
1088
+ replannerLogger.info("Continuing with current plan", {
1089
+ reason: decision.reason,
1090
+ duration: Date.now() - startTime
1091
+ });
842
1092
  return {
843
1093
  status: "executing"
844
1094
  };
845
1095
  }
846
1096
  } catch (error) {
1097
+ replannerLogger.error("Replanning evaluation failed", {
1098
+ error: error instanceof Error ? error.message : String(error),
1099
+ duration: Date.now() - startTime
1100
+ });
847
1101
  return {
848
1102
  status: "failed",
849
1103
  error: error instanceof Error ? error.message : "Unknown error in replanner"
@@ -943,46 +1197,46 @@ function createPlanExecuteAgent(config) {
943
1197
  }
944
1198
 
945
1199
  // src/reflection/state.ts
946
- import { z as z6 } from "zod";
1200
+ import { z as z7 } from "zod";
947
1201
  import { createStateAnnotation as createStateAnnotation3 } from "@agentforge/core";
948
1202
 
949
1203
  // src/reflection/schemas.ts
950
- import { z as z5 } from "zod";
951
- var ReflectionSchema = z5.object({
1204
+ import { z as z6 } from "zod";
1205
+ var ReflectionSchema = z6.object({
952
1206
  /**
953
1207
  * The critique or feedback on the current response
954
1208
  */
955
- critique: z5.string().describe("Critique or feedback on the current response"),
1209
+ critique: z6.string().describe("Critique or feedback on the current response"),
956
1210
  /**
957
1211
  * Specific issues identified
958
1212
  */
959
- issues: z5.array(z5.string()).describe("Specific issues or problems identified"),
1213
+ issues: z6.array(z6.string()).describe("Specific issues or problems identified"),
960
1214
  /**
961
1215
  * Suggestions for improvement
962
1216
  */
963
- suggestions: z5.array(z5.string()).describe("Suggestions for improving the response"),
1217
+ suggestions: z6.array(z6.string()).describe("Suggestions for improving the response"),
964
1218
  /**
965
1219
  * Quality score (0-10)
966
1220
  */
967
- score: z5.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
1221
+ score: z6.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
968
1222
  /**
969
1223
  * Whether the response meets quality standards
970
1224
  */
971
- meetsStandards: z5.boolean().describe("Whether the response meets quality standards"),
1225
+ meetsStandards: z6.boolean().describe("Whether the response meets quality standards"),
972
1226
  /**
973
1227
  * Timestamp of the reflection
974
1228
  */
975
- timestamp: z5.date().optional().describe("When this reflection was created")
1229
+ timestamp: z6.date().optional().describe("When this reflection was created")
976
1230
  });
977
- var RevisionSchema = z5.object({
1231
+ var RevisionSchema = z6.object({
978
1232
  /**
979
1233
  * The revised content
980
1234
  */
981
- content: z5.string().describe("The revised content"),
1235
+ content: z6.string().describe("The revised content"),
982
1236
  /**
983
1237
  * Which iteration this revision is from
984
1238
  */
985
- iteration: z5.number().int().nonnegative().describe("Iteration number"),
1239
+ iteration: z6.number().int().nonnegative().describe("Iteration number"),
986
1240
  /**
987
1241
  * The reflection that prompted this revision
988
1242
  */
@@ -990,9 +1244,9 @@ var RevisionSchema = z5.object({
990
1244
  /**
991
1245
  * Timestamp of the revision
992
1246
  */
993
- timestamp: z5.date().optional().describe("When this revision was created")
1247
+ timestamp: z6.date().optional().describe("When this revision was created")
994
1248
  });
995
- var ReflectionStatusSchema = z5.enum([
1249
+ var ReflectionStatusSchema = z6.enum([
996
1250
  "generating",
997
1251
  // Initial generation
998
1252
  "reflecting",
@@ -1004,25 +1258,25 @@ var ReflectionStatusSchema = z5.enum([
1004
1258
  "failed"
1005
1259
  // Max iterations reached without meeting standards
1006
1260
  ]);
1007
- var QualityCriteriaSchema = z5.object({
1261
+ var QualityCriteriaSchema = z6.object({
1008
1262
  /**
1009
1263
  * Minimum quality score required (0-10)
1010
1264
  */
1011
- minScore: z5.number().min(0).max(10).default(7).describe("Minimum quality score required"),
1265
+ minScore: z6.number().min(0).max(10).default(7).describe("Minimum quality score required"),
1012
1266
  /**
1013
1267
  * Specific criteria to evaluate
1014
1268
  */
1015
- criteria: z5.array(z5.string()).optional().describe("Specific criteria to evaluate"),
1269
+ criteria: z6.array(z6.string()).optional().describe("Specific criteria to evaluate"),
1016
1270
  /**
1017
1271
  * Whether all criteria must be met
1018
1272
  */
1019
- requireAll: z5.boolean().default(true).describe("Whether all criteria must be met")
1273
+ requireAll: z6.boolean().default(true).describe("Whether all criteria must be met")
1020
1274
  });
1021
- var ReflectionConfigSchema = z5.object({
1275
+ var ReflectionConfigSchema = z6.object({
1022
1276
  /**
1023
1277
  * Maximum number of reflection iterations
1024
1278
  */
1025
- maxIterations: z5.number().int().positive().default(3).describe("Maximum reflection iterations"),
1279
+ maxIterations: z6.number().int().positive().default(3).describe("Maximum reflection iterations"),
1026
1280
  /**
1027
1281
  * Quality criteria for completion
1028
1282
  */
@@ -1030,7 +1284,7 @@ var ReflectionConfigSchema = z5.object({
1030
1284
  /**
1031
1285
  * Whether to include previous reflections in context
1032
1286
  */
1033
- includeHistory: z5.boolean().default(true).describe("Include previous reflections in context")
1287
+ includeHistory: z6.boolean().default(true).describe("Include previous reflections in context")
1034
1288
  });
1035
1289
 
1036
1290
  // src/reflection/state.ts
@@ -1038,16 +1292,12 @@ var ReflectionStateConfig = {
1038
1292
  /**
1039
1293
  * Original user input/task
1040
1294
  */
1041
- input: {
1042
- schema: z6.string(),
1043
- default: () => "",
1044
- description: "Original user input or task"
1045
- },
1295
+ input: inputField,
1046
1296
  /**
1047
1297
  * Current response/output
1048
1298
  */
1049
1299
  currentResponse: {
1050
- schema: z6.string().optional(),
1300
+ schema: z7.string().optional(),
1051
1301
  description: "Current response or output"
1052
1302
  },
1053
1303
  /**
@@ -1055,7 +1305,7 @@ var ReflectionStateConfig = {
1055
1305
  * Accumulates all reflections
1056
1306
  */
1057
1307
  reflections: {
1058
- schema: z6.array(ReflectionSchema),
1308
+ schema: z7.array(ReflectionSchema),
1059
1309
  reducer: (left, right) => [...left, ...right],
1060
1310
  default: () => [],
1061
1311
  description: "History of all reflections and critiques"
@@ -1065,7 +1315,7 @@ var ReflectionStateConfig = {
1065
1315
  * Accumulates all revisions
1066
1316
  */
1067
1317
  revisions: {
1068
- schema: z6.array(RevisionSchema),
1318
+ schema: z7.array(RevisionSchema),
1069
1319
  reducer: (left, right) => [...left, ...right],
1070
1320
  default: () => [],
1071
1321
  description: "History of all revisions"
@@ -1073,12 +1323,7 @@ var ReflectionStateConfig = {
1073
1323
  /**
1074
1324
  * Current iteration number
1075
1325
  */
1076
- iteration: {
1077
- schema: z6.number().int().nonnegative(),
1078
- reducer: (left, right) => left + right,
1079
- default: () => 0,
1080
- description: "Current iteration number"
1081
- },
1326
+ iteration: iterationField,
1082
1327
  /**
1083
1328
  * Current status
1084
1329
  */
@@ -1097,25 +1342,15 @@ var ReflectionStateConfig = {
1097
1342
  /**
1098
1343
  * Maximum iterations allowed
1099
1344
  */
1100
- maxIterations: {
1101
- schema: z6.number().int().positive(),
1102
- default: () => 3,
1103
- description: "Maximum number of reflection iterations allowed"
1104
- },
1345
+ maxIterations: maxIterationsField(3),
1105
1346
  /**
1106
1347
  * Final response (when completed)
1107
1348
  */
1108
- response: {
1109
- schema: z6.string().optional(),
1110
- description: "Final response after reflection process"
1111
- },
1349
+ response: responseField,
1112
1350
  /**
1113
1351
  * Error message if failed
1114
1352
  */
1115
- error: {
1116
- schema: z6.string().optional(),
1117
- description: "Error message if reflection failed"
1118
- }
1353
+ error: errorField
1119
1354
  };
1120
1355
  var ReflectionState = createStateAnnotation3(ReflectionStateConfig);
1121
1356
 
@@ -1221,6 +1456,9 @@ var REVISION_ENTRY_TEMPLATE = `Iteration {iteration}:
1221
1456
 
1222
1457
  // src/reflection/nodes.ts
1223
1458
  import { HumanMessage as HumanMessage3, SystemMessage as SystemMessage3 } from "@langchain/core/messages";
1459
+ var generatorLogger = createPatternLogger("agentforge:patterns:reflection:generator");
1460
+ var reflectorLogger = createPatternLogger("agentforge:patterns:reflection:reflector");
1461
+ var reviserLogger = createPatternLogger("agentforge:patterns:reflection:reviser");
1224
1462
  function createGeneratorNode(config) {
1225
1463
  const {
1226
1464
  model,
@@ -1228,10 +1466,13 @@ function createGeneratorNode(config) {
1228
1466
  verbose = false
1229
1467
  } = config;
1230
1468
  return async (state) => {
1469
+ const startTime = Date.now();
1231
1470
  try {
1232
- if (verbose) {
1233
- console.log("[Generator] Generating initial response...");
1234
- }
1471
+ generatorLogger.debug("Generating response", {
1472
+ attempt: state.iteration + 1,
1473
+ hasFeedback: state.reflections.length > 0,
1474
+ hasExistingResponse: !!state.currentResponse
1475
+ });
1235
1476
  let context = "";
1236
1477
  if (state.iteration > 0 && state.reflections.length > 0) {
1237
1478
  const lastReflection = state.reflections[state.reflections.length - 1];
@@ -1246,19 +1487,27 @@ ${lastReflection.critique}`;
1246
1487
  ];
1247
1488
  const response = await model.invoke(messages);
1248
1489
  const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1249
- if (verbose) {
1250
- console.log("[Generator] Generated response:", content.substring(0, 100) + "...");
1251
- }
1490
+ generatorLogger.info("Response generated", {
1491
+ attempt: state.iteration + 1,
1492
+ responseLength: content.length,
1493
+ isRevision: state.iteration > 0,
1494
+ duration: Date.now() - startTime
1495
+ });
1252
1496
  return {
1253
1497
  currentResponse: content,
1254
1498
  status: "reflecting",
1255
1499
  iteration: 1
1256
1500
  };
1257
1501
  } catch (error) {
1258
- console.error("[Generator] Error:", error);
1502
+ const errorMessage = handleNodeError(error, "generator", false);
1503
+ generatorLogger.error("Response generation failed", {
1504
+ attempt: state.iteration + 1,
1505
+ error: errorMessage,
1506
+ duration: Date.now() - startTime
1507
+ });
1259
1508
  return {
1260
1509
  status: "failed",
1261
- error: error instanceof Error ? error.message : "Unknown error in generator"
1510
+ error: errorMessage
1262
1511
  };
1263
1512
  }
1264
1513
  };
@@ -1271,10 +1520,13 @@ function createReflectorNode(config) {
1271
1520
  verbose = false
1272
1521
  } = config;
1273
1522
  return async (state) => {
1523
+ const startTime = Date.now();
1274
1524
  try {
1275
- if (verbose) {
1276
- console.log("[Reflector] Reflecting on response...");
1277
- }
1525
+ reflectorLogger.debug("Reflecting on response", {
1526
+ attempt: state.iteration,
1527
+ responseLength: state.currentResponse?.length || 0,
1528
+ hasCriteria: !!(qualityCriteria || state.qualityCriteria)
1529
+ });
1278
1530
  if (!state.currentResponse) {
1279
1531
  throw new Error("No current response to reflect on");
1280
1532
  }
@@ -1324,19 +1576,28 @@ function createReflectorNode(config) {
1324
1576
  meetsStandards: false
1325
1577
  };
1326
1578
  }
1327
- if (verbose) {
1328
- console.log("[Reflector] Reflection score:", reflection.score);
1329
- console.log("[Reflector] Meets standards:", reflection.meetsStandards);
1330
- }
1579
+ reflectorLogger.info("Reflection complete", {
1580
+ attempt: state.iteration,
1581
+ score: reflection.score,
1582
+ meetsStandards: reflection.meetsStandards,
1583
+ issueCount: reflection.issues.length,
1584
+ suggestionCount: reflection.suggestions.length,
1585
+ duration: Date.now() - startTime
1586
+ });
1331
1587
  return {
1332
1588
  reflections: [reflection],
1333
1589
  status: reflection.meetsStandards ? "completed" : "revising"
1334
1590
  };
1335
1591
  } catch (error) {
1336
- console.error("[Reflector] Error:", error);
1592
+ const errorMessage = handleNodeError(error, "reflector", false);
1593
+ reflectorLogger.error("Reflection failed", {
1594
+ attempt: state.iteration,
1595
+ error: errorMessage,
1596
+ duration: Date.now() - startTime
1597
+ });
1337
1598
  return {
1338
1599
  status: "failed",
1339
- error: error instanceof Error ? error.message : "Unknown error in reflector"
1600
+ error: errorMessage
1340
1601
  };
1341
1602
  }
1342
1603
  };
@@ -1348,10 +1609,8 @@ function createReviserNode(config) {
1348
1609
  verbose = false
1349
1610
  } = config;
1350
1611
  return async (state) => {
1612
+ const startTime = Date.now();
1351
1613
  try {
1352
- if (verbose) {
1353
- console.log("[Reviser] Revising response...");
1354
- }
1355
1614
  if (!state.currentResponse) {
1356
1615
  throw new Error("No current response to revise");
1357
1616
  }
@@ -1359,6 +1618,12 @@ function createReviserNode(config) {
1359
1618
  throw new Error("No reflections to base revision on");
1360
1619
  }
1361
1620
  const lastReflection = state.reflections[state.reflections.length - 1];
1621
+ reviserLogger.debug("Revising response", {
1622
+ attempt: state.iteration,
1623
+ previousScore: lastReflection.score,
1624
+ issueCount: lastReflection.issues.length,
1625
+ suggestionCount: lastReflection.suggestions.length
1626
+ });
1362
1627
  let historySection = "";
1363
1628
  if (state.revisions.length > 0) {
1364
1629
  const revisionsText = state.revisions.map(
@@ -1375,14 +1640,17 @@ ${revisionsText}`;
1375
1640
  ];
1376
1641
  const response = await model.invoke(messages);
1377
1642
  const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1378
- if (verbose) {
1379
- console.log("[Reviser] Created revision:", content.substring(0, 100) + "...");
1380
- }
1381
1643
  const revision = {
1382
1644
  content,
1383
1645
  iteration: state.iteration,
1384
1646
  basedOn: lastReflection
1385
1647
  };
1648
+ reviserLogger.info("Revision complete", {
1649
+ attempt: state.iteration,
1650
+ revisionLength: content.length,
1651
+ basedOnScore: lastReflection.score,
1652
+ duration: Date.now() - startTime
1653
+ });
1386
1654
  return {
1387
1655
  currentResponse: content,
1388
1656
  revisions: [revision],
@@ -1390,10 +1658,15 @@ ${revisionsText}`;
1390
1658
  iteration: 1
1391
1659
  };
1392
1660
  } catch (error) {
1393
- console.error("[Reviser] Error:", error);
1661
+ const errorMessage = handleNodeError(error, "reviser", false);
1662
+ reviserLogger.error("Revision failed", {
1663
+ attempt: state.iteration,
1664
+ error: errorMessage,
1665
+ duration: Date.now() - startTime
1666
+ });
1394
1667
  return {
1395
1668
  status: "failed",
1396
- error: error instanceof Error ? error.message : "Unknown error in reviser"
1669
+ error: errorMessage
1397
1670
  };
1398
1671
  }
1399
1672
  };
@@ -1479,13 +1752,13 @@ function createReflectionAgent(config) {
1479
1752
  }
1480
1753
 
1481
1754
  // src/multi-agent/state.ts
1482
- import { z as z8 } from "zod";
1755
+ import { z as z9 } from "zod";
1483
1756
  import { createStateAnnotation as createStateAnnotation4 } from "@agentforge/core";
1484
1757
 
1485
1758
  // src/multi-agent/schemas.ts
1486
- import { z as z7 } from "zod";
1487
- var AgentRoleSchema = z7.enum(["supervisor", "worker"]);
1488
- var MessageTypeSchema = z7.enum([
1759
+ import { z as z8 } from "zod";
1760
+ var AgentRoleSchema = z8.enum(["supervisor", "worker"]);
1761
+ var MessageTypeSchema = z8.enum([
1489
1762
  "user_input",
1490
1763
  // Initial user message
1491
1764
  "task_assignment",
@@ -1499,11 +1772,11 @@ var MessageTypeSchema = z7.enum([
1499
1772
  "completion"
1500
1773
  // Final completion message
1501
1774
  ]);
1502
- var AgentMessageSchema = z7.object({
1775
+ var AgentMessageSchema = z8.object({
1503
1776
  /**
1504
1777
  * Unique identifier for the message
1505
1778
  */
1506
- id: z7.string().describe("Unique message identifier"),
1779
+ id: z8.string().describe("Unique message identifier"),
1507
1780
  /**
1508
1781
  * Type of message
1509
1782
  */
@@ -1511,25 +1784,25 @@ var AgentMessageSchema = z7.object({
1511
1784
  /**
1512
1785
  * Agent that sent the message
1513
1786
  */
1514
- from: z7.string().describe("Agent identifier that sent the message"),
1787
+ from: z8.string().describe("Agent identifier that sent the message"),
1515
1788
  /**
1516
1789
  * Agent(s) that should receive the message
1517
1790
  */
1518
- to: z7.union([z7.string(), z7.array(z7.string())]).describe("Target agent(s)"),
1791
+ to: z8.union([z8.string(), z8.array(z8.string())]).describe("Target agent(s)"),
1519
1792
  /**
1520
1793
  * Message content
1521
1794
  */
1522
- content: z7.string().describe("Message content"),
1795
+ content: z8.string().describe("Message content"),
1523
1796
  /**
1524
1797
  * Optional metadata
1525
1798
  */
1526
- metadata: z7.record(z7.any()).optional().describe("Additional message metadata"),
1799
+ metadata: z8.record(z8.any()).optional().describe("Additional message metadata"),
1527
1800
  /**
1528
1801
  * Timestamp when message was created
1529
1802
  */
1530
- timestamp: z7.number().describe("Timestamp when message was created")
1803
+ timestamp: z8.number().describe("Timestamp when message was created")
1531
1804
  });
1532
- var RoutingStrategySchema = z7.enum([
1805
+ var RoutingStrategySchema = z8.enum([
1533
1806
  "llm-based",
1534
1807
  // LLM decides which agent to route to
1535
1808
  "rule-based",
@@ -1541,25 +1814,25 @@ var RoutingStrategySchema = z7.enum([
1541
1814
  "load-balanced"
1542
1815
  // Route based on agent workload
1543
1816
  ]);
1544
- var RoutingDecisionSchema = z7.object({
1817
+ var RoutingDecisionSchema = z8.object({
1545
1818
  /**
1546
1819
  * Target agent to route to (single agent routing)
1547
1820
  * @deprecated Use targetAgents for parallel routing support
1548
1821
  */
1549
- targetAgent: z7.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
1822
+ targetAgent: z8.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
1550
1823
  /**
1551
1824
  * Target agents to route to (parallel routing)
1552
1825
  * When multiple agents are specified, they execute in parallel
1553
1826
  */
1554
- targetAgents: z7.array(z7.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
1827
+ targetAgents: z8.array(z8.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
1555
1828
  /**
1556
1829
  * Reasoning for the routing decision
1557
1830
  */
1558
- reasoning: z7.string().default("").describe("Explanation for routing decision"),
1831
+ reasoning: z8.string().default("").describe("Explanation for routing decision"),
1559
1832
  /**
1560
1833
  * Confidence in the routing decision (0-1)
1561
1834
  */
1562
- confidence: z7.number().min(0).max(1).default(0.8).describe("Confidence score"),
1835
+ confidence: z8.number().min(0).max(1).default(0.8).describe("Confidence score"),
1563
1836
  /**
1564
1837
  * Strategy used for routing
1565
1838
  */
@@ -1567,86 +1840,86 @@ var RoutingDecisionSchema = z7.object({
1567
1840
  /**
1568
1841
  * Timestamp of the routing decision
1569
1842
  */
1570
- timestamp: z7.number().default(() => Date.now()).describe("Timestamp of the decision")
1843
+ timestamp: z8.number().default(() => Date.now()).describe("Timestamp of the decision")
1571
1844
  }).refine(
1572
1845
  (data) => data.targetAgent || data.targetAgents && data.targetAgents.length > 0,
1573
1846
  { message: "Either targetAgent or targetAgents must be provided" }
1574
1847
  );
1575
- var WorkerCapabilitiesSchema = z7.object({
1848
+ var WorkerCapabilitiesSchema = z8.object({
1576
1849
  /**
1577
1850
  * Skills/capabilities the agent has
1578
1851
  */
1579
- skills: z7.array(z7.string()).describe("List of agent skills"),
1852
+ skills: z8.array(z8.string()).describe("List of agent skills"),
1580
1853
  /**
1581
1854
  * Tools available to the agent
1582
1855
  */
1583
- tools: z7.array(z7.string()).describe("List of tool names available to agent"),
1856
+ tools: z8.array(z8.string()).describe("List of tool names available to agent"),
1584
1857
  /**
1585
1858
  * Whether the agent is currently available
1586
1859
  */
1587
- available: z7.boolean().default(true).describe("Whether agent is available"),
1860
+ available: z8.boolean().default(true).describe("Whether agent is available"),
1588
1861
  /**
1589
1862
  * Current workload (number of active tasks)
1590
1863
  */
1591
- currentWorkload: z7.number().int().nonnegative().default(0).describe("Current number of active tasks")
1864
+ currentWorkload: z8.number().int().nonnegative().default(0).describe("Current number of active tasks")
1592
1865
  });
1593
- var TaskAssignmentSchema = z7.object({
1866
+ var TaskAssignmentSchema = z8.object({
1594
1867
  /**
1595
1868
  * Unique assignment identifier
1596
1869
  */
1597
- id: z7.string().describe("Unique assignment identifier"),
1870
+ id: z8.string().describe("Unique assignment identifier"),
1598
1871
  /**
1599
1872
  * Worker ID assigned to the task
1600
1873
  */
1601
- workerId: z7.string().describe("Worker identifier assigned to task"),
1874
+ workerId: z8.string().describe("Worker identifier assigned to task"),
1602
1875
  /**
1603
1876
  * Task description
1604
1877
  */
1605
- task: z7.string().describe("Description of the task"),
1878
+ task: z8.string().describe("Description of the task"),
1606
1879
  /**
1607
1880
  * Task priority (1-10, higher is more urgent)
1608
1881
  */
1609
- priority: z7.number().int().min(1).max(10).default(5).describe("Task priority"),
1882
+ priority: z8.number().int().min(1).max(10).default(5).describe("Task priority"),
1610
1883
  /**
1611
1884
  * Timestamp when task was assigned
1612
1885
  */
1613
- assignedAt: z7.number().describe("Timestamp when task was assigned"),
1886
+ assignedAt: z8.number().describe("Timestamp when task was assigned"),
1614
1887
  /**
1615
1888
  * Optional deadline for task completion
1616
1889
  */
1617
- deadline: z7.number().optional().describe("Optional task deadline timestamp")
1890
+ deadline: z8.number().optional().describe("Optional task deadline timestamp")
1618
1891
  });
1619
- var TaskResultSchema = z7.object({
1892
+ var TaskResultSchema = z8.object({
1620
1893
  /**
1621
1894
  * Assignment identifier
1622
1895
  */
1623
- assignmentId: z7.string().describe("Assignment identifier"),
1896
+ assignmentId: z8.string().describe("Assignment identifier"),
1624
1897
  /**
1625
1898
  * Worker that completed the task
1626
1899
  */
1627
- workerId: z7.string().describe("Worker that completed the task"),
1900
+ workerId: z8.string().describe("Worker that completed the task"),
1628
1901
  /**
1629
1902
  * Whether the task succeeded
1630
1903
  */
1631
- success: z7.boolean().describe("Whether the task succeeded"),
1904
+ success: z8.boolean().describe("Whether the task succeeded"),
1632
1905
  /**
1633
1906
  * Task result/output
1634
1907
  */
1635
- result: z7.string().describe("Task result or output"),
1908
+ result: z8.string().describe("Task result or output"),
1636
1909
  /**
1637
1910
  * Optional error message if task failed
1638
1911
  */
1639
- error: z7.string().optional().describe("Error message if task failed"),
1912
+ error: z8.string().optional().describe("Error message if task failed"),
1640
1913
  /**
1641
1914
  * Timestamp when task was completed
1642
1915
  */
1643
- completedAt: z7.number().describe("Timestamp when task was completed"),
1916
+ completedAt: z8.number().describe("Timestamp when task was completed"),
1644
1917
  /**
1645
1918
  * Optional metadata about execution
1646
1919
  */
1647
- metadata: z7.record(z7.any()).optional().describe("Execution metadata")
1920
+ metadata: z8.record(z8.any()).optional().describe("Execution metadata")
1648
1921
  });
1649
- var MultiAgentStatusSchema = z7.enum([
1922
+ var MultiAgentStatusSchema = z8.enum([
1650
1923
  "initializing",
1651
1924
  // System is initializing
1652
1925
  "routing",
@@ -1662,27 +1935,27 @@ var MultiAgentStatusSchema = z7.enum([
1662
1935
  "failed"
1663
1936
  // Task failed
1664
1937
  ]);
1665
- var HandoffRequestSchema = z7.object({
1938
+ var HandoffRequestSchema = z8.object({
1666
1939
  /**
1667
1940
  * Agent requesting the handoff
1668
1941
  */
1669
- from: z7.string().describe("Agent requesting handoff"),
1942
+ from: z8.string().describe("Agent requesting handoff"),
1670
1943
  /**
1671
1944
  * Target agent for handoff
1672
1945
  */
1673
- to: z7.string().describe("Target agent for handoff"),
1946
+ to: z8.string().describe("Target agent for handoff"),
1674
1947
  /**
1675
1948
  * Reason for handoff
1676
1949
  */
1677
- reason: z7.string().describe("Reason for requesting handoff"),
1950
+ reason: z8.string().describe("Reason for requesting handoff"),
1678
1951
  /**
1679
1952
  * Context to pass to next agent
1680
1953
  */
1681
- context: z7.any().describe("Context to pass to next agent"),
1954
+ context: z8.any().describe("Context to pass to next agent"),
1682
1955
  /**
1683
1956
  * Timestamp of handoff request
1684
1957
  */
1685
- timestamp: z7.string().datetime().describe("ISO timestamp of handoff request")
1958
+ timestamp: z8.string().datetime().describe("ISO timestamp of handoff request")
1686
1959
  });
1687
1960
 
1688
1961
  // src/multi-agent/state.ts
@@ -1690,17 +1963,13 @@ var MultiAgentStateConfig = {
1690
1963
  /**
1691
1964
  * Original user input/query
1692
1965
  */
1693
- input: {
1694
- schema: z8.string(),
1695
- default: () => "",
1696
- description: "Original user input or query"
1697
- },
1966
+ input: inputField,
1698
1967
  /**
1699
1968
  * All messages in the multi-agent conversation
1700
1969
  * Accumulates all messages between agents
1701
1970
  */
1702
1971
  messages: {
1703
- schema: z8.array(AgentMessageSchema),
1972
+ schema: z9.array(AgentMessageSchema),
1704
1973
  reducer: (left, right) => [...left, ...right],
1705
1974
  default: () => [],
1706
1975
  description: "All messages in the multi-agent conversation"
@@ -1709,7 +1978,7 @@ var MultiAgentStateConfig = {
1709
1978
  * Available worker agents and their capabilities
1710
1979
  */
1711
1980
  workers: {
1712
- schema: z8.record(z8.string(), WorkerCapabilitiesSchema),
1981
+ schema: z9.record(z9.string(), WorkerCapabilitiesSchema),
1713
1982
  reducer: (left, right) => ({
1714
1983
  ...left,
1715
1984
  ...right
@@ -1721,7 +1990,7 @@ var MultiAgentStateConfig = {
1721
1990
  * Current active agent
1722
1991
  */
1723
1992
  currentAgent: {
1724
- schema: z8.string().optional(),
1993
+ schema: z9.string().optional(),
1725
1994
  description: "Identifier of the currently active agent"
1726
1995
  },
1727
1996
  /**
@@ -1729,7 +1998,7 @@ var MultiAgentStateConfig = {
1729
1998
  * Accumulates all routing decisions
1730
1999
  */
1731
2000
  routingHistory: {
1732
- schema: z8.array(RoutingDecisionSchema),
2001
+ schema: z9.array(RoutingDecisionSchema),
1733
2002
  reducer: (left, right) => [...left, ...right],
1734
2003
  default: () => [],
1735
2004
  description: "History of routing decisions"
@@ -1738,7 +2007,7 @@ var MultiAgentStateConfig = {
1738
2007
  * Active task assignments
1739
2008
  */
1740
2009
  activeAssignments: {
1741
- schema: z8.array(TaskAssignmentSchema),
2010
+ schema: z9.array(TaskAssignmentSchema),
1742
2011
  reducer: (left, right) => [...left, ...right],
1743
2012
  default: () => [],
1744
2013
  description: "Currently active task assignments"
@@ -1748,7 +2017,7 @@ var MultiAgentStateConfig = {
1748
2017
  * Accumulates all completed tasks
1749
2018
  */
1750
2019
  completedTasks: {
1751
- schema: z8.array(TaskResultSchema),
2020
+ schema: z9.array(TaskResultSchema),
1752
2021
  reducer: (left, right) => [...left, ...right],
1753
2022
  default: () => [],
1754
2023
  description: "Completed task results"
@@ -1758,7 +2027,7 @@ var MultiAgentStateConfig = {
1758
2027
  * Accumulates all handoff requests
1759
2028
  */
1760
2029
  handoffs: {
1761
- schema: z8.array(HandoffRequestSchema),
2030
+ schema: z9.array(HandoffRequestSchema),
1762
2031
  reducer: (left, right) => [...left, ...right],
1763
2032
  default: () => [],
1764
2033
  description: "Handoff requests between agents"
@@ -1774,44 +2043,40 @@ var MultiAgentStateConfig = {
1774
2043
  /**
1775
2044
  * Iteration counter
1776
2045
  */
1777
- iteration: {
1778
- schema: z8.number().int().nonnegative(),
1779
- reducer: (left, right) => left + right,
1780
- default: () => 0,
1781
- description: "Current iteration number"
1782
- },
2046
+ iteration: iterationField,
1783
2047
  /**
1784
2048
  * Maximum iterations allowed
1785
2049
  */
1786
- maxIterations: {
1787
- schema: z8.number().int().positive(),
1788
- default: () => 10,
1789
- description: "Maximum number of iterations allowed"
1790
- },
2050
+ maxIterations: maxIterationsField(10),
1791
2051
  /**
1792
2052
  * Final aggregated response
1793
2053
  */
1794
- response: {
1795
- schema: z8.string().optional(),
1796
- description: "Final aggregated response"
1797
- },
2054
+ response: responseField,
1798
2055
  /**
1799
2056
  * Error message if execution failed
1800
2057
  */
1801
- error: {
1802
- schema: z8.string().optional(),
1803
- description: "Error message if execution failed"
1804
- }
2058
+ error: errorField
1805
2059
  };
1806
2060
  var MultiAgentState = createStateAnnotation4(MultiAgentStateConfig);
1807
2061
 
1808
2062
  // src/multi-agent/routing.ts
1809
2063
  import { HumanMessage as HumanMessage4, SystemMessage as SystemMessage4, AIMessage as AIMessage2, ToolMessage as ToolMessage2 } from "@langchain/core/messages";
2064
+ import { createLogger as createLogger2, LogLevel } from "@agentforge/core";
2065
+ var logLevel = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
2066
+ var logger = createLogger2("multi-agent:routing", { level: logLevel });
1810
2067
  async function executeTools(toolCalls, tools) {
1811
2068
  const results = [];
2069
+ logger.debug("Executing tools", {
2070
+ toolCallCount: toolCalls.length,
2071
+ toolNames: toolCalls.map((tc) => tc.name)
2072
+ });
1812
2073
  for (const toolCall of toolCalls) {
1813
2074
  const tool = tools.find((t) => t.metadata.name === toolCall.name);
1814
2075
  if (!tool) {
2076
+ logger.warn("Tool not found", {
2077
+ toolName: toolCall.name,
2078
+ availableTools: tools.map((t) => t.metadata.name)
2079
+ });
1815
2080
  results.push(new ToolMessage2({
1816
2081
  content: `Error: Tool '${toolCall.name}' not found`,
1817
2082
  tool_call_id: toolCall.id
@@ -1819,19 +2084,41 @@ async function executeTools(toolCalls, tools) {
1819
2084
  continue;
1820
2085
  }
1821
2086
  try {
2087
+ logger.debug("Executing tool", {
2088
+ toolName: toolCall.name,
2089
+ args: toolCall.args
2090
+ });
1822
2091
  const result = await tool.execute(toolCall.args);
1823
2092
  const content = typeof result === "string" ? result : JSON.stringify(result);
2093
+ logger.debug("Tool execution successful", {
2094
+ toolName: toolCall.name,
2095
+ resultLength: content.length
2096
+ });
1824
2097
  results.push(new ToolMessage2({
1825
2098
  content,
1826
2099
  tool_call_id: toolCall.id
1827
2100
  }));
1828
2101
  } catch (error) {
2102
+ logger.error("Tool execution failed", {
2103
+ toolName: toolCall.name,
2104
+ error: error.message
2105
+ });
1829
2106
  results.push(new ToolMessage2({
1830
2107
  content: `Error executing tool: ${error.message}`,
1831
2108
  tool_call_id: toolCall.id
1832
2109
  }));
1833
2110
  }
1834
2111
  }
2112
+ logger.debug("Tool execution complete", {
2113
+ successCount: results.filter((r) => {
2114
+ const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
2115
+ return !content.startsWith("Error");
2116
+ }).length,
2117
+ errorCount: results.filter((r) => {
2118
+ const content = typeof r.content === "string" ? r.content : JSON.stringify(r.content);
2119
+ return content.startsWith("Error");
2120
+ }).length
2121
+ });
1835
2122
  return results;
1836
2123
  }
1837
2124
  var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
@@ -1869,6 +2156,10 @@ Choose parallel routing when the task benefits from multiple perspectives or dat
1869
2156
  var llmBasedRouting = {
1870
2157
  name: "llm-based",
1871
2158
  async route(state, config) {
2159
+ logger.info("Starting LLM-based routing", {
2160
+ iteration: state.iteration,
2161
+ availableWorkers: Object.keys(state.workers).length
2162
+ });
1872
2163
  if (!config.model) {
1873
2164
  throw new Error("LLM-based routing requires a model to be configured");
1874
2165
  }
@@ -1881,8 +2172,20 @@ var llmBasedRouting = {
1881
2172
  const available = caps.available ? "available" : "busy";
1882
2173
  return `- ${id}: Skills: [${skills}], Tools: [${tools2}], Status: ${available}, Workload: ${caps.currentWorkload}`;
1883
2174
  }).join("\n");
2175
+ logger.debug("Worker capabilities", {
2176
+ workers: Object.entries(state.workers).map(([id, caps]) => ({
2177
+ id,
2178
+ skills: caps.skills,
2179
+ available: caps.available,
2180
+ workload: caps.currentWorkload
2181
+ }))
2182
+ });
1884
2183
  const lastMessage = state.messages[state.messages.length - 1];
1885
2184
  const taskContext = lastMessage?.content || state.input;
2185
+ logger.debug("Task context", {
2186
+ taskLength: taskContext.length,
2187
+ taskPreview: taskContext.substring(0, 100)
2188
+ });
1886
2189
  const userPrompt = `Current task: ${taskContext}
1887
2190
 
1888
2191
  Available workers:
@@ -1892,6 +2195,11 @@ Select the best worker(s) for this task and explain your reasoning.`;
1892
2195
  const conversationHistory = [];
1893
2196
  let attempt = 0;
1894
2197
  while (attempt < maxRetries) {
2198
+ logger.debug("LLM routing attempt", {
2199
+ attempt: attempt + 1,
2200
+ maxRetries,
2201
+ conversationHistoryLength: conversationHistory.length
2202
+ });
1895
2203
  const messages = [
1896
2204
  new SystemMessage4(systemPrompt),
1897
2205
  new HumanMessage4(userPrompt),
@@ -1899,6 +2207,10 @@ Select the best worker(s) for this task and explain your reasoning.`;
1899
2207
  ];
1900
2208
  const response = await config.model.invoke(messages);
1901
2209
  if (response.tool_calls && response.tool_calls.length > 0) {
2210
+ logger.info("LLM requested tool calls", {
2211
+ toolCount: response.tool_calls.length,
2212
+ toolNames: response.tool_calls.map((tc) => tc.name)
2213
+ });
1902
2214
  if (tools.length === 0) {
1903
2215
  throw new Error("LLM requested tool calls but no tools are configured");
1904
2216
  }
@@ -1908,24 +2220,42 @@ Select the best worker(s) for this task and explain your reasoning.`;
1908
2220
  ...toolResults
1909
2221
  );
1910
2222
  attempt++;
2223
+ logger.debug("Retrying routing with tool results", { attempt });
1911
2224
  continue;
1912
2225
  }
2226
+ logger.debug("Parsing routing decision from LLM response");
1913
2227
  let decision;
1914
2228
  if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
2229
+ logger.debug("Response is structured output", {
2230
+ hasTargetAgent: "targetAgent" in response,
2231
+ hasTargetAgents: "targetAgents" in response
2232
+ });
1915
2233
  decision = response;
1916
2234
  } else if (response.content) {
1917
2235
  if (typeof response.content === "string") {
1918
2236
  try {
1919
2237
  decision = JSON.parse(response.content);
2238
+ logger.debug("Parsed JSON from string response");
1920
2239
  } catch (error) {
2240
+ logger.error("Failed to parse routing decision", {
2241
+ content: response.content,
2242
+ error: error instanceof Error ? error.message : String(error)
2243
+ });
1921
2244
  throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
1922
2245
  }
1923
2246
  } else if (typeof response.content === "object") {
2247
+ logger.debug("Response content is already an object");
1924
2248
  decision = response.content;
1925
2249
  } else {
2250
+ logger.error("Unexpected response content type", {
2251
+ type: typeof response.content
2252
+ });
1926
2253
  throw new Error(`Unexpected response content type: ${typeof response.content}`);
1927
2254
  }
1928
2255
  } else {
2256
+ logger.error("Unexpected response format", {
2257
+ response: JSON.stringify(response)
2258
+ });
1929
2259
  throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
1930
2260
  }
1931
2261
  const result = {
@@ -1936,20 +2266,41 @@ Select the best worker(s) for this task and explain your reasoning.`;
1936
2266
  strategy: "llm-based",
1937
2267
  timestamp: Date.now()
1938
2268
  };
2269
+ logger.info("LLM routing decision made", {
2270
+ targetAgent: result.targetAgent,
2271
+ targetAgents: result.targetAgents,
2272
+ isParallel: result.targetAgents && result.targetAgents.length > 1,
2273
+ confidence: result.confidence,
2274
+ reasoning: result.reasoning
2275
+ });
1939
2276
  return result;
1940
2277
  }
2278
+ logger.error("Max tool retries exceeded", { maxRetries });
1941
2279
  throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
1942
2280
  }
1943
2281
  };
1944
2282
  var roundRobinRouting = {
1945
2283
  name: "round-robin",
1946
2284
  async route(state, config) {
2285
+ logger.info("Starting round-robin routing", {
2286
+ iteration: state.iteration
2287
+ });
1947
2288
  const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
2289
+ logger.debug("Available workers for round-robin", {
2290
+ count: availableWorkers.length,
2291
+ workers: availableWorkers
2292
+ });
1948
2293
  if (availableWorkers.length === 0) {
2294
+ logger.error("No available workers for round-robin routing");
1949
2295
  throw new Error("No available workers for round-robin routing");
1950
2296
  }
1951
2297
  const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
1952
2298
  const targetAgent = availableWorkers[lastRoutingIndex];
2299
+ logger.info("Round-robin routing decision", {
2300
+ targetAgent,
2301
+ index: lastRoutingIndex + 1,
2302
+ totalWorkers: availableWorkers.length
2303
+ });
1953
2304
  return {
1954
2305
  targetAgent,
1955
2306
  targetAgents: null,
@@ -1963,8 +2314,15 @@ var roundRobinRouting = {
1963
2314
  var skillBasedRouting = {
1964
2315
  name: "skill-based",
1965
2316
  async route(state, config) {
2317
+ logger.info("Starting skill-based routing", {
2318
+ iteration: state.iteration
2319
+ });
1966
2320
  const lastMessage = state.messages[state.messages.length - 1];
1967
2321
  const taskContent = (lastMessage?.content || state.input).toLowerCase();
2322
+ logger.debug("Task content for skill matching", {
2323
+ taskLength: taskContent.length,
2324
+ taskPreview: taskContent.substring(0, 100)
2325
+ });
1968
2326
  const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
1969
2327
  const skillMatches = caps.skills.filter(
1970
2328
  (skill) => taskContent.includes(skill.toLowerCase())
@@ -1975,11 +2333,20 @@ var skillBasedRouting = {
1975
2333
  const score = skillMatches * 2 + toolMatches;
1976
2334
  return { id, score, skills: caps.skills, tools: caps.tools };
1977
2335
  }).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
2336
+ logger.debug("Worker skill scores", {
2337
+ scoredWorkers: workerScores.map((w) => ({ id: w.id, score: w.score }))
2338
+ });
1978
2339
  if (workerScores.length === 0) {
2340
+ logger.warn("No skill matches found, using fallback");
1979
2341
  const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
1980
2342
  if (!firstAvailable) {
2343
+ logger.error("No available workers for skill-based routing");
1981
2344
  throw new Error("No available workers for skill-based routing");
1982
2345
  }
2346
+ logger.info("Skill-based routing fallback decision", {
2347
+ targetAgent: firstAvailable[0],
2348
+ confidence: 0.5
2349
+ });
1983
2350
  return {
1984
2351
  targetAgent: firstAvailable[0],
1985
2352
  targetAgents: null,
@@ -1991,6 +2358,12 @@ var skillBasedRouting = {
1991
2358
  }
1992
2359
  const best = workerScores[0];
1993
2360
  const confidence = Math.min(best.score / 5, 1);
2361
+ logger.info("Skill-based routing decision", {
2362
+ targetAgent: best.id,
2363
+ score: best.score,
2364
+ confidence,
2365
+ matchedSkills: best.skills
2366
+ });
1994
2367
  return {
1995
2368
  targetAgent: best.id,
1996
2369
  targetAgents: null,
@@ -2004,13 +2377,26 @@ var skillBasedRouting = {
2004
2377
  var loadBalancedRouting = {
2005
2378
  name: "load-balanced",
2006
2379
  async route(state, config) {
2380
+ logger.info("Starting load-balanced routing", {
2381
+ iteration: state.iteration
2382
+ });
2007
2383
  const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
2384
+ logger.debug("Worker workloads", {
2385
+ workers: availableWorkers.map((w) => ({ id: w.id, workload: w.workload }))
2386
+ });
2008
2387
  if (availableWorkers.length === 0) {
2388
+ logger.error("No available workers for load-balanced routing");
2009
2389
  throw new Error("No available workers for load-balanced routing");
2010
2390
  }
2011
2391
  const targetWorker = availableWorkers[0];
2012
2392
  const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
2013
2393
  const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2394
+ logger.info("Load-balanced routing decision", {
2395
+ targetAgent: targetWorker.id,
2396
+ workload: targetWorker.workload,
2397
+ avgWorkload: avgWorkload.toFixed(1),
2398
+ confidence
2399
+ });
2014
2400
  return {
2015
2401
  targetAgent: targetWorker.id,
2016
2402
  targetAgents: null,
@@ -2024,13 +2410,24 @@ var loadBalancedRouting = {
2024
2410
  var ruleBasedRouting = {
2025
2411
  name: "rule-based",
2026
2412
  async route(state, config) {
2413
+ logger.info("Starting rule-based routing", {
2414
+ iteration: state.iteration
2415
+ });
2027
2416
  if (!config.routingFn) {
2417
+ logger.error("Rule-based routing requires a custom routing function");
2028
2418
  throw new Error("Rule-based routing requires a custom routing function");
2029
2419
  }
2030
- return await config.routingFn(state);
2420
+ const decision = await config.routingFn(state);
2421
+ logger.info("Rule-based routing decision", {
2422
+ targetAgent: decision.targetAgent,
2423
+ targetAgents: decision.targetAgents,
2424
+ confidence: decision.confidence
2425
+ });
2426
+ return decision;
2031
2427
  }
2032
2428
  };
2033
2429
  function getRoutingStrategy(name) {
2430
+ logger.debug("Getting routing strategy", { name });
2034
2431
  switch (name) {
2035
2432
  case "llm-based":
2036
2433
  return llmBasedRouting;
@@ -2043,14 +2440,15 @@ function getRoutingStrategy(name) {
2043
2440
  case "rule-based":
2044
2441
  return ruleBasedRouting;
2045
2442
  default:
2443
+ logger.error("Unknown routing strategy", { name });
2046
2444
  throw new Error(`Unknown routing strategy: ${name}`);
2047
2445
  }
2048
2446
  }
2049
2447
 
2050
2448
  // src/multi-agent/utils.ts
2051
- import { createLogger, LogLevel } from "@agentforge/core";
2052
- var logLevel = process.env.LOG_LEVEL?.toLowerCase() || LogLevel.INFO;
2053
- var logger = createLogger("multi-agent", { level: logLevel });
2449
+ import { createLogger as createLogger3, LogLevel as LogLevel2 } from "@agentforge/core";
2450
+ var logLevel2 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel2.INFO;
2451
+ var logger2 = createLogger3("multi-agent", { level: logLevel2 });
2054
2452
  function isReActAgent(obj) {
2055
2453
  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
2056
2454
  (obj.constructor?.name === "CompiledGraph" || obj.constructor?.name === "CompiledStateGraph");
@@ -2058,9 +2456,9 @@ function isReActAgent(obj) {
2058
2456
  function wrapReActAgent(workerId, agent, verbose = false) {
2059
2457
  return async (state, config) => {
2060
2458
  try {
2061
- logger.debug("Wrapping ReAct agent execution", { workerId });
2459
+ logger2.debug("Wrapping ReAct agent execution", { workerId });
2062
2460
  const task = state.messages[state.messages.length - 1]?.content || state.input;
2063
- logger.debug("Extracted task", {
2461
+ logger2.debug("Extracted task", {
2064
2462
  workerId,
2065
2463
  taskPreview: task.substring(0, 100) + (task.length > 100 ? "..." : "")
2066
2464
  });
@@ -2068,7 +2466,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2068
2466
  (assignment) => assignment.workerId === workerId && !state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2069
2467
  );
2070
2468
  if (!currentAssignment) {
2071
- logger.debug("No active assignment found", { workerId });
2469
+ logger2.debug("No active assignment found", { workerId });
2072
2470
  return {};
2073
2471
  }
2074
2472
  const result = await agent.invoke(
@@ -2079,14 +2477,14 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2079
2477
  // Pass through the config for checkpointing and interrupt support
2080
2478
  );
2081
2479
  const response = result.messages?.[result.messages.length - 1]?.content || "No response";
2082
- logger.debug("Received response from ReAct agent", {
2480
+ logger2.debug("Received response from ReAct agent", {
2083
2481
  workerId,
2084
2482
  responsePreview: response.substring(0, 100) + (response.length > 100 ? "..." : "")
2085
2483
  });
2086
2484
  const toolsUsed = result.actions?.map((action) => action.name).filter(Boolean) || [];
2087
2485
  const uniqueTools = [...new Set(toolsUsed)];
2088
2486
  if (uniqueTools.length > 0) {
2089
- logger.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2487
+ logger2.debug("Tools used by ReAct agent", { workerId, tools: uniqueTools });
2090
2488
  }
2091
2489
  const taskResult = {
2092
2490
  assignmentId: currentAssignment.id,
@@ -2104,14 +2502,10 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2104
2502
  completedTasks: [taskResult]
2105
2503
  };
2106
2504
  } catch (error) {
2107
- if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
2108
- logger.debug("GraphInterrupt detected - re-throwing", { workerId });
2109
- throw error;
2110
- }
2111
- logger.error("Error in ReAct agent execution", {
2505
+ const errorMessage = handleNodeError(error, `react-agent:${workerId}`, false);
2506
+ logger2.error("Error in ReAct agent execution", {
2112
2507
  workerId,
2113
- error: error instanceof Error ? error.message : String(error),
2114
- stack: error instanceof Error ? error.stack : void 0
2508
+ error: errorMessage
2115
2509
  });
2116
2510
  const currentAssignment = state.activeAssignments.find(
2117
2511
  (assignment) => assignment.workerId === workerId
@@ -2122,7 +2516,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2122
2516
  workerId,
2123
2517
  success: false,
2124
2518
  result: "",
2125
- error: error instanceof Error ? error.message : "Unknown error in ReAct agent",
2519
+ error: errorMessage,
2126
2520
  completedAt: Date.now()
2127
2521
  };
2128
2522
  return {
@@ -2133,7 +2527,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2133
2527
  }
2134
2528
  return {
2135
2529
  status: "failed",
2136
- error: error instanceof Error ? error.message : `Unknown error in ReAct wrapper for ${workerId}`
2530
+ error: errorMessage
2137
2531
  };
2138
2532
  }
2139
2533
  };
@@ -2141,7 +2535,9 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2141
2535
 
2142
2536
  // src/multi-agent/nodes.ts
2143
2537
  import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
2144
- import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
2538
+ import { toLangChainTools as toLangChainTools2, createLogger as createLogger4, LogLevel as LogLevel3 } from "@agentforge/core";
2539
+ var logLevel3 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel3.INFO;
2540
+ var logger3 = createLogger4("multi-agent:nodes", { level: logLevel3 });
2145
2541
  var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
2146
2542
 
2147
2543
  Your job is to:
@@ -2159,13 +2555,19 @@ function createSupervisorNode(config) {
2159
2555
  } = config;
2160
2556
  return async (state) => {
2161
2557
  try {
2162
- if (verbose) {
2163
- console.log(`[Supervisor] Routing iteration ${state.iteration}/${maxIterations}`);
2164
- }
2558
+ logger3.info("Supervisor node executing", {
2559
+ iteration: state.iteration,
2560
+ maxIterations,
2561
+ activeAssignments: state.activeAssignments.length,
2562
+ completedTasks: state.completedTasks.length
2563
+ });
2564
+ logger3.debug(`Routing iteration ${state.iteration}/${maxIterations}`);
2165
2565
  if (state.iteration >= maxIterations) {
2166
- if (verbose) {
2167
- console.log("[Supervisor] Max iterations reached, moving to aggregation");
2168
- }
2566
+ logger3.warn("Max iterations reached", {
2567
+ iteration: state.iteration,
2568
+ maxIterations
2569
+ });
2570
+ logger3.debug("Max iterations reached, moving to aggregation");
2169
2571
  return {
2170
2572
  status: "aggregating",
2171
2573
  currentAgent: "aggregator"
@@ -2174,27 +2576,55 @@ function createSupervisorNode(config) {
2174
2576
  const allCompleted = state.activeAssignments.every(
2175
2577
  (assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2176
2578
  );
2579
+ logger3.debug("Checking task completion", {
2580
+ activeAssignments: state.activeAssignments.length,
2581
+ completedTasks: state.completedTasks.length,
2582
+ allCompleted
2583
+ });
2177
2584
  if (allCompleted && state.activeAssignments.length > 0) {
2178
- if (verbose) {
2179
- console.log("[Supervisor] All tasks completed, moving to aggregation");
2180
- }
2585
+ logger3.info("All tasks completed, moving to aggregation", {
2586
+ completedCount: state.completedTasks.length
2587
+ });
2588
+ logger3.debug("All tasks completed, moving to aggregation");
2181
2589
  return {
2182
2590
  status: "aggregating",
2183
2591
  currentAgent: "aggregator"
2184
2592
  };
2185
2593
  }
2594
+ logger3.debug("Getting routing strategy", { strategy });
2186
2595
  const routingImpl = getRoutingStrategy(strategy);
2187
2596
  const decision = await routingImpl.route(state, config);
2188
2597
  const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
2598
+ logger3.debug("Target agents determined", {
2599
+ targetAgents,
2600
+ isParallel: targetAgents.length > 1,
2601
+ decision: {
2602
+ reasoning: decision.reasoning,
2603
+ confidence: decision.confidence
2604
+ }
2605
+ });
2189
2606
  if (targetAgents.length === 0) {
2607
+ logger3.error("No target agents specified in routing decision");
2190
2608
  throw new Error("Routing decision must specify at least one target agent");
2191
2609
  }
2192
- if (verbose) {
2193
- if (targetAgents.length === 1) {
2194
- console.log(`[Supervisor] Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2195
- } else {
2196
- console.log(`[Supervisor] Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2197
- }
2610
+ if (targetAgents.length === 1) {
2611
+ logger3.info("Routing to single agent", {
2612
+ targetAgent: targetAgents[0],
2613
+ reasoning: decision.reasoning,
2614
+ confidence: decision.confidence
2615
+ });
2616
+ } else {
2617
+ logger3.info("Routing to multiple agents in parallel", {
2618
+ targetAgents,
2619
+ count: targetAgents.length,
2620
+ reasoning: decision.reasoning,
2621
+ confidence: decision.confidence
2622
+ });
2623
+ }
2624
+ if (targetAgents.length === 1) {
2625
+ logger3.debug(`Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2626
+ } else {
2627
+ logger3.debug(`Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2198
2628
  }
2199
2629
  const task = state.messages[state.messages.length - 1]?.content || state.input;
2200
2630
  const assignments = targetAgents.map((workerId) => ({
@@ -2204,6 +2634,14 @@ function createSupervisorNode(config) {
2204
2634
  priority: 5,
2205
2635
  assignedAt: Date.now()
2206
2636
  }));
2637
+ logger3.debug("Created task assignments", {
2638
+ assignmentCount: assignments.length,
2639
+ assignments: assignments.map((a) => ({
2640
+ id: a.id,
2641
+ workerId: a.workerId,
2642
+ taskLength: a.task.length
2643
+ }))
2644
+ });
2207
2645
  const messages = assignments.map((assignment) => ({
2208
2646
  id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2209
2647
  from: "supervisor",
@@ -2216,6 +2654,12 @@ function createSupervisorNode(config) {
2216
2654
  priority: assignment.priority
2217
2655
  }
2218
2656
  }));
2657
+ logger3.info("Supervisor routing complete", {
2658
+ currentAgent: targetAgents.join(","),
2659
+ status: "executing",
2660
+ assignmentCount: assignments.length,
2661
+ nextIteration: state.iteration + 1
2662
+ });
2219
2663
  return {
2220
2664
  currentAgent: targetAgents.join(","),
2221
2665
  // Store all agents (for backward compat)
@@ -2227,7 +2671,11 @@ function createSupervisorNode(config) {
2227
2671
  iteration: state.iteration + 1
2228
2672
  };
2229
2673
  } catch (error) {
2230
- console.error("[Supervisor] Error:", error);
2674
+ logger3.error("Supervisor node error", {
2675
+ error: error instanceof Error ? error.message : String(error),
2676
+ stack: error instanceof Error ? error.stack : void 0,
2677
+ iteration: state.iteration
2678
+ });
2231
2679
  return {
2232
2680
  status: "failed",
2233
2681
  error: error instanceof Error ? error.message : "Unknown error in supervisor"
@@ -2248,40 +2696,52 @@ function createWorkerNode(config) {
2248
2696
  } = config;
2249
2697
  return async (state, runConfig) => {
2250
2698
  try {
2251
- if (verbose) {
2252
- console.log(`[Worker:${id}] Executing task`);
2253
- }
2699
+ logger3.info("Worker node executing", {
2700
+ workerId: id,
2701
+ iteration: state.iteration,
2702
+ activeAssignments: state.activeAssignments.length
2703
+ });
2254
2704
  const currentAssignment = state.activeAssignments.find(
2255
2705
  (assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
2256
2706
  );
2257
2707
  if (!currentAssignment) {
2258
- if (verbose) {
2259
- console.log(`[Worker:${id}] No active assignment found`);
2260
- }
2708
+ logger3.debug("No active assignment found for worker", {
2709
+ workerId: id,
2710
+ totalActiveAssignments: state.activeAssignments.length,
2711
+ completedTasks: state.completedTasks.length
2712
+ });
2261
2713
  return {};
2262
2714
  }
2715
+ logger3.info("Worker processing assignment", {
2716
+ workerId: id,
2717
+ assignmentId: currentAssignment.id,
2718
+ taskLength: currentAssignment.task.length,
2719
+ taskPreview: currentAssignment.task.substring(0, 100)
2720
+ });
2263
2721
  if (executeFn) {
2264
- if (verbose) {
2265
- console.log(`[Worker:${id}] Using custom executeFn`);
2266
- }
2722
+ logger3.debug("Using custom execution function", { workerId: id });
2267
2723
  return await executeFn(state, runConfig);
2268
2724
  }
2269
2725
  if (agent) {
2270
2726
  if (isReActAgent(agent)) {
2271
- if (verbose) {
2272
- console.log(`[Worker:${id}] Using ReAct agent (auto-wrapped)`);
2273
- }
2727
+ logger3.debug("Using ReAct agent", { workerId: id });
2274
2728
  const wrappedFn = wrapReActAgent(id, agent, verbose);
2275
2729
  return await wrappedFn(state, runConfig);
2276
2730
  } else {
2277
- console.warn(`[Worker:${id}] Agent provided but does not appear to be a ReAct agent. Falling back to default execution.`);
2731
+ logger3.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
2278
2732
  }
2279
2733
  }
2280
2734
  if (!model) {
2735
+ logger3.error("Worker missing required configuration", { workerId: id });
2281
2736
  throw new Error(
2282
2737
  `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2283
2738
  );
2284
2739
  }
2740
+ logger3.debug("Using default LLM execution", {
2741
+ workerId: id,
2742
+ hasTools: tools.length > 0,
2743
+ toolCount: tools.length
2744
+ });
2285
2745
  const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2286
2746
  Skills: ${capabilities.skills.join(", ")}
2287
2747
  Tools: ${capabilities.tools.join(", ")}
@@ -2293,14 +2753,23 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2293
2753
  ];
2294
2754
  let modelToUse = model;
2295
2755
  if (tools.length > 0 && model.bindTools) {
2756
+ logger3.debug("Binding tools to model", {
2757
+ workerId: id,
2758
+ toolCount: tools.length,
2759
+ toolNames: tools.map((t) => t.metadata.name)
2760
+ });
2296
2761
  const langchainTools = toLangChainTools2(tools);
2297
2762
  modelToUse = model.bindTools(langchainTools);
2298
2763
  }
2764
+ logger3.debug("Invoking LLM", { workerId: id });
2299
2765
  const response = await modelToUse.invoke(messages);
2300
2766
  const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2301
- if (verbose) {
2302
- console.log(`[Worker:${id}] Task completed:`, result.substring(0, 100) + "...");
2303
- }
2767
+ logger3.info("Worker task completed", {
2768
+ workerId: id,
2769
+ assignmentId: currentAssignment.id,
2770
+ resultLength: result.length,
2771
+ resultPreview: result.substring(0, 100)
2772
+ });
2304
2773
  const taskResult = {
2305
2774
  assignmentId: currentAssignment.id,
2306
2775
  workerId: id,
@@ -2330,26 +2799,35 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2330
2799
  currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
2331
2800
  }
2332
2801
  };
2802
+ logger3.debug("Worker state update", {
2803
+ workerId: id,
2804
+ newWorkload: updatedWorkers[id].currentWorkload
2805
+ });
2333
2806
  return {
2334
2807
  completedTasks: [taskResult],
2335
2808
  messages: [message],
2336
2809
  workers: updatedWorkers
2337
2810
  };
2338
2811
  } catch (error) {
2339
- if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
2340
- throw error;
2341
- }
2342
- console.error(`[Worker:${id}] Error:`, error);
2812
+ const errorMessage = handleNodeError(error, `worker:${id}`, false);
2813
+ logger3.error("Worker node error", {
2814
+ workerId: id,
2815
+ error: errorMessage
2816
+ });
2343
2817
  const currentAssignment = state.activeAssignments.find(
2344
2818
  (assignment) => assignment.workerId === id
2345
2819
  );
2346
2820
  if (currentAssignment) {
2821
+ logger3.warn("Creating error result for assignment", {
2822
+ workerId: id,
2823
+ assignmentId: currentAssignment.id
2824
+ });
2347
2825
  const errorResult = {
2348
2826
  assignmentId: currentAssignment.id,
2349
2827
  workerId: id,
2350
2828
  success: false,
2351
2829
  result: "",
2352
- error: error instanceof Error ? error.message : "Unknown error",
2830
+ error: errorMessage,
2353
2831
  completedAt: Date.now()
2354
2832
  };
2355
2833
  return {
@@ -2358,9 +2836,10 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2358
2836
  status: "routing"
2359
2837
  };
2360
2838
  }
2839
+ logger3.error("No assignment found for error handling", { workerId: id });
2361
2840
  return {
2362
2841
  status: "failed",
2363
- error: error instanceof Error ? error.message : `Unknown error in worker ${id}`
2842
+ error: errorMessage
2364
2843
  };
2365
2844
  }
2366
2845
  };
@@ -2374,29 +2853,44 @@ function createAggregatorNode(config = {}) {
2374
2853
  } = config;
2375
2854
  return async (state) => {
2376
2855
  try {
2377
- if (verbose) {
2378
- console.log("[Aggregator] Combining results from workers");
2379
- }
2856
+ logger3.info("Aggregator node executing", {
2857
+ completedTasks: state.completedTasks.length,
2858
+ successfulTasks: state.completedTasks.filter((t) => t.success).length,
2859
+ failedTasks: state.completedTasks.filter((t) => !t.success).length
2860
+ });
2861
+ logger3.debug("Combining results from workers");
2380
2862
  if (aggregateFn) {
2863
+ logger3.debug("Using custom aggregation function");
2381
2864
  const response2 = await aggregateFn(state);
2865
+ logger3.info("Custom aggregation complete", {
2866
+ responseLength: response2.length
2867
+ });
2382
2868
  return {
2383
2869
  response: response2,
2384
2870
  status: "completed"
2385
2871
  };
2386
2872
  }
2387
2873
  if (state.completedTasks.length === 0) {
2874
+ logger3.warn("No completed tasks to aggregate");
2388
2875
  return {
2389
2876
  response: "No tasks were completed.",
2390
2877
  status: "completed"
2391
2878
  };
2392
2879
  }
2393
2880
  if (!model) {
2881
+ logger3.debug("No model provided, concatenating results");
2394
2882
  const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2883
+ logger3.info("Simple concatenation complete", {
2884
+ resultLength: combinedResults.length
2885
+ });
2395
2886
  return {
2396
2887
  response: combinedResults || "No successful results to aggregate.",
2397
2888
  status: "completed"
2398
2889
  };
2399
2890
  }
2891
+ logger3.debug("Using LLM for intelligent aggregation", {
2892
+ taskCount: state.completedTasks.length
2893
+ });
2400
2894
  const taskResults = state.completedTasks.map((task, idx) => {
2401
2895
  const status = task.success ? "\u2713" : "\u2717";
2402
2896
  const result = task.success ? task.result : `Error: ${task.error}`;
@@ -2413,17 +2907,24 @@ Please synthesize these results into a comprehensive response that addresses the
2413
2907
  new SystemMessage5(systemPrompt),
2414
2908
  new HumanMessage5(userPrompt)
2415
2909
  ];
2910
+ logger3.debug("Invoking aggregation LLM");
2416
2911
  const response = await model.invoke(messages);
2417
2912
  const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2418
- if (verbose) {
2419
- console.log("[Aggregator] Aggregation complete");
2420
- }
2913
+ logger3.info("Aggregation complete", {
2914
+ responseLength: aggregatedResponse.length,
2915
+ responsePreview: aggregatedResponse.substring(0, 100)
2916
+ });
2917
+ logger3.debug("Aggregation complete");
2421
2918
  return {
2422
2919
  response: aggregatedResponse,
2423
2920
  status: "completed"
2424
2921
  };
2425
2922
  } catch (error) {
2426
- console.error("[Aggregator] Error:", error);
2923
+ logger3.error("Aggregator node error", {
2924
+ error: error instanceof Error ? error.message : String(error),
2925
+ stack: error instanceof Error ? error.stack : void 0,
2926
+ completedTasks: state.completedTasks.length
2927
+ });
2427
2928
  return {
2428
2929
  status: "failed",
2429
2930
  error: error instanceof Error ? error.message : "Unknown error in aggregator"
@@ -2434,7 +2935,9 @@ Please synthesize these results into a comprehensive response that addresses the
2434
2935
 
2435
2936
  // src/multi-agent/agent.ts
2436
2937
  import { StateGraph as StateGraph4, END as END4 } from "@langchain/langgraph";
2437
- import { toLangChainTools as toLangChainTools3 } from "@agentforge/core";
2938
+ import { toLangChainTools as toLangChainTools3, createLogger as createLogger5, LogLevel as LogLevel4 } from "@agentforge/core";
2939
+ var logLevel4 = process.env.LOG_LEVEL?.toLowerCase() || LogLevel4.INFO;
2940
+ var logger4 = createLogger5("multi-agent:system", { level: logLevel4 });
2438
2941
  function createMultiAgentSystem(config) {
2439
2942
  const {
2440
2943
  supervisor,
@@ -2476,25 +2979,49 @@ function createMultiAgentSystem(config) {
2476
2979
  });
2477
2980
  workflow.addNode("aggregator", aggregatorNode);
2478
2981
  const supervisorRouter = (state) => {
2982
+ logger4.debug("Supervisor router executing", {
2983
+ status: state.status,
2984
+ currentAgent: state.currentAgent,
2985
+ iteration: state.iteration
2986
+ });
2479
2987
  if (state.status === "completed" || state.status === "failed") {
2988
+ logger4.info("Supervisor router: ending workflow", { status: state.status });
2480
2989
  return END4;
2481
2990
  }
2482
2991
  if (state.status === "aggregating") {
2992
+ logger4.info("Supervisor router: routing to aggregator");
2483
2993
  return "aggregator";
2484
2994
  }
2485
2995
  if (state.currentAgent && state.currentAgent !== "supervisor") {
2486
2996
  if (state.currentAgent.includes(",")) {
2487
2997
  const agents = state.currentAgent.split(",").map((a) => a.trim());
2998
+ logger4.info("Supervisor router: parallel routing", {
2999
+ agents,
3000
+ count: agents.length
3001
+ });
2488
3002
  return agents;
2489
3003
  }
3004
+ logger4.info("Supervisor router: single agent routing", {
3005
+ targetAgent: state.currentAgent
3006
+ });
2490
3007
  return state.currentAgent;
2491
3008
  }
3009
+ logger4.debug("Supervisor router: staying at supervisor");
2492
3010
  return "supervisor";
2493
3011
  };
2494
3012
  const workerRouter = (state) => {
3013
+ logger4.debug("Worker router executing", {
3014
+ iteration: state.iteration,
3015
+ completedTasks: state.completedTasks.length
3016
+ });
3017
+ logger4.debug("Worker router: returning to supervisor");
2495
3018
  return "supervisor";
2496
3019
  };
2497
3020
  const aggregatorRouter = (state) => {
3021
+ logger4.info("Aggregator router: ending workflow", {
3022
+ completedTasks: state.completedTasks.length,
3023
+ status: state.status
3024
+ });
2498
3025
  return END4;
2499
3026
  };
2500
3027
  workflow.setEntryPoint("supervisor");
@@ -2687,11 +3214,14 @@ export {
2687
3214
  ToolCallSchema,
2688
3215
  ToolResultSchema,
2689
3216
  WorkerCapabilitiesSchema,
3217
+ buildDeduplicationMetrics,
3218
+ calculateDeduplicationSavings,
2690
3219
  createAggregatorNode,
2691
3220
  createExecutorNode,
2692
3221
  createFinisherNode,
2693
3222
  createGeneratorNode,
2694
3223
  createMultiAgentSystem,
3224
+ createPatternLogger,
2695
3225
  createPlanExecuteAgent,
2696
3226
  createPlannerNode,
2697
3227
  createReActAgent,
@@ -2703,6 +3233,7 @@ export {
2703
3233
  createReviserNode,
2704
3234
  createSupervisorNode,
2705
3235
  createWorkerNode,
3236
+ generateToolCallCacheKey,
2706
3237
  getRoutingStrategy,
2707
3238
  llmBasedRouting,
2708
3239
  loadBalancedRouting,