@agentforge/patterns 0.15.8 → 0.15.10

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.
Files changed (3) hide show
  1. package/dist/index.cjs +121 -79
  2. package/dist/index.js +127 -80
  3. package/package.json +3 -3
package/dist/index.cjs CHANGED
@@ -343,54 +343,101 @@ function handleNodeError(error, context, verbose = false) {
343
343
  var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
344
344
  var actionLogger = createPatternLogger("agentforge:patterns:react:action");
345
345
  var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
346
+ function normalizeConversationMessage(message) {
347
+ switch (message.role) {
348
+ case "user":
349
+ return new import_messages.HumanMessage(message.content);
350
+ case "assistant":
351
+ return new import_messages.AIMessage(message.content);
352
+ case "system":
353
+ return new import_messages.SystemMessage(message.content);
354
+ case "tool":
355
+ if (!message.tool_call_id) {
356
+ reasoningLogger.warn(
357
+ "Tool message missing tool_call_id; falling back to human message",
358
+ message.name ? { name: message.name } : void 0
359
+ );
360
+ return new import_messages.HumanMessage(message.content);
361
+ }
362
+ return new import_messages.ToolMessage({
363
+ content: message.content,
364
+ tool_call_id: message.tool_call_id,
365
+ name: message.name
366
+ });
367
+ default:
368
+ return new import_messages.HumanMessage(message.content);
369
+ }
370
+ }
371
+ function buildReasoningMessages(systemPrompt, stateMessages, scratchpad) {
372
+ const messages = [
373
+ new import_messages.SystemMessage(systemPrompt),
374
+ ...stateMessages.map(normalizeConversationMessage)
375
+ ];
376
+ if (scratchpad.length > 0) {
377
+ messages.push(new import_messages.SystemMessage(`Previous steps:
378
+ ${formatScratchpad(scratchpad)}`));
379
+ }
380
+ return messages;
381
+ }
382
+ function extractToolCalls(response) {
383
+ if (!response.tool_calls || response.tool_calls.length === 0) {
384
+ return [];
385
+ }
386
+ return response.tool_calls.map((toolCall) => ({
387
+ id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
388
+ name: toolCall.name,
389
+ arguments: toolCall.args ?? {},
390
+ timestamp: Date.now()
391
+ }));
392
+ }
393
+ function formatObservationContent(observation) {
394
+ if (observation.error) {
395
+ return `Error: ${observation.error}`;
396
+ }
397
+ return stringifyObservationResult(observation.result, 2);
398
+ }
399
+ function formatActionSummary(actions) {
400
+ return actions.map((action) => `${action.name}(${JSON.stringify(action.arguments)})`).join(", ");
401
+ }
402
+ function formatObservationSummary(observations) {
403
+ return observations.map((observation) => {
404
+ if (observation.error) {
405
+ return `Error: ${observation.error}`;
406
+ }
407
+ return stringifyObservationResult(observation.result);
408
+ }).join("; ");
409
+ }
410
+ function stringifyObservationResult(result, space) {
411
+ if (typeof result === "string") {
412
+ return result;
413
+ }
414
+ const stringified = JSON.stringify(result, null, space);
415
+ return stringified ?? String(result);
416
+ }
417
+ function getLatestThought(thoughts) {
418
+ return thoughts[thoughts.length - 1]?.content ?? "";
419
+ }
420
+ function debugIfVerbose(logger4, verbose, message, data) {
421
+ if (verbose) {
422
+ logger4.debug(message, data);
423
+ }
424
+ }
346
425
  function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
347
426
  const langchainTools = (0, import_core3.toLangChainTools)(tools);
348
427
  const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
349
428
  return async (state) => {
350
429
  const currentIteration = state.iteration || 0;
351
430
  const startTime = Date.now();
352
- reasoningLogger.debug("Reasoning iteration started", {
431
+ debugIfVerbose(reasoningLogger, verbose, "Reasoning iteration started", {
353
432
  iteration: currentIteration + 1,
354
433
  maxIterations,
355
- observationCount: state.observations?.length || 0,
356
- hasActions: !!state.actions?.length
434
+ observationCount: state.observations.length,
435
+ hasActions: state.actions.length > 0
357
436
  });
358
- const stateMessages = state.messages || [];
359
- const messages = [
360
- new import_messages.SystemMessage(systemPrompt),
361
- ...stateMessages.map((msg) => {
362
- if (msg.role === "user") return new import_messages.HumanMessage(msg.content);
363
- if (msg.role === "assistant") return new import_messages.AIMessage(msg.content);
364
- if (msg.role === "system") return new import_messages.SystemMessage(msg.content);
365
- if (msg.role === "tool") {
366
- return new import_messages.ToolMessage({
367
- content: msg.content,
368
- tool_call_id: msg.tool_call_id,
369
- name: msg.name
370
- });
371
- }
372
- return new import_messages.HumanMessage(msg.content);
373
- })
374
- ];
375
- const scratchpad = state.scratchpad || [];
376
- if (scratchpad.length > 0) {
377
- const scratchpadText = formatScratchpad(scratchpad);
378
- messages.push(new import_messages.SystemMessage(`Previous steps:
379
- ${scratchpadText}`));
380
- }
437
+ const messages = buildReasoningMessages(systemPrompt, state.messages, state.scratchpad);
381
438
  const response = await llmWithTools.invoke(messages);
382
439
  const thought = typeof response.content === "string" ? response.content : "";
383
- const toolCalls = [];
384
- if (response.tool_calls && response.tool_calls.length > 0) {
385
- for (const toolCall of response.tool_calls) {
386
- toolCalls.push({
387
- id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
388
- name: toolCall.name,
389
- arguments: toolCall.args || {},
390
- timestamp: Date.now()
391
- });
392
- }
393
- }
440
+ const toolCalls = extractToolCalls(response);
394
441
  const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
395
442
  reasoningLogger.info("Reasoning complete", {
396
443
  iteration: currentIteration + 1,
@@ -415,11 +462,11 @@ ${scratchpadText}`));
415
462
  function createActionNode(tools, verbose = false, enableDeduplication = true) {
416
463
  const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
417
464
  return async (state) => {
418
- const actions = state.actions || [];
419
- const allObservations = state.observations || [];
465
+ const actions = state.actions;
466
+ const allObservations = state.observations;
420
467
  const iteration = state.iteration || 0;
421
468
  const startTime = Date.now();
422
- actionLogger.debug("Action node started", {
469
+ debugIfVerbose(actionLogger, verbose, "Action node started", {
423
470
  actionCount: actions.length,
424
471
  iteration,
425
472
  cacheEnabled: enableDeduplication
@@ -438,7 +485,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
438
485
  }
439
486
  }
440
487
  if (cacheSize > 0) {
441
- actionLogger.debug("Deduplication cache built", {
488
+ debugIfVerbose(actionLogger, verbose, "Deduplication cache built", {
442
489
  cacheSize,
443
490
  totalObservations: allObservations.length
444
491
  });
@@ -449,7 +496,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
449
496
  for (const action of recentActions) {
450
497
  const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
451
498
  if (existingObservation) {
452
- actionLogger.debug("Skipping already-processed action", {
499
+ debugIfVerbose(actionLogger, verbose, "Skipping already-processed action", {
453
500
  toolName: action.name,
454
501
  toolCallId: action.id,
455
502
  iteration
@@ -492,7 +539,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
492
539
  const result = await tool.invoke(action.arguments);
493
540
  const executionTime = Date.now() - startTime2;
494
541
  toolsExecuted++;
495
- actionLogger.debug("Tool executed successfully", {
542
+ debugIfVerbose(actionLogger, verbose, "Tool executed successfully", {
496
543
  toolName: action.name,
497
544
  executionTime,
498
545
  iteration
@@ -508,7 +555,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
508
555
  executionCache.set(cacheKey, observation);
509
556
  }
510
557
  } catch (error) {
511
- const errorMessage = handleNodeError(error, `action:${action.name}`, false);
558
+ const errorMessage = handleNodeError(error, `action:${action.name}`, verbose);
512
559
  actionLogger.error("Tool execution failed", {
513
560
  toolName: action.name,
514
561
  error: errorMessage,
@@ -537,39 +584,31 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
537
584
  }
538
585
  function createObservationNode(verbose = false, returnIntermediateSteps = false) {
539
586
  return async (state) => {
540
- const observations = state.observations || [];
541
- const thoughts = state.thoughts || [];
542
- const actions = state.actions || [];
587
+ const observations = state.observations;
588
+ const thoughts = state.thoughts;
589
+ const actions = state.actions;
543
590
  const iteration = state.iteration || 0;
544
- observationLogger.debug("Processing observations", {
591
+ debugIfVerbose(observationLogger, verbose, "Processing observations", {
545
592
  observationCount: observations.length,
546
593
  iteration
547
594
  });
548
595
  const recentObservations = observations.slice(-10);
549
596
  const latestActions = actions.slice(-10);
550
- const observationMessages = recentObservations.map((obs) => {
551
- const content = obs.error ? `Error: ${obs.error}` : typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result, null, 2);
552
- return {
553
- role: "tool",
554
- content,
555
- name: latestActions.find((a) => a.id === obs.toolCallId)?.name,
556
- tool_call_id: obs.toolCallId
557
- // Include tool_call_id for proper ToolMessage construction
558
- };
559
- });
597
+ const actionNamesById = new Map(latestActions.map((action) => [action.id, action.name]));
598
+ const observationMessages = recentObservations.map((observation) => ({
599
+ role: "tool",
600
+ content: formatObservationContent(observation),
601
+ name: actionNamesById.get(observation.toolCallId),
602
+ tool_call_id: observation.toolCallId
603
+ }));
560
604
  const scratchpadEntries = returnIntermediateSteps ? [{
561
605
  step: state.iteration,
562
- thought: thoughts[thoughts.length - 1]?.content || "",
563
- action: latestActions.map((a) => `${a.name}(${JSON.stringify(a.arguments)})`).join(", "),
564
- observation: recentObservations.map((obs) => {
565
- if (obs.error) {
566
- return `Error: ${obs.error}`;
567
- }
568
- return typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result);
569
- }).join("; "),
606
+ thought: getLatestThought(thoughts),
607
+ action: formatActionSummary(latestActions),
608
+ observation: formatObservationSummary(recentObservations),
570
609
  timestamp: Date.now()
571
610
  }] : [];
572
- observationLogger.debug("Observation node complete", {
611
+ debugIfVerbose(observationLogger, verbose, "Observation node complete", {
573
612
  iteration,
574
613
  scratchpadUpdated: returnIntermediateSteps,
575
614
  messageCount: observationMessages.length
@@ -1039,7 +1078,7 @@ function createPlannerNode(config) {
1039
1078
  plannerLogger.info("Plan created", {
1040
1079
  stepCount: plan.steps.length,
1041
1080
  goal: plan.goal.substring(0, 100),
1042
- confidence: plan.confidence,
1081
+ ...plan.confidence !== void 0 ? { confidence: plan.confidence } : {},
1043
1082
  duration: Date.now() - startTime
1044
1083
  });
1045
1084
  return {
@@ -1129,7 +1168,10 @@ function createExecutorNode(config) {
1129
1168
  executorLogger.info("Duplicate step execution prevented", {
1130
1169
  stepId: currentStep.id,
1131
1170
  toolName: currentStep.tool,
1132
- arguments: currentStep.args,
1171
+ ...currentStep.args ? {
1172
+ argumentKeys: Object.keys(currentStep.args),
1173
+ argumentCount: Object.keys(currentStep.args).length
1174
+ } : {},
1133
1175
  iteration,
1134
1176
  cacheHit: true
1135
1177
  });
@@ -1165,7 +1207,7 @@ function createExecutorNode(config) {
1165
1207
  result = null;
1166
1208
  executorLogger.warn("Step execution failed", {
1167
1209
  stepId: currentStep.id,
1168
- toolName: currentStep.tool,
1210
+ ...currentStep.tool ? { toolName: currentStep.tool } : {},
1169
1211
  error,
1170
1212
  iteration
1171
1213
  });
@@ -1243,7 +1285,7 @@ function createReplannerNode(config) {
1243
1285
  if (decision.shouldReplan) {
1244
1286
  replannerLogger.info("Replanning triggered", {
1245
1287
  reason: decision.reason,
1246
- newGoal: decision.newGoal?.substring(0, 100),
1288
+ ...decision.newGoal ? { newGoal: decision.newGoal.substring(0, 100) } : {},
1247
1289
  duration: Date.now() - startTime
1248
1290
  });
1249
1291
  return {
@@ -1745,7 +1787,7 @@ function createReflectorNode(config) {
1745
1787
  }
1746
1788
  reflectorLogger.info("Reflection complete", {
1747
1789
  attempt: state.iteration,
1748
- score: reflection.score,
1790
+ ...reflection.score !== void 0 ? { score: reflection.score } : {},
1749
1791
  meetsStandards: reflection.meetsStandards,
1750
1792
  issueCount: reflection.issues.length,
1751
1793
  suggestionCount: reflection.suggestions.length,
@@ -1787,7 +1829,7 @@ function createReviserNode(config) {
1787
1829
  const lastReflection = state.reflections[state.reflections.length - 1];
1788
1830
  reviserLogger.debug("Revising response", {
1789
1831
  attempt: state.iteration,
1790
- previousScore: lastReflection.score,
1832
+ ...lastReflection.score !== void 0 ? { previousScore: lastReflection.score } : {},
1791
1833
  issueCount: lastReflection.issues.length,
1792
1834
  suggestionCount: lastReflection.suggestions.length
1793
1835
  });
@@ -1815,7 +1857,7 @@ ${revisionsText}`;
1815
1857
  reviserLogger.info("Revision complete", {
1816
1858
  attempt: state.iteration,
1817
1859
  revisionLength: content.length,
1818
- basedOnScore: lastReflection.score,
1860
+ ...lastReflection.score !== void 0 ? { basedOnScore: lastReflection.score } : {},
1819
1861
  duration: Date.now() - startTime
1820
1862
  });
1821
1863
  return {
@@ -2507,8 +2549,8 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2507
2549
  } : runnableConfig;
2508
2550
  logger.debug("Invoking ReAct agent with worker-specific config", {
2509
2551
  workerId,
2510
- parentThreadId: runnableConfig?.configurable?.thread_id,
2511
- workerThreadId,
2552
+ ...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
2553
+ ...workerThreadId ? { workerThreadId } : {},
2512
2554
  hasConfig: !!workerConfig
2513
2555
  });
2514
2556
  const result = await agent.invoke(
@@ -2741,7 +2783,7 @@ function createSupervisorNode(config) {
2741
2783
  } catch (error) {
2742
2784
  logger2.error("Supervisor node error", {
2743
2785
  error: error instanceof Error ? error.message : String(error),
2744
- stack: error instanceof Error ? error.stack : void 0,
2786
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2745
2787
  iteration: state.iteration
2746
2788
  });
2747
2789
  return {
@@ -3017,7 +3059,7 @@ Please synthesize these results into a comprehensive response that addresses the
3017
3059
  } catch (error) {
3018
3060
  logger2.error("Aggregator node error", {
3019
3061
  error: error instanceof Error ? error.message : String(error),
3020
- stack: error instanceof Error ? error.stack : void 0,
3062
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {},
3021
3063
  completedTasks: state.completedTasks.length
3022
3064
  });
3023
3065
  return {
@@ -3083,7 +3125,7 @@ function createMultiAgentSystem(config) {
3083
3125
  const supervisorRouter = (state) => {
3084
3126
  logger3.debug("Supervisor router executing", {
3085
3127
  status: state.status,
3086
- currentAgent: state.currentAgent,
3128
+ ...state.currentAgent ? { currentAgent: state.currentAgent } : {},
3087
3129
  iteration: state.iteration
3088
3130
  });
3089
3131
  if (state.status === "completed" || state.status === "failed") {
package/dist/index.js CHANGED
@@ -171,7 +171,12 @@ function formatScratchpad(scratchpad) {
171
171
  }
172
172
 
173
173
  // src/react/nodes.ts
174
- import { HumanMessage, AIMessage, SystemMessage, ToolMessage } from "@langchain/core/messages";
174
+ import {
175
+ HumanMessage,
176
+ AIMessage,
177
+ SystemMessage,
178
+ ToolMessage
179
+ } from "@langchain/core/messages";
175
180
  import { toLangChainTools } from "@agentforge/core";
176
181
 
177
182
  // src/shared/deduplication.ts
@@ -240,54 +245,101 @@ function handleNodeError(error, context, verbose = false) {
240
245
  var reasoningLogger = createPatternLogger("agentforge:patterns:react:reasoning");
241
246
  var actionLogger = createPatternLogger("agentforge:patterns:react:action");
242
247
  var observationLogger = createPatternLogger("agentforge:patterns:react:observation");
248
+ function normalizeConversationMessage(message) {
249
+ switch (message.role) {
250
+ case "user":
251
+ return new HumanMessage(message.content);
252
+ case "assistant":
253
+ return new AIMessage(message.content);
254
+ case "system":
255
+ return new SystemMessage(message.content);
256
+ case "tool":
257
+ if (!message.tool_call_id) {
258
+ reasoningLogger.warn(
259
+ "Tool message missing tool_call_id; falling back to human message",
260
+ message.name ? { name: message.name } : void 0
261
+ );
262
+ return new HumanMessage(message.content);
263
+ }
264
+ return new ToolMessage({
265
+ content: message.content,
266
+ tool_call_id: message.tool_call_id,
267
+ name: message.name
268
+ });
269
+ default:
270
+ return new HumanMessage(message.content);
271
+ }
272
+ }
273
+ function buildReasoningMessages(systemPrompt, stateMessages, scratchpad) {
274
+ const messages = [
275
+ new SystemMessage(systemPrompt),
276
+ ...stateMessages.map(normalizeConversationMessage)
277
+ ];
278
+ if (scratchpad.length > 0) {
279
+ messages.push(new SystemMessage(`Previous steps:
280
+ ${formatScratchpad(scratchpad)}`));
281
+ }
282
+ return messages;
283
+ }
284
+ function extractToolCalls(response) {
285
+ if (!response.tool_calls || response.tool_calls.length === 0) {
286
+ return [];
287
+ }
288
+ return response.tool_calls.map((toolCall) => ({
289
+ id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
290
+ name: toolCall.name,
291
+ arguments: toolCall.args ?? {},
292
+ timestamp: Date.now()
293
+ }));
294
+ }
295
+ function formatObservationContent(observation) {
296
+ if (observation.error) {
297
+ return `Error: ${observation.error}`;
298
+ }
299
+ return stringifyObservationResult(observation.result, 2);
300
+ }
301
+ function formatActionSummary(actions) {
302
+ return actions.map((action) => `${action.name}(${JSON.stringify(action.arguments)})`).join(", ");
303
+ }
304
+ function formatObservationSummary(observations) {
305
+ return observations.map((observation) => {
306
+ if (observation.error) {
307
+ return `Error: ${observation.error}`;
308
+ }
309
+ return stringifyObservationResult(observation.result);
310
+ }).join("; ");
311
+ }
312
+ function stringifyObservationResult(result, space) {
313
+ if (typeof result === "string") {
314
+ return result;
315
+ }
316
+ const stringified = JSON.stringify(result, null, space);
317
+ return stringified ?? String(result);
318
+ }
319
+ function getLatestThought(thoughts) {
320
+ return thoughts[thoughts.length - 1]?.content ?? "";
321
+ }
322
+ function debugIfVerbose(logger4, verbose, message, data) {
323
+ if (verbose) {
324
+ logger4.debug(message, data);
325
+ }
326
+ }
243
327
  function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
244
328
  const langchainTools = toLangChainTools(tools);
245
329
  const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
246
330
  return async (state) => {
247
331
  const currentIteration = state.iteration || 0;
248
332
  const startTime = Date.now();
249
- reasoningLogger.debug("Reasoning iteration started", {
333
+ debugIfVerbose(reasoningLogger, verbose, "Reasoning iteration started", {
250
334
  iteration: currentIteration + 1,
251
335
  maxIterations,
252
- observationCount: state.observations?.length || 0,
253
- hasActions: !!state.actions?.length
336
+ observationCount: state.observations.length,
337
+ hasActions: state.actions.length > 0
254
338
  });
255
- const stateMessages = state.messages || [];
256
- const messages = [
257
- new SystemMessage(systemPrompt),
258
- ...stateMessages.map((msg) => {
259
- if (msg.role === "user") return new HumanMessage(msg.content);
260
- if (msg.role === "assistant") return new AIMessage(msg.content);
261
- if (msg.role === "system") return new SystemMessage(msg.content);
262
- if (msg.role === "tool") {
263
- return new ToolMessage({
264
- content: msg.content,
265
- tool_call_id: msg.tool_call_id,
266
- name: msg.name
267
- });
268
- }
269
- return new HumanMessage(msg.content);
270
- })
271
- ];
272
- const scratchpad = state.scratchpad || [];
273
- if (scratchpad.length > 0) {
274
- const scratchpadText = formatScratchpad(scratchpad);
275
- messages.push(new SystemMessage(`Previous steps:
276
- ${scratchpadText}`));
277
- }
339
+ const messages = buildReasoningMessages(systemPrompt, state.messages, state.scratchpad);
278
340
  const response = await llmWithTools.invoke(messages);
279
341
  const thought = typeof response.content === "string" ? response.content : "";
280
- const toolCalls = [];
281
- if (response.tool_calls && response.tool_calls.length > 0) {
282
- for (const toolCall of response.tool_calls) {
283
- toolCalls.push({
284
- id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
285
- name: toolCall.name,
286
- arguments: toolCall.args || {},
287
- timestamp: Date.now()
288
- });
289
- }
290
- }
342
+ const toolCalls = extractToolCalls(response);
291
343
  const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
292
344
  reasoningLogger.info("Reasoning complete", {
293
345
  iteration: currentIteration + 1,
@@ -312,11 +364,11 @@ ${scratchpadText}`));
312
364
  function createActionNode(tools, verbose = false, enableDeduplication = true) {
313
365
  const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
314
366
  return async (state) => {
315
- const actions = state.actions || [];
316
- const allObservations = state.observations || [];
367
+ const actions = state.actions;
368
+ const allObservations = state.observations;
317
369
  const iteration = state.iteration || 0;
318
370
  const startTime = Date.now();
319
- actionLogger.debug("Action node started", {
371
+ debugIfVerbose(actionLogger, verbose, "Action node started", {
320
372
  actionCount: actions.length,
321
373
  iteration,
322
374
  cacheEnabled: enableDeduplication
@@ -335,7 +387,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
335
387
  }
336
388
  }
337
389
  if (cacheSize > 0) {
338
- actionLogger.debug("Deduplication cache built", {
390
+ debugIfVerbose(actionLogger, verbose, "Deduplication cache built", {
339
391
  cacheSize,
340
392
  totalObservations: allObservations.length
341
393
  });
@@ -346,7 +398,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
346
398
  for (const action of recentActions) {
347
399
  const existingObservation = allObservations.find((obs) => obs.toolCallId === action.id);
348
400
  if (existingObservation) {
349
- actionLogger.debug("Skipping already-processed action", {
401
+ debugIfVerbose(actionLogger, verbose, "Skipping already-processed action", {
350
402
  toolName: action.name,
351
403
  toolCallId: action.id,
352
404
  iteration
@@ -389,7 +441,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
389
441
  const result = await tool.invoke(action.arguments);
390
442
  const executionTime = Date.now() - startTime2;
391
443
  toolsExecuted++;
392
- actionLogger.debug("Tool executed successfully", {
444
+ debugIfVerbose(actionLogger, verbose, "Tool executed successfully", {
393
445
  toolName: action.name,
394
446
  executionTime,
395
447
  iteration
@@ -405,7 +457,7 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
405
457
  executionCache.set(cacheKey, observation);
406
458
  }
407
459
  } catch (error) {
408
- const errorMessage = handleNodeError(error, `action:${action.name}`, false);
460
+ const errorMessage = handleNodeError(error, `action:${action.name}`, verbose);
409
461
  actionLogger.error("Tool execution failed", {
410
462
  toolName: action.name,
411
463
  error: errorMessage,
@@ -434,39 +486,31 @@ function createActionNode(tools, verbose = false, enableDeduplication = true) {
434
486
  }
435
487
  function createObservationNode(verbose = false, returnIntermediateSteps = false) {
436
488
  return async (state) => {
437
- const observations = state.observations || [];
438
- const thoughts = state.thoughts || [];
439
- const actions = state.actions || [];
489
+ const observations = state.observations;
490
+ const thoughts = state.thoughts;
491
+ const actions = state.actions;
440
492
  const iteration = state.iteration || 0;
441
- observationLogger.debug("Processing observations", {
493
+ debugIfVerbose(observationLogger, verbose, "Processing observations", {
442
494
  observationCount: observations.length,
443
495
  iteration
444
496
  });
445
497
  const recentObservations = observations.slice(-10);
446
498
  const latestActions = actions.slice(-10);
447
- const observationMessages = recentObservations.map((obs) => {
448
- const content = obs.error ? `Error: ${obs.error}` : typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result, null, 2);
449
- return {
450
- role: "tool",
451
- content,
452
- name: latestActions.find((a) => a.id === obs.toolCallId)?.name,
453
- tool_call_id: obs.toolCallId
454
- // Include tool_call_id for proper ToolMessage construction
455
- };
456
- });
499
+ const actionNamesById = new Map(latestActions.map((action) => [action.id, action.name]));
500
+ const observationMessages = recentObservations.map((observation) => ({
501
+ role: "tool",
502
+ content: formatObservationContent(observation),
503
+ name: actionNamesById.get(observation.toolCallId),
504
+ tool_call_id: observation.toolCallId
505
+ }));
457
506
  const scratchpadEntries = returnIntermediateSteps ? [{
458
507
  step: state.iteration,
459
- thought: thoughts[thoughts.length - 1]?.content || "",
460
- action: latestActions.map((a) => `${a.name}(${JSON.stringify(a.arguments)})`).join(", "),
461
- observation: recentObservations.map((obs) => {
462
- if (obs.error) {
463
- return `Error: ${obs.error}`;
464
- }
465
- return typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result);
466
- }).join("; "),
508
+ thought: getLatestThought(thoughts),
509
+ action: formatActionSummary(latestActions),
510
+ observation: formatObservationSummary(recentObservations),
467
511
  timestamp: Date.now()
468
512
  }] : [];
469
- observationLogger.debug("Observation node complete", {
513
+ debugIfVerbose(observationLogger, verbose, "Observation node complete", {
470
514
  iteration,
471
515
  scratchpadUpdated: returnIntermediateSteps,
472
516
  messageCount: observationMessages.length
@@ -936,7 +980,7 @@ function createPlannerNode(config) {
936
980
  plannerLogger.info("Plan created", {
937
981
  stepCount: plan.steps.length,
938
982
  goal: plan.goal.substring(0, 100),
939
- confidence: plan.confidence,
983
+ ...plan.confidence !== void 0 ? { confidence: plan.confidence } : {},
940
984
  duration: Date.now() - startTime
941
985
  });
942
986
  return {
@@ -1026,7 +1070,10 @@ function createExecutorNode(config) {
1026
1070
  executorLogger.info("Duplicate step execution prevented", {
1027
1071
  stepId: currentStep.id,
1028
1072
  toolName: currentStep.tool,
1029
- arguments: currentStep.args,
1073
+ ...currentStep.args ? {
1074
+ argumentKeys: Object.keys(currentStep.args),
1075
+ argumentCount: Object.keys(currentStep.args).length
1076
+ } : {},
1030
1077
  iteration,
1031
1078
  cacheHit: true
1032
1079
  });
@@ -1062,7 +1109,7 @@ function createExecutorNode(config) {
1062
1109
  result = null;
1063
1110
  executorLogger.warn("Step execution failed", {
1064
1111
  stepId: currentStep.id,
1065
- toolName: currentStep.tool,
1112
+ ...currentStep.tool ? { toolName: currentStep.tool } : {},
1066
1113
  error,
1067
1114
  iteration
1068
1115
  });
@@ -1140,7 +1187,7 @@ function createReplannerNode(config) {
1140
1187
  if (decision.shouldReplan) {
1141
1188
  replannerLogger.info("Replanning triggered", {
1142
1189
  reason: decision.reason,
1143
- newGoal: decision.newGoal?.substring(0, 100),
1190
+ ...decision.newGoal ? { newGoal: decision.newGoal.substring(0, 100) } : {},
1144
1191
  duration: Date.now() - startTime
1145
1192
  });
1146
1193
  return {
@@ -1642,7 +1689,7 @@ function createReflectorNode(config) {
1642
1689
  }
1643
1690
  reflectorLogger.info("Reflection complete", {
1644
1691
  attempt: state.iteration,
1645
- score: reflection.score,
1692
+ ...reflection.score !== void 0 ? { score: reflection.score } : {},
1646
1693
  meetsStandards: reflection.meetsStandards,
1647
1694
  issueCount: reflection.issues.length,
1648
1695
  suggestionCount: reflection.suggestions.length,
@@ -1684,7 +1731,7 @@ function createReviserNode(config) {
1684
1731
  const lastReflection = state.reflections[state.reflections.length - 1];
1685
1732
  reviserLogger.debug("Revising response", {
1686
1733
  attempt: state.iteration,
1687
- previousScore: lastReflection.score,
1734
+ ...lastReflection.score !== void 0 ? { previousScore: lastReflection.score } : {},
1688
1735
  issueCount: lastReflection.issues.length,
1689
1736
  suggestionCount: lastReflection.suggestions.length
1690
1737
  });
@@ -1712,7 +1759,7 @@ ${revisionsText}`;
1712
1759
  reviserLogger.info("Revision complete", {
1713
1760
  attempt: state.iteration,
1714
1761
  revisionLength: content.length,
1715
- basedOnScore: lastReflection.score,
1762
+ ...lastReflection.score !== void 0 ? { basedOnScore: lastReflection.score } : {},
1716
1763
  duration: Date.now() - startTime
1717
1764
  });
1718
1765
  return {
@@ -2404,8 +2451,8 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2404
2451
  } : runnableConfig;
2405
2452
  logger.debug("Invoking ReAct agent with worker-specific config", {
2406
2453
  workerId,
2407
- parentThreadId: runnableConfig?.configurable?.thread_id,
2408
- workerThreadId,
2454
+ ...runnableConfig?.configurable?.thread_id !== void 0 ? { parentThreadId: String(runnableConfig.configurable.thread_id) } : {},
2455
+ ...workerThreadId ? { workerThreadId } : {},
2409
2456
  hasConfig: !!workerConfig
2410
2457
  });
2411
2458
  const result = await agent.invoke(
@@ -2638,7 +2685,7 @@ function createSupervisorNode(config) {
2638
2685
  } catch (error) {
2639
2686
  logger2.error("Supervisor node error", {
2640
2687
  error: error instanceof Error ? error.message : String(error),
2641
- stack: error instanceof Error ? error.stack : void 0,
2688
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2642
2689
  iteration: state.iteration
2643
2690
  });
2644
2691
  return {
@@ -2914,7 +2961,7 @@ Please synthesize these results into a comprehensive response that addresses the
2914
2961
  } catch (error) {
2915
2962
  logger2.error("Aggregator node error", {
2916
2963
  error: error instanceof Error ? error.message : String(error),
2917
- stack: error instanceof Error ? error.stack : void 0,
2964
+ ...error instanceof Error && error.stack ? { stack: error.stack } : {},
2918
2965
  completedTasks: state.completedTasks.length
2919
2966
  });
2920
2967
  return {
@@ -2980,7 +3027,7 @@ function createMultiAgentSystem(config) {
2980
3027
  const supervisorRouter = (state) => {
2981
3028
  logger3.debug("Supervisor router executing", {
2982
3029
  status: state.status,
2983
- currentAgent: state.currentAgent,
3030
+ ...state.currentAgent ? { currentAgent: state.currentAgent } : {},
2984
3031
  iteration: state.iteration
2985
3032
  });
2986
3033
  if (state.status === "completed" || state.status === "failed") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.15.8",
3
+ "version": "0.15.10",
4
4
  "description": "Production-ready agent workflow patterns for TypeScript including ReAct and Planner-Executor, built on LangGraph.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -41,13 +41,13 @@
41
41
  "url": "https://github.com/TVScoundrel/agentforge/issues"
42
42
  },
43
43
  "dependencies": {
44
- "@agentforge/core": "0.15.8",
44
+ "@agentforge/core": "0.15.10",
45
45
  "@langchain/core": "^1.1.17",
46
46
  "@langchain/langgraph": "^1.1.2",
47
47
  "zod": "^3.23.8"
48
48
  },
49
49
  "devDependencies": {
50
- "@agentforge/testing": "0.15.8",
50
+ "@agentforge/testing": "0.15.10",
51
51
  "@eslint/js": "^9.17.0",
52
52
  "@types/node": "^22.10.2",
53
53
  "eslint": "^9.17.0",