@agentforge/patterns 0.11.1 → 0.11.3

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/README.md CHANGED
@@ -514,6 +514,13 @@ pnpm test:coverage
514
514
  pnpm typecheck
515
515
  ```
516
516
 
517
+ ## 🔗 Links
518
+
519
+ - [GitHub Repository](https://github.com/TVScoundrel/agentforge)
520
+ - [npm Package](https://www.npmjs.com/package/@agentforge/patterns)
521
+ - [Changelog](https://tvscoundrel.github.io/agentforge/changelog.html) - See what's new before upgrading
522
+ - [Report Issues](https://github.com/TVScoundrel/agentforge/issues)
523
+
517
524
  ## License
518
525
 
519
526
  MIT © 2026 Tom Van Schoor
package/dist/index.cjs CHANGED
@@ -2608,11 +2608,29 @@ function createSupervisorNode(config) {
2608
2608
  priority: assignment.priority
2609
2609
  }
2610
2610
  }));
2611
+ const updatedWorkers = { ...state.workers };
2612
+ for (const assignment of assignments) {
2613
+ const worker = updatedWorkers[assignment.workerId];
2614
+ if (!worker) {
2615
+ logger2.error("Worker not found in state", {
2616
+ workerId: assignment.workerId,
2617
+ availableWorkers: Object.keys(updatedWorkers)
2618
+ });
2619
+ throw new Error(
2620
+ `Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
2621
+ );
2622
+ }
2623
+ updatedWorkers[assignment.workerId] = {
2624
+ ...worker,
2625
+ currentWorkload: worker.currentWorkload + 1
2626
+ };
2627
+ }
2611
2628
  logger2.info("Supervisor routing complete", {
2612
2629
  currentAgent: targetAgents.join(","),
2613
2630
  status: "executing",
2614
2631
  assignmentCount: assignments.length,
2615
- nextIteration: state.iteration + 1
2632
+ nextIteration: state.iteration + 1,
2633
+ workloadUpdates: Object.entries(updatedWorkers).filter(([id]) => targetAgents.includes(id)).map(([id, caps]) => ({ workerId: id, newWorkload: caps.currentWorkload }))
2616
2634
  });
2617
2635
  return {
2618
2636
  currentAgent: targetAgents.join(","),
@@ -2622,6 +2640,8 @@ function createSupervisorNode(config) {
2622
2640
  activeAssignments: assignments,
2623
2641
  // Multiple assignments for parallel execution!
2624
2642
  messages,
2643
+ workers: updatedWorkers,
2644
+ // Include updated workload
2625
2645
  // Add 1 to iteration counter (uses additive reducer)
2626
2646
  iteration: 1
2627
2647
  };
@@ -2673,94 +2693,104 @@ function createWorkerNode(config) {
2673
2693
  taskLength: currentAssignment.task.length,
2674
2694
  taskPreview: currentAssignment.task.substring(0, 100)
2675
2695
  });
2676
- if (executeFn) {
2677
- logger2.debug("Using custom execution function", { workerId: id });
2678
- return await executeFn(state, runConfig);
2679
- }
2680
- if (agent) {
2681
- if (isReActAgent(agent)) {
2682
- logger2.debug("Using ReAct agent", { workerId: id });
2683
- const wrappedFn = wrapReActAgent(id, agent, verbose);
2684
- return await wrappedFn(state, runConfig);
2685
- } else {
2686
- logger2.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
2687
- }
2688
- }
2689
- if (!model) {
2690
- logger2.error("Worker missing required configuration", { workerId: id });
2691
- throw new Error(
2692
- `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2693
- );
2694
- }
2695
- logger2.debug("Using default LLM execution", {
2696
- workerId: id,
2697
- hasTools: tools.length > 0,
2698
- toolCount: tools.length
2699
- });
2700
- const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2696
+ async function executeWithLLM() {
2697
+ logger2.debug("Using default LLM execution", {
2698
+ workerId: id,
2699
+ hasTools: tools.length > 0,
2700
+ toolCount: tools.length
2701
+ });
2702
+ const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2701
2703
  Skills: ${capabilities.skills.join(", ")}
2702
2704
  Tools: ${capabilities.tools.join(", ")}
2703
2705
 
2704
2706
  Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
2705
- const messages = [
2706
- new import_messages5.SystemMessage(systemPrompt || defaultSystemPrompt),
2707
- new import_messages5.HumanMessage(currentAssignment.task)
2708
- ];
2709
- let modelToUse = model;
2710
- if (tools.length > 0 && model.bindTools) {
2711
- logger2.debug("Binding tools to model", {
2707
+ const messages = [
2708
+ new import_messages5.SystemMessage(systemPrompt || defaultSystemPrompt),
2709
+ new import_messages5.HumanMessage(currentAssignment.task)
2710
+ ];
2711
+ let modelToUse = model;
2712
+ if (tools.length > 0 && model.bindTools) {
2713
+ logger2.debug("Binding tools to model", {
2714
+ workerId: id,
2715
+ toolCount: tools.length,
2716
+ toolNames: tools.map((t) => t.metadata.name)
2717
+ });
2718
+ const langchainTools = (0, import_core9.toLangChainTools)(tools);
2719
+ modelToUse = model.bindTools(langchainTools);
2720
+ }
2721
+ logger2.debug("Invoking LLM", { workerId: id });
2722
+ const response = await modelToUse.invoke(messages);
2723
+ const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2724
+ logger2.info("Worker task completed", {
2712
2725
  workerId: id,
2713
- toolCount: tools.length,
2714
- toolNames: tools.map((t) => t.metadata.name)
2726
+ assignmentId: currentAssignment.id,
2727
+ resultLength: result.length,
2728
+ resultPreview: result.substring(0, 100)
2715
2729
  });
2716
- const langchainTools = (0, import_core9.toLangChainTools)(tools);
2717
- modelToUse = model.bindTools(langchainTools);
2718
- }
2719
- logger2.debug("Invoking LLM", { workerId: id });
2720
- const response = await modelToUse.invoke(messages);
2721
- const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2722
- logger2.info("Worker task completed", {
2723
- workerId: id,
2724
- assignmentId: currentAssignment.id,
2725
- resultLength: result.length,
2726
- resultPreview: result.substring(0, 100)
2727
- });
2728
- const taskResult = {
2729
- assignmentId: currentAssignment.id,
2730
- workerId: id,
2731
- success: true,
2732
- result,
2733
- completedAt: Date.now(),
2734
- metadata: {
2735
- skills_used: capabilities.skills
2736
- }
2737
- };
2738
- const message = {
2739
- id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2740
- from: id,
2741
- to: ["supervisor"],
2742
- type: "task_result",
2743
- content: result,
2744
- timestamp: Date.now(),
2745
- metadata: {
2730
+ const taskResult = {
2746
2731
  assignmentId: currentAssignment.id,
2747
- success: true
2748
- }
2732
+ workerId: id,
2733
+ success: true,
2734
+ result,
2735
+ completedAt: Date.now(),
2736
+ metadata: {
2737
+ skills_used: capabilities.skills
2738
+ }
2739
+ };
2740
+ const message = {
2741
+ id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2742
+ from: id,
2743
+ to: ["supervisor"],
2744
+ type: "task_result",
2745
+ content: result,
2746
+ timestamp: Date.now(),
2747
+ metadata: {
2748
+ assignmentId: currentAssignment.id,
2749
+ success: true
2750
+ }
2751
+ };
2752
+ return {
2753
+ completedTasks: [taskResult],
2754
+ messages: [message]
2755
+ };
2756
+ }
2757
+ let executionResult;
2758
+ if (executeFn) {
2759
+ logger2.debug("Using custom execution function", { workerId: id });
2760
+ executionResult = await executeFn(state, runConfig);
2761
+ } else if (agent && isReActAgent(agent)) {
2762
+ logger2.debug("Using ReAct agent", { workerId: id });
2763
+ const wrappedFn = wrapReActAgent(id, agent, verbose);
2764
+ executionResult = await wrappedFn(state, runConfig);
2765
+ } else if (model) {
2766
+ executionResult = await executeWithLLM();
2767
+ } else {
2768
+ logger2.error("Worker missing required configuration", { workerId: id });
2769
+ throw new Error(
2770
+ `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2771
+ );
2772
+ }
2773
+ const currentWorker = state.workers[id];
2774
+ const baseWorkers = {
2775
+ ...state.workers,
2776
+ ...executionResult.workers || {}
2749
2777
  };
2778
+ const workerToUpdate = baseWorkers[id] || currentWorker;
2750
2779
  const updatedWorkers = {
2751
- ...state.workers,
2780
+ ...baseWorkers,
2752
2781
  [id]: {
2753
- ...capabilities,
2754
- currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
2782
+ ...workerToUpdate,
2783
+ currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
2755
2784
  }
2756
2785
  };
2757
- logger2.debug("Worker state update", {
2786
+ logger2.debug("Worker workload decremented", {
2758
2787
  workerId: id,
2759
- newWorkload: updatedWorkers[id].currentWorkload
2788
+ previousWorkload: workerToUpdate.currentWorkload,
2789
+ newWorkload: updatedWorkers[id].currentWorkload,
2790
+ hadExecutionResultWorkers: !!executionResult.workers
2760
2791
  });
2761
2792
  return {
2762
- completedTasks: [taskResult],
2763
- messages: [message],
2793
+ ...executionResult,
2764
2794
  workers: updatedWorkers
2765
2795
  };
2766
2796
  } catch (error) {
@@ -2769,6 +2799,19 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2769
2799
  workerId: id,
2770
2800
  error: errorMessage
2771
2801
  });
2802
+ const currentWorker = state.workers[id];
2803
+ const updatedWorkers = {
2804
+ ...state.workers,
2805
+ [id]: {
2806
+ ...currentWorker,
2807
+ currentWorkload: Math.max(0, currentWorker.currentWorkload - 1)
2808
+ }
2809
+ };
2810
+ logger2.debug("Worker workload decremented (error path)", {
2811
+ workerId: id,
2812
+ previousWorkload: currentWorker.currentWorkload,
2813
+ newWorkload: updatedWorkers[id].currentWorkload
2814
+ });
2772
2815
  const currentAssignment = state.activeAssignments.find(
2773
2816
  (assignment) => assignment.workerId === id
2774
2817
  );
@@ -2788,13 +2831,17 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2788
2831
  return {
2789
2832
  completedTasks: [errorResult],
2790
2833
  currentAgent: "supervisor",
2791
- status: "routing"
2834
+ status: "routing",
2835
+ workers: updatedWorkers
2836
+ // Include workload update
2792
2837
  };
2793
2838
  }
2794
2839
  logger2.error("No assignment found for error handling", { workerId: id });
2795
2840
  return {
2796
2841
  status: "failed",
2797
- error: errorMessage
2842
+ error: errorMessage,
2843
+ workers: updatedWorkers
2844
+ // Include workload update even on failure
2798
2845
  };
2799
2846
  }
2800
2847
  };
package/dist/index.d.cts CHANGED
@@ -2817,6 +2817,33 @@ declare const DEFAULT_AGGREGATOR_SYSTEM_PROMPT = "You are an aggregator agent re
2817
2817
  declare function createSupervisorNode(config: SupervisorConfig): (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
2818
2818
  /**
2819
2819
  * Create a worker agent node
2820
+ *
2821
+ * **Workload Management Contract:**
2822
+ * The framework automatically manages `currentWorkload` for all workers:
2823
+ * - Incremented by supervisor when task is assigned
2824
+ * - Decremented by worker when task completes (success or failure)
2825
+ *
2826
+ * Custom `executeFn` implementations should NOT modify `currentWorkload`.
2827
+ * The framework will automatically decrement it after execution completes.
2828
+ *
2829
+ * Custom `executeFn` CAN modify other worker properties (skills, tools,
2830
+ * availability, etc.) and these changes will be preserved and merged with
2831
+ * the workload decrement.
2832
+ *
2833
+ * @example
2834
+ * ```typescript
2835
+ * // ✅ CORRECT: Custom executeFn modifies skills, framework handles workload
2836
+ * const executeFn = async (state) => ({
2837
+ * completedTasks: [...],
2838
+ * workers: {
2839
+ * 'worker1': {
2840
+ * ...state.workers['worker1'],
2841
+ * skills: [...state.workers['worker1'].skills, 'new-skill'], // ✅ OK
2842
+ * // currentWorkload: ... // ❌ DON'T - framework manages this
2843
+ * }
2844
+ * }
2845
+ * });
2846
+ * ```
2820
2847
  */
2821
2848
  declare function createWorkerNode(config: WorkerConfig): (state: MultiAgentStateType, runConfig?: any) => Promise<Partial<MultiAgentStateType>>;
2822
2849
  /**
package/dist/index.d.ts CHANGED
@@ -2817,6 +2817,33 @@ declare const DEFAULT_AGGREGATOR_SYSTEM_PROMPT = "You are an aggregator agent re
2817
2817
  declare function createSupervisorNode(config: SupervisorConfig): (state: MultiAgentStateType) => Promise<Partial<MultiAgentStateType>>;
2818
2818
  /**
2819
2819
  * Create a worker agent node
2820
+ *
2821
+ * **Workload Management Contract:**
2822
+ * The framework automatically manages `currentWorkload` for all workers:
2823
+ * - Incremented by supervisor when task is assigned
2824
+ * - Decremented by worker when task completes (success or failure)
2825
+ *
2826
+ * Custom `executeFn` implementations should NOT modify `currentWorkload`.
2827
+ * The framework will automatically decrement it after execution completes.
2828
+ *
2829
+ * Custom `executeFn` CAN modify other worker properties (skills, tools,
2830
+ * availability, etc.) and these changes will be preserved and merged with
2831
+ * the workload decrement.
2832
+ *
2833
+ * @example
2834
+ * ```typescript
2835
+ * // ✅ CORRECT: Custom executeFn modifies skills, framework handles workload
2836
+ * const executeFn = async (state) => ({
2837
+ * completedTasks: [...],
2838
+ * workers: {
2839
+ * 'worker1': {
2840
+ * ...state.workers['worker1'],
2841
+ * skills: [...state.workers['worker1'].skills, 'new-skill'], // ✅ OK
2842
+ * // currentWorkload: ... // ❌ DON'T - framework manages this
2843
+ * }
2844
+ * }
2845
+ * });
2846
+ * ```
2820
2847
  */
2821
2848
  declare function createWorkerNode(config: WorkerConfig): (state: MultiAgentStateType, runConfig?: any) => Promise<Partial<MultiAgentStateType>>;
2822
2849
  /**
package/dist/index.js CHANGED
@@ -2505,11 +2505,29 @@ function createSupervisorNode(config) {
2505
2505
  priority: assignment.priority
2506
2506
  }
2507
2507
  }));
2508
+ const updatedWorkers = { ...state.workers };
2509
+ for (const assignment of assignments) {
2510
+ const worker = updatedWorkers[assignment.workerId];
2511
+ if (!worker) {
2512
+ logger2.error("Worker not found in state", {
2513
+ workerId: assignment.workerId,
2514
+ availableWorkers: Object.keys(updatedWorkers)
2515
+ });
2516
+ throw new Error(
2517
+ `Worker ${assignment.workerId} not found in state.workers. Available workers: ${Object.keys(updatedWorkers).join(", ")}`
2518
+ );
2519
+ }
2520
+ updatedWorkers[assignment.workerId] = {
2521
+ ...worker,
2522
+ currentWorkload: worker.currentWorkload + 1
2523
+ };
2524
+ }
2508
2525
  logger2.info("Supervisor routing complete", {
2509
2526
  currentAgent: targetAgents.join(","),
2510
2527
  status: "executing",
2511
2528
  assignmentCount: assignments.length,
2512
- nextIteration: state.iteration + 1
2529
+ nextIteration: state.iteration + 1,
2530
+ workloadUpdates: Object.entries(updatedWorkers).filter(([id]) => targetAgents.includes(id)).map(([id, caps]) => ({ workerId: id, newWorkload: caps.currentWorkload }))
2513
2531
  });
2514
2532
  return {
2515
2533
  currentAgent: targetAgents.join(","),
@@ -2519,6 +2537,8 @@ function createSupervisorNode(config) {
2519
2537
  activeAssignments: assignments,
2520
2538
  // Multiple assignments for parallel execution!
2521
2539
  messages,
2540
+ workers: updatedWorkers,
2541
+ // Include updated workload
2522
2542
  // Add 1 to iteration counter (uses additive reducer)
2523
2543
  iteration: 1
2524
2544
  };
@@ -2570,94 +2590,104 @@ function createWorkerNode(config) {
2570
2590
  taskLength: currentAssignment.task.length,
2571
2591
  taskPreview: currentAssignment.task.substring(0, 100)
2572
2592
  });
2573
- if (executeFn) {
2574
- logger2.debug("Using custom execution function", { workerId: id });
2575
- return await executeFn(state, runConfig);
2576
- }
2577
- if (agent) {
2578
- if (isReActAgent(agent)) {
2579
- logger2.debug("Using ReAct agent", { workerId: id });
2580
- const wrappedFn = wrapReActAgent(id, agent, verbose);
2581
- return await wrappedFn(state, runConfig);
2582
- } else {
2583
- logger2.warn("Agent provided but not a ReAct agent, falling back", { workerId: id });
2584
- }
2585
- }
2586
- if (!model) {
2587
- logger2.error("Worker missing required configuration", { workerId: id });
2588
- throw new Error(
2589
- `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2590
- );
2591
- }
2592
- logger2.debug("Using default LLM execution", {
2593
- workerId: id,
2594
- hasTools: tools.length > 0,
2595
- toolCount: tools.length
2596
- });
2597
- const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2593
+ async function executeWithLLM() {
2594
+ logger2.debug("Using default LLM execution", {
2595
+ workerId: id,
2596
+ hasTools: tools.length > 0,
2597
+ toolCount: tools.length
2598
+ });
2599
+ const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2598
2600
  Skills: ${capabilities.skills.join(", ")}
2599
2601
  Tools: ${capabilities.tools.join(", ")}
2600
2602
 
2601
2603
  Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
2602
- const messages = [
2603
- new SystemMessage5(systemPrompt || defaultSystemPrompt),
2604
- new HumanMessage5(currentAssignment.task)
2605
- ];
2606
- let modelToUse = model;
2607
- if (tools.length > 0 && model.bindTools) {
2608
- logger2.debug("Binding tools to model", {
2604
+ const messages = [
2605
+ new SystemMessage5(systemPrompt || defaultSystemPrompt),
2606
+ new HumanMessage5(currentAssignment.task)
2607
+ ];
2608
+ let modelToUse = model;
2609
+ if (tools.length > 0 && model.bindTools) {
2610
+ logger2.debug("Binding tools to model", {
2611
+ workerId: id,
2612
+ toolCount: tools.length,
2613
+ toolNames: tools.map((t) => t.metadata.name)
2614
+ });
2615
+ const langchainTools = toLangChainTools2(tools);
2616
+ modelToUse = model.bindTools(langchainTools);
2617
+ }
2618
+ logger2.debug("Invoking LLM", { workerId: id });
2619
+ const response = await modelToUse.invoke(messages);
2620
+ const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2621
+ logger2.info("Worker task completed", {
2609
2622
  workerId: id,
2610
- toolCount: tools.length,
2611
- toolNames: tools.map((t) => t.metadata.name)
2623
+ assignmentId: currentAssignment.id,
2624
+ resultLength: result.length,
2625
+ resultPreview: result.substring(0, 100)
2612
2626
  });
2613
- const langchainTools = toLangChainTools2(tools);
2614
- modelToUse = model.bindTools(langchainTools);
2615
- }
2616
- logger2.debug("Invoking LLM", { workerId: id });
2617
- const response = await modelToUse.invoke(messages);
2618
- const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2619
- logger2.info("Worker task completed", {
2620
- workerId: id,
2621
- assignmentId: currentAssignment.id,
2622
- resultLength: result.length,
2623
- resultPreview: result.substring(0, 100)
2624
- });
2625
- const taskResult = {
2626
- assignmentId: currentAssignment.id,
2627
- workerId: id,
2628
- success: true,
2629
- result,
2630
- completedAt: Date.now(),
2631
- metadata: {
2632
- skills_used: capabilities.skills
2633
- }
2634
- };
2635
- const message = {
2636
- id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2637
- from: id,
2638
- to: ["supervisor"],
2639
- type: "task_result",
2640
- content: result,
2641
- timestamp: Date.now(),
2642
- metadata: {
2627
+ const taskResult = {
2643
2628
  assignmentId: currentAssignment.id,
2644
- success: true
2645
- }
2629
+ workerId: id,
2630
+ success: true,
2631
+ result,
2632
+ completedAt: Date.now(),
2633
+ metadata: {
2634
+ skills_used: capabilities.skills
2635
+ }
2636
+ };
2637
+ const message = {
2638
+ id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2639
+ from: id,
2640
+ to: ["supervisor"],
2641
+ type: "task_result",
2642
+ content: result,
2643
+ timestamp: Date.now(),
2644
+ metadata: {
2645
+ assignmentId: currentAssignment.id,
2646
+ success: true
2647
+ }
2648
+ };
2649
+ return {
2650
+ completedTasks: [taskResult],
2651
+ messages: [message]
2652
+ };
2653
+ }
2654
+ let executionResult;
2655
+ if (executeFn) {
2656
+ logger2.debug("Using custom execution function", { workerId: id });
2657
+ executionResult = await executeFn(state, runConfig);
2658
+ } else if (agent && isReActAgent(agent)) {
2659
+ logger2.debug("Using ReAct agent", { workerId: id });
2660
+ const wrappedFn = wrapReActAgent(id, agent, verbose);
2661
+ executionResult = await wrappedFn(state, runConfig);
2662
+ } else if (model) {
2663
+ executionResult = await executeWithLLM();
2664
+ } else {
2665
+ logger2.error("Worker missing required configuration", { workerId: id });
2666
+ throw new Error(
2667
+ `Worker ${id} requires either a model, an agent, or a custom execution function. Provide one of: config.model, config.agent, or config.executeFn`
2668
+ );
2669
+ }
2670
+ const currentWorker = state.workers[id];
2671
+ const baseWorkers = {
2672
+ ...state.workers,
2673
+ ...executionResult.workers || {}
2646
2674
  };
2675
+ const workerToUpdate = baseWorkers[id] || currentWorker;
2647
2676
  const updatedWorkers = {
2648
- ...state.workers,
2677
+ ...baseWorkers,
2649
2678
  [id]: {
2650
- ...capabilities,
2651
- currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
2679
+ ...workerToUpdate,
2680
+ currentWorkload: Math.max(0, workerToUpdate.currentWorkload - 1)
2652
2681
  }
2653
2682
  };
2654
- logger2.debug("Worker state update", {
2683
+ logger2.debug("Worker workload decremented", {
2655
2684
  workerId: id,
2656
- newWorkload: updatedWorkers[id].currentWorkload
2685
+ previousWorkload: workerToUpdate.currentWorkload,
2686
+ newWorkload: updatedWorkers[id].currentWorkload,
2687
+ hadExecutionResultWorkers: !!executionResult.workers
2657
2688
  });
2658
2689
  return {
2659
- completedTasks: [taskResult],
2660
- messages: [message],
2690
+ ...executionResult,
2661
2691
  workers: updatedWorkers
2662
2692
  };
2663
2693
  } catch (error) {
@@ -2666,6 +2696,19 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2666
2696
  workerId: id,
2667
2697
  error: errorMessage
2668
2698
  });
2699
+ const currentWorker = state.workers[id];
2700
+ const updatedWorkers = {
2701
+ ...state.workers,
2702
+ [id]: {
2703
+ ...currentWorker,
2704
+ currentWorkload: Math.max(0, currentWorker.currentWorkload - 1)
2705
+ }
2706
+ };
2707
+ logger2.debug("Worker workload decremented (error path)", {
2708
+ workerId: id,
2709
+ previousWorkload: currentWorker.currentWorkload,
2710
+ newWorkload: updatedWorkers[id].currentWorkload
2711
+ });
2669
2712
  const currentAssignment = state.activeAssignments.find(
2670
2713
  (assignment) => assignment.workerId === id
2671
2714
  );
@@ -2685,13 +2728,17 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2685
2728
  return {
2686
2729
  completedTasks: [errorResult],
2687
2730
  currentAgent: "supervisor",
2688
- status: "routing"
2731
+ status: "routing",
2732
+ workers: updatedWorkers
2733
+ // Include workload update
2689
2734
  };
2690
2735
  }
2691
2736
  logger2.error("No assignment found for error handling", { workerId: id });
2692
2737
  return {
2693
2738
  status: "failed",
2694
- error: errorMessage
2739
+ error: errorMessage,
2740
+ workers: updatedWorkers
2741
+ // Include workload update even on failure
2695
2742
  };
2696
2743
  }
2697
2744
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "Agent patterns (ReAct, Planner-Executor) for AgentForge framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",