@agentforge/patterns 0.6.1 → 0.6.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/dist/index.cjs CHANGED
@@ -873,6 +873,9 @@ function createExecutorNode(config) {
873
873
  result = { message: "Step completed without tool execution" };
874
874
  }
875
875
  } catch (execError) {
876
+ if (execError && typeof execError === "object" && "constructor" in execError && execError.constructor.name === "GraphInterrupt") {
877
+ throw execError;
878
+ }
876
879
  success = false;
877
880
  error = execError instanceof Error ? execError.message : "Unknown execution error";
878
881
  result = null;
@@ -1639,26 +1642,35 @@ var RoutingStrategySchema = import_zod7.z.enum([
1639
1642
  ]);
1640
1643
  var RoutingDecisionSchema = import_zod7.z.object({
1641
1644
  /**
1642
- * Target agent to route to
1645
+ * Target agent to route to (single agent routing)
1646
+ * @deprecated Use targetAgents for parallel routing support
1647
+ */
1648
+ targetAgent: import_zod7.z.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
1649
+ /**
1650
+ * Target agents to route to (parallel routing)
1651
+ * When multiple agents are specified, they execute in parallel
1643
1652
  */
1644
- targetAgent: import_zod7.z.string().describe("Agent to route the task to"),
1653
+ targetAgents: import_zod7.z.array(import_zod7.z.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
1645
1654
  /**
1646
1655
  * Reasoning for the routing decision
1647
1656
  */
1648
- reasoning: import_zod7.z.string().optional().describe("Explanation for routing decision"),
1657
+ reasoning: import_zod7.z.string().default("").describe("Explanation for routing decision"),
1649
1658
  /**
1650
1659
  * Confidence in the routing decision (0-1)
1651
1660
  */
1652
- confidence: import_zod7.z.number().min(0).max(1).optional().describe("Confidence score"),
1661
+ confidence: import_zod7.z.number().min(0).max(1).default(0.8).describe("Confidence score"),
1653
1662
  /**
1654
1663
  * Strategy used for routing
1655
1664
  */
1656
- strategy: RoutingStrategySchema.describe("Strategy used for this decision"),
1665
+ strategy: RoutingStrategySchema.default("llm-based").describe("Strategy used for this decision"),
1657
1666
  /**
1658
1667
  * Timestamp of the routing decision
1659
1668
  */
1660
- timestamp: import_zod7.z.number().optional().describe("Timestamp of the decision")
1661
- });
1669
+ timestamp: import_zod7.z.number().default(() => Date.now()).describe("Timestamp of the decision")
1670
+ }).refine(
1671
+ (data) => data.targetAgent || data.targetAgents && data.targetAgents.length > 0,
1672
+ { message: "Either targetAgent or targetAgents must be provided" }
1673
+ );
1662
1674
  var WorkerCapabilitiesSchema = import_zod7.z.object({
1663
1675
  /**
1664
1676
  * Skills/capabilities the agent has
@@ -1926,16 +1938,33 @@ var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible f
1926
1938
  Your job is to:
1927
1939
  1. Analyze the current task and context
1928
1940
  2. Review available worker capabilities
1929
- 3. Select the most appropriate worker for the task
1941
+ 3. Select the most appropriate worker(s) for the task
1930
1942
  4. Provide clear reasoning for your decision
1931
1943
 
1932
- Respond with a JSON object containing:
1944
+ **IMPORTANT: You can route to MULTIPLE workers for parallel execution when:**
1945
+ - The task requires information from multiple domains (e.g., code + documentation)
1946
+ - Multiple workers have complementary expertise
1947
+ - Parallel execution would provide a more comprehensive answer
1948
+
1949
+ **Response Format:**
1950
+
1951
+ For SINGLE worker routing:
1933
1952
  {
1934
1953
  "targetAgent": "worker_id",
1935
1954
  "reasoning": "explanation of why this worker is best suited",
1936
1955
  "confidence": 0.0-1.0,
1937
1956
  "strategy": "llm-based"
1938
- }`;
1957
+ }
1958
+
1959
+ For PARALLEL multi-worker routing:
1960
+ {
1961
+ "targetAgents": ["worker_id_1", "worker_id_2", ...],
1962
+ "reasoning": "explanation of why these workers should work in parallel",
1963
+ "confidence": 0.0-1.0,
1964
+ "strategy": "llm-based"
1965
+ }
1966
+
1967
+ Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
1939
1968
  var llmBasedRouting = {
1940
1969
  name: "llm-based",
1941
1970
  async route(state, config) {
@@ -1958,7 +1987,7 @@ var llmBasedRouting = {
1958
1987
  Available workers:
1959
1988
  ${workerInfo}
1960
1989
 
1961
- Select the best worker for this task and explain your reasoning.`;
1990
+ Select the best worker(s) for this task and explain your reasoning.`;
1962
1991
  const conversationHistory = [];
1963
1992
  let attempt = 0;
1964
1993
  while (attempt < maxRetries) {
@@ -1980,19 +2009,33 @@ Select the best worker for this task and explain your reasoning.`;
1980
2009
  attempt++;
1981
2010
  continue;
1982
2011
  }
1983
- const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1984
- try {
1985
- const decision = JSON.parse(content);
1986
- return {
1987
- targetAgent: decision.targetAgent,
1988
- reasoning: decision.reasoning,
1989
- confidence: decision.confidence,
1990
- strategy: "llm-based",
1991
- timestamp: Date.now()
1992
- };
1993
- } catch (error) {
1994
- throw new Error(`Failed to parse routing decision from LLM: ${error}`);
2012
+ let decision;
2013
+ if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
2014
+ decision = response;
2015
+ } else if (response.content) {
2016
+ if (typeof response.content === "string") {
2017
+ try {
2018
+ decision = JSON.parse(response.content);
2019
+ } catch (error) {
2020
+ throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
2021
+ }
2022
+ } else if (typeof response.content === "object") {
2023
+ decision = response.content;
2024
+ } else {
2025
+ throw new Error(`Unexpected response content type: ${typeof response.content}`);
2026
+ }
2027
+ } else {
2028
+ throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
1995
2029
  }
2030
+ const result = {
2031
+ targetAgent: decision.targetAgent,
2032
+ targetAgents: decision.targetAgents,
2033
+ reasoning: decision.reasoning,
2034
+ confidence: decision.confidence,
2035
+ strategy: "llm-based",
2036
+ timestamp: Date.now()
2037
+ };
2038
+ return result;
1996
2039
  }
1997
2040
  throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
1998
2041
  }
@@ -2008,6 +2051,7 @@ var roundRobinRouting = {
2008
2051
  const targetAgent = availableWorkers[lastRoutingIndex];
2009
2052
  return {
2010
2053
  targetAgent,
2054
+ targetAgents: null,
2011
2055
  reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
2012
2056
  confidence: 1,
2013
2057
  strategy: "round-robin",
@@ -2037,6 +2081,7 @@ var skillBasedRouting = {
2037
2081
  }
2038
2082
  return {
2039
2083
  targetAgent: firstAvailable[0],
2084
+ targetAgents: null,
2040
2085
  reasoning: "No skill matches found, using first available worker",
2041
2086
  confidence: 0.5,
2042
2087
  strategy: "skill-based",
@@ -2047,6 +2092,7 @@ var skillBasedRouting = {
2047
2092
  const confidence = Math.min(best.score / 5, 1);
2048
2093
  return {
2049
2094
  targetAgent: best.id,
2095
+ targetAgents: null,
2050
2096
  reasoning: `Best skill match with score ${best.score} (skills: ${best.skills.join(", ")})`,
2051
2097
  confidence,
2052
2098
  strategy: "skill-based",
@@ -2066,6 +2112,7 @@ var loadBalancedRouting = {
2066
2112
  const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
2067
2113
  return {
2068
2114
  targetAgent: targetWorker.id,
2115
+ targetAgents: null,
2069
2116
  reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
2070
2117
  confidence,
2071
2118
  strategy: "load-balanced",
@@ -2121,10 +2168,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2121
2168
  );
2122
2169
  if (!currentAssignment) {
2123
2170
  logger.debug("No active assignment found", { workerId });
2124
- return {
2125
- currentAgent: "supervisor",
2126
- status: "routing"
2127
- };
2171
+ return {};
2128
2172
  }
2129
2173
  const result = await agent.invoke(
2130
2174
  {
@@ -2156,9 +2200,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2156
2200
  }
2157
2201
  };
2158
2202
  return {
2159
- completedTasks: [taskResult],
2160
- currentAgent: "supervisor",
2161
- status: "routing"
2203
+ completedTasks: [taskResult]
2162
2204
  };
2163
2205
  } catch (error) {
2164
2206
  if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
@@ -2229,7 +2271,7 @@ function createSupervisorNode(config) {
2229
2271
  };
2230
2272
  }
2231
2273
  const allCompleted = state.activeAssignments.every(
2232
- (assignment2) => state.completedTasks.some((task) => task.assignmentId === assignment2.id)
2274
+ (assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2233
2275
  );
2234
2276
  if (allCompleted && state.activeAssignments.length > 0) {
2235
2277
  if (verbose) {
@@ -2242,20 +2284,29 @@ function createSupervisorNode(config) {
2242
2284
  }
2243
2285
  const routingImpl = getRoutingStrategy(strategy);
2244
2286
  const decision = await routingImpl.route(state, config);
2287
+ const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
2288
+ if (targetAgents.length === 0) {
2289
+ throw new Error("Routing decision must specify at least one target agent");
2290
+ }
2245
2291
  if (verbose) {
2246
- console.log(`[Supervisor] Routing to ${decision.targetAgent}: ${decision.reasoning}`);
2292
+ if (targetAgents.length === 1) {
2293
+ console.log(`[Supervisor] Routing to ${targetAgents[0]}: ${decision.reasoning}`);
2294
+ } else {
2295
+ console.log(`[Supervisor] Routing to ${targetAgents.length} agents in parallel [${targetAgents.join(", ")}]: ${decision.reasoning}`);
2296
+ }
2247
2297
  }
2248
- const assignment = {
2298
+ const task = state.messages[state.messages.length - 1]?.content || state.input;
2299
+ const assignments = targetAgents.map((workerId) => ({
2249
2300
  id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
2250
- workerId: decision.targetAgent,
2251
- task: state.messages[state.messages.length - 1]?.content || state.input,
2301
+ workerId,
2302
+ task,
2252
2303
  priority: 5,
2253
2304
  assignedAt: Date.now()
2254
- };
2255
- const message = {
2305
+ }));
2306
+ const messages = assignments.map((assignment) => ({
2256
2307
  id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2257
2308
  from: "supervisor",
2258
- to: [decision.targetAgent],
2309
+ to: [assignment.workerId],
2259
2310
  type: "task_assignment",
2260
2311
  content: assignment.task,
2261
2312
  timestamp: Date.now(),
@@ -2263,13 +2314,15 @@ function createSupervisorNode(config) {
2263
2314
  assignmentId: assignment.id,
2264
2315
  priority: assignment.priority
2265
2316
  }
2266
- };
2317
+ }));
2267
2318
  return {
2268
- currentAgent: decision.targetAgent,
2319
+ currentAgent: targetAgents.join(","),
2320
+ // Store all agents (for backward compat)
2269
2321
  status: "executing",
2270
2322
  routingHistory: [decision],
2271
- activeAssignments: [assignment],
2272
- messages: [message],
2323
+ activeAssignments: assignments,
2324
+ // Multiple assignments for parallel execution!
2325
+ messages,
2273
2326
  iteration: state.iteration + 1
2274
2327
  };
2275
2328
  } catch (error) {
@@ -2304,10 +2357,7 @@ function createWorkerNode(config) {
2304
2357
  if (verbose) {
2305
2358
  console.log(`[Worker:${id}] No active assignment found`);
2306
2359
  }
2307
- return {
2308
- currentAgent: "supervisor",
2309
- status: "routing"
2310
- };
2360
+ return {};
2311
2361
  }
2312
2362
  if (executeFn) {
2313
2363
  if (verbose) {
@@ -2382,9 +2432,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2382
2432
  return {
2383
2433
  completedTasks: [taskResult],
2384
2434
  messages: [message],
2385
- workers: updatedWorkers,
2386
- currentAgent: "supervisor",
2387
- status: "routing"
2435
+ workers: updatedWorkers
2388
2436
  };
2389
2437
  } catch (error) {
2390
2438
  if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
@@ -2497,10 +2545,16 @@ function createMultiAgentSystem(config) {
2497
2545
  } = config;
2498
2546
  const workflow = new import_langgraph4.StateGraph(MultiAgentState);
2499
2547
  let supervisorConfig = { ...supervisor, maxIterations, verbose };
2500
- if (supervisor.model && supervisor.tools && supervisor.tools.length > 0) {
2501
- const langchainTools = (0, import_core9.toLangChainTools)(supervisor.tools);
2502
- const modelWithTools = supervisor.model.bindTools(langchainTools);
2503
- supervisorConfig.model = modelWithTools;
2548
+ if (supervisor.model) {
2549
+ let configuredModel = supervisor.model;
2550
+ if (supervisor.strategy === "llm-based") {
2551
+ configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
2552
+ }
2553
+ if (supervisor.tools && supervisor.tools.length > 0) {
2554
+ const langchainTools = (0, import_core9.toLangChainTools)(supervisor.tools);
2555
+ configuredModel = configuredModel.bindTools(langchainTools);
2556
+ }
2557
+ supervisorConfig.model = configuredModel;
2504
2558
  }
2505
2559
  const supervisorNode = createSupervisorNode(supervisorConfig);
2506
2560
  workflow.addNode("supervisor", supervisorNode);
@@ -2528,6 +2582,10 @@ function createMultiAgentSystem(config) {
2528
2582
  return "aggregator";
2529
2583
  }
2530
2584
  if (state.currentAgent && state.currentAgent !== "supervisor") {
2585
+ if (state.currentAgent.includes(",")) {
2586
+ const agents = state.currentAgent.split(",").map((a) => a.trim());
2587
+ return agents;
2588
+ }
2531
2589
  return state.currentAgent;
2532
2590
  }
2533
2591
  return "supervisor";
package/dist/index.d.cts CHANGED
@@ -1860,40 +1860,70 @@ declare const RoutingStrategySchema: z.ZodEnum<["llm-based", "rule-based", "roun
1860
1860
  type RoutingStrategy = z.infer<typeof RoutingStrategySchema>;
1861
1861
  /**
1862
1862
  * Schema for routing decision
1863
+ *
1864
+ * Supports both single-agent and parallel multi-agent routing:
1865
+ * - Single: Use `targetAgent` field
1866
+ * - Parallel: Use `targetAgents` array field
1867
+ *
1868
+ * If both are provided, `targetAgents` takes precedence.
1869
+ *
1870
+ * Note: Uses .nullable() instead of .optional() for OpenAI structured output compatibility
1863
1871
  */
1864
- declare const RoutingDecisionSchema: z.ZodObject<{
1872
+ declare const RoutingDecisionSchema: z.ZodEffects<z.ZodObject<{
1865
1873
  /**
1866
- * Target agent to route to
1874
+ * Target agent to route to (single agent routing)
1875
+ * @deprecated Use targetAgents for parallel routing support
1867
1876
  */
1868
- targetAgent: z.ZodString;
1877
+ targetAgent: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1878
+ /**
1879
+ * Target agents to route to (parallel routing)
1880
+ * When multiple agents are specified, they execute in parallel
1881
+ */
1882
+ targetAgents: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
1869
1883
  /**
1870
1884
  * Reasoning for the routing decision
1871
1885
  */
1872
- reasoning: z.ZodOptional<z.ZodString>;
1886
+ reasoning: z.ZodDefault<z.ZodString>;
1873
1887
  /**
1874
1888
  * Confidence in the routing decision (0-1)
1875
1889
  */
1876
- confidence: z.ZodOptional<z.ZodNumber>;
1890
+ confidence: z.ZodDefault<z.ZodNumber>;
1877
1891
  /**
1878
1892
  * Strategy used for routing
1879
1893
  */
1880
- strategy: z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>;
1894
+ strategy: z.ZodDefault<z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>>;
1881
1895
  /**
1882
1896
  * Timestamp of the routing decision
1883
1897
  */
1884
- timestamp: z.ZodOptional<z.ZodNumber>;
1898
+ timestamp: z.ZodDefault<z.ZodNumber>;
1885
1899
  }, "strip", z.ZodTypeAny, {
1886
- targetAgent: string;
1900
+ timestamp: number;
1901
+ reasoning: string;
1902
+ confidence: number;
1903
+ targetAgent: string | null;
1904
+ targetAgents: string[] | null;
1887
1905
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
1906
+ }, {
1888
1907
  timestamp?: number | undefined;
1889
1908
  reasoning?: string | undefined;
1890
1909
  confidence?: number | undefined;
1891
- }, {
1892
- targetAgent: string;
1910
+ targetAgent?: string | null | undefined;
1911
+ targetAgents?: string[] | null | undefined;
1912
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
1913
+ }>, {
1914
+ timestamp: number;
1915
+ reasoning: string;
1916
+ confidence: number;
1917
+ targetAgent: string | null;
1918
+ targetAgents: string[] | null;
1893
1919
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
1920
+ }, {
1894
1921
  timestamp?: number | undefined;
1895
1922
  reasoning?: string | undefined;
1896
1923
  confidence?: number | undefined;
1924
+ targetAgent?: string | null | undefined;
1925
+ targetAgents?: string[] | null | undefined;
1926
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
1897
1927
  }>;
1898
1928
  type RoutingDecision = z.infer<typeof RoutingDecisionSchema>;
1899
1929
  /**
@@ -2166,31 +2196,49 @@ declare const MultiAgentStateConfig: {
2166
2196
  * Accumulates all routing decisions
2167
2197
  */
2168
2198
  routingHistory: {
2169
- schema: z.ZodArray<z.ZodObject<{
2170
- targetAgent: z.ZodString;
2171
- reasoning: z.ZodOptional<z.ZodString>;
2172
- confidence: z.ZodOptional<z.ZodNumber>;
2173
- strategy: z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>;
2174
- timestamp: z.ZodOptional<z.ZodNumber>;
2199
+ schema: z.ZodArray<z.ZodEffects<z.ZodObject<{
2200
+ targetAgent: z.ZodDefault<z.ZodNullable<z.ZodString>>;
2201
+ targetAgents: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
2202
+ reasoning: z.ZodDefault<z.ZodString>;
2203
+ confidence: z.ZodDefault<z.ZodNumber>;
2204
+ strategy: z.ZodDefault<z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>>;
2205
+ timestamp: z.ZodDefault<z.ZodNumber>;
2175
2206
  }, "strip", z.ZodTypeAny, {
2176
- targetAgent: string;
2207
+ timestamp: number;
2208
+ reasoning: string;
2209
+ confidence: number;
2210
+ targetAgent: string | null;
2211
+ targetAgents: string[] | null;
2177
2212
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
2213
+ }, {
2178
2214
  timestamp?: number | undefined;
2179
2215
  reasoning?: string | undefined;
2180
2216
  confidence?: number | undefined;
2181
- }, {
2182
- targetAgent: string;
2217
+ targetAgent?: string | null | undefined;
2218
+ targetAgents?: string[] | null | undefined;
2219
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
2220
+ }>, {
2221
+ timestamp: number;
2222
+ reasoning: string;
2223
+ confidence: number;
2224
+ targetAgent: string | null;
2225
+ targetAgents: string[] | null;
2183
2226
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
2227
+ }, {
2184
2228
  timestamp?: number | undefined;
2185
2229
  reasoning?: string | undefined;
2186
2230
  confidence?: number | undefined;
2231
+ targetAgent?: string | null | undefined;
2232
+ targetAgents?: string[] | null | undefined;
2233
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
2187
2234
  }>, "many">;
2188
2235
  reducer: (left: RoutingDecision[], right: RoutingDecision[]) => {
2189
- targetAgent: string;
2236
+ timestamp: number;
2237
+ reasoning: string;
2238
+ confidence: number;
2239
+ targetAgent: string | null;
2240
+ targetAgents: string[] | null;
2190
2241
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
2191
- timestamp?: number | undefined;
2192
- reasoning?: string | undefined;
2193
- confidence?: number | undefined;
2194
2242
  }[];
2195
2243
  default: () => never[];
2196
2244
  description: string;
@@ -2606,7 +2654,7 @@ interface RoutingStrategyImpl {
2606
2654
  /**
2607
2655
  * Default system prompt for LLM-based routing
2608
2656
  */
2609
- declare const DEFAULT_SUPERVISOR_SYSTEM_PROMPT = "You are a supervisor agent responsible for routing tasks to specialized worker agents.\n\nYour job is to:\n1. Analyze the current task and context\n2. Review available worker capabilities\n3. Select the most appropriate worker for the task\n4. Provide clear reasoning for your decision\n\nRespond with a JSON object containing:\n{\n \"targetAgent\": \"worker_id\",\n \"reasoning\": \"explanation of why this worker is best suited\",\n \"confidence\": 0.0-1.0,\n \"strategy\": \"llm-based\"\n}";
2657
+ declare const DEFAULT_SUPERVISOR_SYSTEM_PROMPT = "You are a supervisor agent responsible for routing tasks to specialized worker agents.\n\nYour job is to:\n1. Analyze the current task and context\n2. Review available worker capabilities\n3. Select the most appropriate worker(s) for the task\n4. Provide clear reasoning for your decision\n\n**IMPORTANT: You can route to MULTIPLE workers for parallel execution when:**\n- The task requires information from multiple domains (e.g., code + documentation)\n- Multiple workers have complementary expertise\n- Parallel execution would provide a more comprehensive answer\n\n**Response Format:**\n\nFor SINGLE worker routing:\n{\n \"targetAgent\": \"worker_id\",\n \"reasoning\": \"explanation of why this worker is best suited\",\n \"confidence\": 0.0-1.0,\n \"strategy\": \"llm-based\"\n}\n\nFor PARALLEL multi-worker routing:\n{\n \"targetAgents\": [\"worker_id_1\", \"worker_id_2\", ...],\n \"reasoning\": \"explanation of why these workers should work in parallel\",\n \"confidence\": 0.0-1.0,\n \"strategy\": \"llm-based\"\n}\n\nChoose parallel routing when the task benefits from multiple perspectives or data sources.";
2610
2658
  /**
2611
2659
  * LLM-based routing strategy
2612
2660
  * Uses an LLM to intelligently route tasks based on worker capabilities
package/dist/index.d.ts CHANGED
@@ -1860,40 +1860,70 @@ declare const RoutingStrategySchema: z.ZodEnum<["llm-based", "rule-based", "roun
1860
1860
  type RoutingStrategy = z.infer<typeof RoutingStrategySchema>;
1861
1861
  /**
1862
1862
  * Schema for routing decision
1863
+ *
1864
+ * Supports both single-agent and parallel multi-agent routing:
1865
+ * - Single: Use `targetAgent` field
1866
+ * - Parallel: Use `targetAgents` array field
1867
+ *
1868
+ * If both are provided, `targetAgents` takes precedence.
1869
+ *
1870
+ * Note: Uses .nullable() instead of .optional() for OpenAI structured output compatibility
1863
1871
  */
1864
- declare const RoutingDecisionSchema: z.ZodObject<{
1872
+ declare const RoutingDecisionSchema: z.ZodEffects<z.ZodObject<{
1865
1873
  /**
1866
- * Target agent to route to
1874
+ * Target agent to route to (single agent routing)
1875
+ * @deprecated Use targetAgents for parallel routing support
1867
1876
  */
1868
- targetAgent: z.ZodString;
1877
+ targetAgent: z.ZodDefault<z.ZodNullable<z.ZodString>>;
1878
+ /**
1879
+ * Target agents to route to (parallel routing)
1880
+ * When multiple agents are specified, they execute in parallel
1881
+ */
1882
+ targetAgents: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
1869
1883
  /**
1870
1884
  * Reasoning for the routing decision
1871
1885
  */
1872
- reasoning: z.ZodOptional<z.ZodString>;
1886
+ reasoning: z.ZodDefault<z.ZodString>;
1873
1887
  /**
1874
1888
  * Confidence in the routing decision (0-1)
1875
1889
  */
1876
- confidence: z.ZodOptional<z.ZodNumber>;
1890
+ confidence: z.ZodDefault<z.ZodNumber>;
1877
1891
  /**
1878
1892
  * Strategy used for routing
1879
1893
  */
1880
- strategy: z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>;
1894
+ strategy: z.ZodDefault<z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>>;
1881
1895
  /**
1882
1896
  * Timestamp of the routing decision
1883
1897
  */
1884
- timestamp: z.ZodOptional<z.ZodNumber>;
1898
+ timestamp: z.ZodDefault<z.ZodNumber>;
1885
1899
  }, "strip", z.ZodTypeAny, {
1886
- targetAgent: string;
1900
+ timestamp: number;
1901
+ reasoning: string;
1902
+ confidence: number;
1903
+ targetAgent: string | null;
1904
+ targetAgents: string[] | null;
1887
1905
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
1906
+ }, {
1888
1907
  timestamp?: number | undefined;
1889
1908
  reasoning?: string | undefined;
1890
1909
  confidence?: number | undefined;
1891
- }, {
1892
- targetAgent: string;
1910
+ targetAgent?: string | null | undefined;
1911
+ targetAgents?: string[] | null | undefined;
1912
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
1913
+ }>, {
1914
+ timestamp: number;
1915
+ reasoning: string;
1916
+ confidence: number;
1917
+ targetAgent: string | null;
1918
+ targetAgents: string[] | null;
1893
1919
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
1920
+ }, {
1894
1921
  timestamp?: number | undefined;
1895
1922
  reasoning?: string | undefined;
1896
1923
  confidence?: number | undefined;
1924
+ targetAgent?: string | null | undefined;
1925
+ targetAgents?: string[] | null | undefined;
1926
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
1897
1927
  }>;
1898
1928
  type RoutingDecision = z.infer<typeof RoutingDecisionSchema>;
1899
1929
  /**
@@ -2166,31 +2196,49 @@ declare const MultiAgentStateConfig: {
2166
2196
  * Accumulates all routing decisions
2167
2197
  */
2168
2198
  routingHistory: {
2169
- schema: z.ZodArray<z.ZodObject<{
2170
- targetAgent: z.ZodString;
2171
- reasoning: z.ZodOptional<z.ZodString>;
2172
- confidence: z.ZodOptional<z.ZodNumber>;
2173
- strategy: z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>;
2174
- timestamp: z.ZodOptional<z.ZodNumber>;
2199
+ schema: z.ZodArray<z.ZodEffects<z.ZodObject<{
2200
+ targetAgent: z.ZodDefault<z.ZodNullable<z.ZodString>>;
2201
+ targetAgents: z.ZodDefault<z.ZodNullable<z.ZodArray<z.ZodString, "many">>>;
2202
+ reasoning: z.ZodDefault<z.ZodString>;
2203
+ confidence: z.ZodDefault<z.ZodNumber>;
2204
+ strategy: z.ZodDefault<z.ZodEnum<["llm-based", "rule-based", "round-robin", "skill-based", "load-balanced"]>>;
2205
+ timestamp: z.ZodDefault<z.ZodNumber>;
2175
2206
  }, "strip", z.ZodTypeAny, {
2176
- targetAgent: string;
2207
+ timestamp: number;
2208
+ reasoning: string;
2209
+ confidence: number;
2210
+ targetAgent: string | null;
2211
+ targetAgents: string[] | null;
2177
2212
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
2213
+ }, {
2178
2214
  timestamp?: number | undefined;
2179
2215
  reasoning?: string | undefined;
2180
2216
  confidence?: number | undefined;
2181
- }, {
2182
- targetAgent: string;
2217
+ targetAgent?: string | null | undefined;
2218
+ targetAgents?: string[] | null | undefined;
2219
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
2220
+ }>, {
2221
+ timestamp: number;
2222
+ reasoning: string;
2223
+ confidence: number;
2224
+ targetAgent: string | null;
2225
+ targetAgents: string[] | null;
2183
2226
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
2227
+ }, {
2184
2228
  timestamp?: number | undefined;
2185
2229
  reasoning?: string | undefined;
2186
2230
  confidence?: number | undefined;
2231
+ targetAgent?: string | null | undefined;
2232
+ targetAgents?: string[] | null | undefined;
2233
+ strategy?: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced" | undefined;
2187
2234
  }>, "many">;
2188
2235
  reducer: (left: RoutingDecision[], right: RoutingDecision[]) => {
2189
- targetAgent: string;
2236
+ timestamp: number;
2237
+ reasoning: string;
2238
+ confidence: number;
2239
+ targetAgent: string | null;
2240
+ targetAgents: string[] | null;
2190
2241
  strategy: "llm-based" | "rule-based" | "round-robin" | "skill-based" | "load-balanced";
2191
- timestamp?: number | undefined;
2192
- reasoning?: string | undefined;
2193
- confidence?: number | undefined;
2194
2242
  }[];
2195
2243
  default: () => never[];
2196
2244
  description: string;
@@ -2606,7 +2654,7 @@ interface RoutingStrategyImpl {
2606
2654
  /**
2607
2655
  * Default system prompt for LLM-based routing
2608
2656
  */
2609
- declare const DEFAULT_SUPERVISOR_SYSTEM_PROMPT = "You are a supervisor agent responsible for routing tasks to specialized worker agents.\n\nYour job is to:\n1. Analyze the current task and context\n2. Review available worker capabilities\n3. Select the most appropriate worker for the task\n4. Provide clear reasoning for your decision\n\nRespond with a JSON object containing:\n{\n \"targetAgent\": \"worker_id\",\n \"reasoning\": \"explanation of why this worker is best suited\",\n \"confidence\": 0.0-1.0,\n \"strategy\": \"llm-based\"\n}";
2657
+ declare const DEFAULT_SUPERVISOR_SYSTEM_PROMPT = "You are a supervisor agent responsible for routing tasks to specialized worker agents.\n\nYour job is to:\n1. Analyze the current task and context\n2. Review available worker capabilities\n3. Select the most appropriate worker(s) for the task\n4. Provide clear reasoning for your decision\n\n**IMPORTANT: You can route to MULTIPLE workers for parallel execution when:**\n- The task requires information from multiple domains (e.g., code + documentation)\n- Multiple workers have complementary expertise\n- Parallel execution would provide a more comprehensive answer\n\n**Response Format:**\n\nFor SINGLE worker routing:\n{\n \"targetAgent\": \"worker_id\",\n \"reasoning\": \"explanation of why this worker is best suited\",\n \"confidence\": 0.0-1.0,\n \"strategy\": \"llm-based\"\n}\n\nFor PARALLEL multi-worker routing:\n{\n \"targetAgents\": [\"worker_id_1\", \"worker_id_2\", ...],\n \"reasoning\": \"explanation of why these workers should work in parallel\",\n \"confidence\": 0.0-1.0,\n \"strategy\": \"llm-based\"\n}\n\nChoose parallel routing when the task benefits from multiple perspectives or data sources.";
2610
2658
  /**
2611
2659
  * LLM-based routing strategy
2612
2660
  * Uses an LLM to intelligently route tasks based on worker capabilities
package/dist/index.js CHANGED
@@ -774,6 +774,9 @@ function createExecutorNode(config) {
774
774
  result = { message: "Step completed without tool execution" };
775
775
  }
776
776
  } catch (execError) {
777
+ if (execError && typeof execError === "object" && "constructor" in execError && execError.constructor.name === "GraphInterrupt") {
778
+ throw execError;
779
+ }
777
780
  success = false;
778
781
  error = execError instanceof Error ? execError.message : "Unknown execution error";
779
782
  result = null;
@@ -1540,26 +1543,35 @@ var RoutingStrategySchema = z7.enum([
1540
1543
  ]);
1541
1544
  var RoutingDecisionSchema = z7.object({
1542
1545
  /**
1543
- * Target agent to route to
1546
+ * Target agent to route to (single agent routing)
1547
+ * @deprecated Use targetAgents for parallel routing support
1548
+ */
1549
+ targetAgent: z7.string().nullable().default(null).describe("Agent to route the task to (single routing)"),
1550
+ /**
1551
+ * Target agents to route to (parallel routing)
1552
+ * When multiple agents are specified, they execute in parallel
1544
1553
  */
1545
- targetAgent: z7.string().describe("Agent to route the task to"),
1554
+ targetAgents: z7.array(z7.string()).nullable().default(null).describe("Agents to route the task to (parallel routing)"),
1546
1555
  /**
1547
1556
  * Reasoning for the routing decision
1548
1557
  */
1549
- reasoning: z7.string().optional().describe("Explanation for routing decision"),
1558
+ reasoning: z7.string().default("").describe("Explanation for routing decision"),
1550
1559
  /**
1551
1560
  * Confidence in the routing decision (0-1)
1552
1561
  */
1553
- confidence: z7.number().min(0).max(1).optional().describe("Confidence score"),
1562
+ confidence: z7.number().min(0).max(1).default(0.8).describe("Confidence score"),
1554
1563
  /**
1555
1564
  * Strategy used for routing
1556
1565
  */
1557
- strategy: RoutingStrategySchema.describe("Strategy used for this decision"),
1566
+ strategy: RoutingStrategySchema.default("llm-based").describe("Strategy used for this decision"),
1558
1567
  /**
1559
1568
  * Timestamp of the routing decision
1560
1569
  */
1561
- timestamp: z7.number().optional().describe("Timestamp of the decision")
1562
- });
1570
+ timestamp: z7.number().default(() => Date.now()).describe("Timestamp of the decision")
1571
+ }).refine(
1572
+ (data) => data.targetAgent || data.targetAgents && data.targetAgents.length > 0,
1573
+ { message: "Either targetAgent or targetAgents must be provided" }
1574
+ );
1563
1575
  var WorkerCapabilitiesSchema = z7.object({
1564
1576
  /**
1565
1577
  * Skills/capabilities the agent has
@@ -1827,16 +1839,33 @@ var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible f
1827
1839
  Your job is to:
1828
1840
  1. Analyze the current task and context
1829
1841
  2. Review available worker capabilities
1830
- 3. Select the most appropriate worker for the task
1842
+ 3. Select the most appropriate worker(s) for the task
1831
1843
  4. Provide clear reasoning for your decision
1832
1844
 
1833
- Respond with a JSON object containing:
1845
+ **IMPORTANT: You can route to MULTIPLE workers for parallel execution when:**
1846
+ - The task requires information from multiple domains (e.g., code + documentation)
1847
+ - Multiple workers have complementary expertise
1848
+ - Parallel execution would provide a more comprehensive answer
1849
+
1850
+ **Response Format:**
1851
+
1852
+ For SINGLE worker routing:
1834
1853
  {
1835
1854
  "targetAgent": "worker_id",
1836
1855
  "reasoning": "explanation of why this worker is best suited",
1837
1856
  "confidence": 0.0-1.0,
1838
1857
  "strategy": "llm-based"
1839
- }`;
1858
+ }
1859
+
1860
+ For PARALLEL multi-worker routing:
1861
+ {
1862
+ "targetAgents": ["worker_id_1", "worker_id_2", ...],
1863
+ "reasoning": "explanation of why these workers should work in parallel",
1864
+ "confidence": 0.0-1.0,
1865
+ "strategy": "llm-based"
1866
+ }
1867
+
1868
+ Choose parallel routing when the task benefits from multiple perspectives or data sources.`;
1840
1869
  var llmBasedRouting = {
1841
1870
  name: "llm-based",
1842
1871
  async route(state, config) {
@@ -1859,7 +1888,7 @@ var llmBasedRouting = {
1859
1888
  Available workers:
1860
1889
  ${workerInfo}
1861
1890
 
1862
- Select the best worker for this task and explain your reasoning.`;
1891
+ Select the best worker(s) for this task and explain your reasoning.`;
1863
1892
  const conversationHistory = [];
1864
1893
  let attempt = 0;
1865
1894
  while (attempt < maxRetries) {
@@ -1881,19 +1910,33 @@ Select the best worker for this task and explain your reasoning.`;
1881
1910
  attempt++;
1882
1911
  continue;
1883
1912
  }
1884
- const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1885
- try {
1886
- const decision = JSON.parse(content);
1887
- return {
1888
- targetAgent: decision.targetAgent,
1889
- reasoning: decision.reasoning,
1890
- confidence: decision.confidence,
1891
- strategy: "llm-based",
1892
- timestamp: Date.now()
1893
- };
1894
- } catch (error) {
1895
- throw new Error(`Failed to parse routing decision from LLM: ${error}`);
1913
+ let decision;
1914
+ if (response && typeof response === "object" && ("targetAgent" in response || "targetAgents" in response)) {
1915
+ decision = response;
1916
+ } else if (response.content) {
1917
+ if (typeof response.content === "string") {
1918
+ try {
1919
+ decision = JSON.parse(response.content);
1920
+ } catch (error) {
1921
+ throw new Error(`Failed to parse routing decision from LLM. Expected JSON but got: ${response.content}`);
1922
+ }
1923
+ } else if (typeof response.content === "object") {
1924
+ decision = response.content;
1925
+ } else {
1926
+ throw new Error(`Unexpected response content type: ${typeof response.content}`);
1927
+ }
1928
+ } else {
1929
+ throw new Error(`Unexpected response format: ${JSON.stringify(response)}`);
1896
1930
  }
1931
+ const result = {
1932
+ targetAgent: decision.targetAgent,
1933
+ targetAgents: decision.targetAgents,
1934
+ reasoning: decision.reasoning,
1935
+ confidence: decision.confidence,
1936
+ strategy: "llm-based",
1937
+ timestamp: Date.now()
1938
+ };
1939
+ return result;
1897
1940
  }
1898
1941
  throw new Error(`Max tool retries (${maxRetries}) exceeded without routing decision`);
1899
1942
  }
@@ -1909,6 +1952,7 @@ var roundRobinRouting = {
1909
1952
  const targetAgent = availableWorkers[lastRoutingIndex];
1910
1953
  return {
1911
1954
  targetAgent,
1955
+ targetAgents: null,
1912
1956
  reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
1913
1957
  confidence: 1,
1914
1958
  strategy: "round-robin",
@@ -1938,6 +1982,7 @@ var skillBasedRouting = {
1938
1982
  }
1939
1983
  return {
1940
1984
  targetAgent: firstAvailable[0],
1985
+ targetAgents: null,
1941
1986
  reasoning: "No skill matches found, using first available worker",
1942
1987
  confidence: 0.5,
1943
1988
  strategy: "skill-based",
@@ -1948,6 +1993,7 @@ var skillBasedRouting = {
1948
1993
  const confidence = Math.min(best.score / 5, 1);
1949
1994
  return {
1950
1995
  targetAgent: best.id,
1996
+ targetAgents: null,
1951
1997
  reasoning: `Best skill match with score ${best.score} (skills: ${best.skills.join(", ")})`,
1952
1998
  confidence,
1953
1999
  strategy: "skill-based",
@@ -1967,6 +2013,7 @@ var loadBalancedRouting = {
1967
2013
  const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
1968
2014
  return {
1969
2015
  targetAgent: targetWorker.id,
2016
+ targetAgents: null,
1970
2017
  reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
1971
2018
  confidence,
1972
2019
  strategy: "load-balanced",
@@ -2022,10 +2069,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2022
2069
  );
2023
2070
  if (!currentAssignment) {
2024
2071
  logger.debug("No active assignment found", { workerId });
2025
- return {
2026
- currentAgent: "supervisor",
2027
- status: "routing"
2028
- };
2072
+ return {};
2029
2073
  }
2030
2074
  const result = await agent.invoke(
2031
2075
  {
@@ -2057,9 +2101,7 @@ function wrapReActAgent(workerId, agent, verbose = false) {
2057
2101
  }
2058
2102
  };
2059
2103
  return {
2060
- completedTasks: [taskResult],
2061
- currentAgent: "supervisor",
2062
- status: "routing"
2104
+ completedTasks: [taskResult]
2063
2105
  };
2064
2106
  } catch (error) {
2065
2107
  if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
@@ -2130,7 +2172,7 @@ function createSupervisorNode(config) {
2130
2172
  };
2131
2173
  }
2132
2174
  const allCompleted = state.activeAssignments.every(
2133
- (assignment2) => state.completedTasks.some((task) => task.assignmentId === assignment2.id)
2175
+ (assignment) => state.completedTasks.some((task2) => task2.assignmentId === assignment.id)
2134
2176
  );
2135
2177
  if (allCompleted && state.activeAssignments.length > 0) {
2136
2178
  if (verbose) {
@@ -2143,20 +2185,29 @@ function createSupervisorNode(config) {
2143
2185
  }
2144
2186
  const routingImpl = getRoutingStrategy(strategy);
2145
2187
  const decision = await routingImpl.route(state, config);
2188
+ const targetAgents = decision.targetAgents && decision.targetAgents.length > 0 ? decision.targetAgents : decision.targetAgent ? [decision.targetAgent] : [];
2189
+ if (targetAgents.length === 0) {
2190
+ throw new Error("Routing decision must specify at least one target agent");
2191
+ }
2146
2192
  if (verbose) {
2147
- console.log(`[Supervisor] Routing to ${decision.targetAgent}: ${decision.reasoning}`);
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
+ }
2148
2198
  }
2149
- const assignment = {
2199
+ const task = state.messages[state.messages.length - 1]?.content || state.input;
2200
+ const assignments = targetAgents.map((workerId) => ({
2150
2201
  id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
2151
- workerId: decision.targetAgent,
2152
- task: state.messages[state.messages.length - 1]?.content || state.input,
2202
+ workerId,
2203
+ task,
2153
2204
  priority: 5,
2154
2205
  assignedAt: Date.now()
2155
- };
2156
- const message = {
2206
+ }));
2207
+ const messages = assignments.map((assignment) => ({
2157
2208
  id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2158
2209
  from: "supervisor",
2159
- to: [decision.targetAgent],
2210
+ to: [assignment.workerId],
2160
2211
  type: "task_assignment",
2161
2212
  content: assignment.task,
2162
2213
  timestamp: Date.now(),
@@ -2164,13 +2215,15 @@ function createSupervisorNode(config) {
2164
2215
  assignmentId: assignment.id,
2165
2216
  priority: assignment.priority
2166
2217
  }
2167
- };
2218
+ }));
2168
2219
  return {
2169
- currentAgent: decision.targetAgent,
2220
+ currentAgent: targetAgents.join(","),
2221
+ // Store all agents (for backward compat)
2170
2222
  status: "executing",
2171
2223
  routingHistory: [decision],
2172
- activeAssignments: [assignment],
2173
- messages: [message],
2224
+ activeAssignments: assignments,
2225
+ // Multiple assignments for parallel execution!
2226
+ messages,
2174
2227
  iteration: state.iteration + 1
2175
2228
  };
2176
2229
  } catch (error) {
@@ -2205,10 +2258,7 @@ function createWorkerNode(config) {
2205
2258
  if (verbose) {
2206
2259
  console.log(`[Worker:${id}] No active assignment found`);
2207
2260
  }
2208
- return {
2209
- currentAgent: "supervisor",
2210
- status: "routing"
2211
- };
2261
+ return {};
2212
2262
  }
2213
2263
  if (executeFn) {
2214
2264
  if (verbose) {
@@ -2283,9 +2333,7 @@ Execute the assigned task using your skills and tools. Provide a clear, actionab
2283
2333
  return {
2284
2334
  completedTasks: [taskResult],
2285
2335
  messages: [message],
2286
- workers: updatedWorkers,
2287
- currentAgent: "supervisor",
2288
- status: "routing"
2336
+ workers: updatedWorkers
2289
2337
  };
2290
2338
  } catch (error) {
2291
2339
  if (error && typeof error === "object" && "constructor" in error && error.constructor.name === "GraphInterrupt") {
@@ -2398,10 +2446,16 @@ function createMultiAgentSystem(config) {
2398
2446
  } = config;
2399
2447
  const workflow = new StateGraph4(MultiAgentState);
2400
2448
  let supervisorConfig = { ...supervisor, maxIterations, verbose };
2401
- if (supervisor.model && supervisor.tools && supervisor.tools.length > 0) {
2402
- const langchainTools = toLangChainTools3(supervisor.tools);
2403
- const modelWithTools = supervisor.model.bindTools(langchainTools);
2404
- supervisorConfig.model = modelWithTools;
2449
+ if (supervisor.model) {
2450
+ let configuredModel = supervisor.model;
2451
+ if (supervisor.strategy === "llm-based") {
2452
+ configuredModel = configuredModel.withStructuredOutput(RoutingDecisionSchema);
2453
+ }
2454
+ if (supervisor.tools && supervisor.tools.length > 0) {
2455
+ const langchainTools = toLangChainTools3(supervisor.tools);
2456
+ configuredModel = configuredModel.bindTools(langchainTools);
2457
+ }
2458
+ supervisorConfig.model = configuredModel;
2405
2459
  }
2406
2460
  const supervisorNode = createSupervisorNode(supervisorConfig);
2407
2461
  workflow.addNode("supervisor", supervisorNode);
@@ -2429,6 +2483,10 @@ function createMultiAgentSystem(config) {
2429
2483
  return "aggregator";
2430
2484
  }
2431
2485
  if (state.currentAgent && state.currentAgent !== "supervisor") {
2486
+ if (state.currentAgent.includes(",")) {
2487
+ const agents = state.currentAgent.split(",").map((a) => a.trim());
2488
+ return agents;
2489
+ }
2432
2490
  return state.currentAgent;
2433
2491
  }
2434
2492
  return "supervisor";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentforge/patterns",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "Agent patterns (ReAct, Planner-Executor) for AgentForge framework",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",