@agentforge/patterns 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2464 @@
1
+ // src/react/schemas.ts
2
+ import { z } from "zod";
3
+ var MessageRoleSchema = z.enum(["system", "user", "assistant", "tool"]);
4
+ var MessageSchema = z.object({
5
+ role: MessageRoleSchema,
6
+ content: z.string(),
7
+ name: z.string().optional(),
8
+ metadata: z.record(z.any()).optional()
9
+ });
10
+ var ThoughtSchema = z.object({
11
+ content: z.string(),
12
+ timestamp: z.number().optional(),
13
+ metadata: z.record(z.any()).optional()
14
+ });
15
+ var ToolCallSchema = z.object({
16
+ id: z.string(),
17
+ name: z.string(),
18
+ arguments: z.record(z.any()),
19
+ timestamp: z.number().optional()
20
+ });
21
+ var ToolResultSchema = z.object({
22
+ toolCallId: z.string(),
23
+ result: z.any(),
24
+ error: z.string().optional(),
25
+ timestamp: z.number().optional()
26
+ });
27
+ var ScratchpadEntrySchema = z.object({
28
+ step: z.number(),
29
+ thought: z.string().optional(),
30
+ action: z.string().optional(),
31
+ observation: z.string().optional(),
32
+ timestamp: z.number().optional()
33
+ });
34
+
35
+ // src/react/state.ts
36
+ import { z as z2 } from "zod";
37
+ import { createStateAnnotation } from "@agentforge/core";
38
+ var ReActStateConfig = {
39
+ /**
40
+ * Conversation messages
41
+ * Accumulates all messages in the conversation
42
+ */
43
+ messages: {
44
+ schema: z2.array(MessageSchema),
45
+ reducer: (left, right) => [...left, ...right],
46
+ default: () => [],
47
+ description: "Conversation message history"
48
+ },
49
+ /**
50
+ * Reasoning thoughts
51
+ * Accumulates all reasoning steps the agent takes
52
+ */
53
+ thoughts: {
54
+ schema: z2.array(ThoughtSchema),
55
+ reducer: (left, right) => [...left, ...right],
56
+ default: () => [],
57
+ description: "Agent reasoning steps"
58
+ },
59
+ /**
60
+ * Tool calls (actions)
61
+ * Accumulates all tool calls made by the agent
62
+ */
63
+ actions: {
64
+ schema: z2.array(ToolCallSchema),
65
+ reducer: (left, right) => [...left, ...right],
66
+ default: () => [],
67
+ description: "Tool calls made by the agent"
68
+ },
69
+ /**
70
+ * Tool results (observations)
71
+ * Accumulates all observations from tool executions
72
+ */
73
+ observations: {
74
+ schema: z2.array(ToolResultSchema),
75
+ reducer: (left, right) => [...left, ...right],
76
+ default: () => [],
77
+ description: "Results from tool executions"
78
+ },
79
+ /**
80
+ * Scratchpad for intermediate reasoning
81
+ * Accumulates step-by-step reasoning process
82
+ */
83
+ scratchpad: {
84
+ schema: z2.array(ScratchpadEntrySchema),
85
+ reducer: (left, right) => [...left, ...right],
86
+ default: () => [],
87
+ description: "Intermediate reasoning scratchpad"
88
+ },
89
+ /**
90
+ * Current iteration count
91
+ * Tracks how many thought-action-observation loops have been executed
92
+ */
93
+ iteration: {
94
+ schema: z2.number(),
95
+ reducer: (left, right) => left + right,
96
+ default: () => 0,
97
+ description: "Current iteration count"
98
+ },
99
+ /**
100
+ * Whether the agent should continue iterating
101
+ */
102
+ shouldContinue: {
103
+ schema: z2.boolean().optional(),
104
+ default: () => true,
105
+ description: "Whether to continue the ReAct loop"
106
+ },
107
+ /**
108
+ * Final response (if any)
109
+ */
110
+ response: {
111
+ schema: z2.string().optional(),
112
+ description: "Final response from the agent"
113
+ }
114
+ };
115
+ var ReActState = createStateAnnotation(ReActStateConfig);
116
+
117
+ // src/react/prompts.ts
118
+ var DEFAULT_REACT_SYSTEM_PROMPT = `You are a helpful assistant that uses tools to solve problems.
119
+
120
+ You follow the ReAct (Reasoning and Action) pattern:
121
+ 1. THINK: Reason about what to do next
122
+ 2. ACT: Use a tool or provide a final answer
123
+ 3. OBSERVE: Examine the tool's result
124
+ 4. REPEAT: Continue until you can answer the user's question
125
+
126
+ When you need to use a tool:
127
+ - Think carefully about which tool to use
128
+ - Provide the correct arguments
129
+ - Wait for the observation before proceeding
130
+
131
+ When you have enough information:
132
+ - Provide a clear, complete answer
133
+ - Cite the tools you used if relevant`;
134
+ function formatScratchpad(scratchpad) {
135
+ if (scratchpad.length === 0) {
136
+ return "No previous steps.";
137
+ }
138
+ return scratchpad.map((entry) => {
139
+ const parts = [`Step ${entry.step}:`];
140
+ if (entry.thought) parts.push(` Thought: ${entry.thought}`);
141
+ if (entry.action) parts.push(` Action: ${entry.action}`);
142
+ if (entry.observation) parts.push(` Observation: ${entry.observation}`);
143
+ return parts.join("\n");
144
+ }).join("\n\n");
145
+ }
146
+
147
+ // src/react/nodes.ts
148
+ import { HumanMessage, AIMessage, SystemMessage } from "@langchain/core/messages";
149
+ import { toLangChainTools } from "@agentforge/core";
150
+ function createReasoningNode(llm, tools, systemPrompt, maxIterations, verbose = false) {
151
+ const langchainTools = toLangChainTools(tools);
152
+ const llmWithTools = llm.bindTools ? llm.bindTools(langchainTools) : llm;
153
+ return async (state) => {
154
+ if (verbose) {
155
+ console.log(`[reasoning] Iteration ${state.iteration + 1}/${maxIterations}`);
156
+ }
157
+ const stateMessages = state.messages || [];
158
+ const messages = [
159
+ new SystemMessage(systemPrompt),
160
+ ...stateMessages.map((msg) => {
161
+ if (msg.role === "user") return new HumanMessage(msg.content);
162
+ if (msg.role === "assistant") return new AIMessage(msg.content);
163
+ if (msg.role === "system") return new SystemMessage(msg.content);
164
+ return new HumanMessage(msg.content);
165
+ })
166
+ ];
167
+ const scratchpad = state.scratchpad || [];
168
+ if (scratchpad.length > 0) {
169
+ const scratchpadText = formatScratchpad(scratchpad);
170
+ messages.push(new SystemMessage(`Previous steps:
171
+ ${scratchpadText}`));
172
+ }
173
+ const response = await llmWithTools.invoke(messages);
174
+ const thought = typeof response.content === "string" ? response.content : "";
175
+ const toolCalls = [];
176
+ if (response.tool_calls && response.tool_calls.length > 0) {
177
+ for (const toolCall of response.tool_calls) {
178
+ toolCalls.push({
179
+ id: toolCall.id || `call_${Date.now()}_${Math.random()}`,
180
+ name: toolCall.name,
181
+ arguments: toolCall.args || {},
182
+ timestamp: Date.now()
183
+ });
184
+ }
185
+ }
186
+ const currentIteration = state.iteration || 0;
187
+ const shouldContinue = toolCalls.length > 0 && currentIteration + 1 < maxIterations;
188
+ return {
189
+ messages: [{ role: "assistant", content: thought }],
190
+ thoughts: thought ? [{ content: thought, timestamp: Date.now() }] : [],
191
+ actions: toolCalls,
192
+ iteration: 1,
193
+ // Increment iteration
194
+ shouldContinue,
195
+ response: toolCalls.length === 0 ? thought : void 0
196
+ // Final response if no tool calls
197
+ };
198
+ };
199
+ }
200
+ function createActionNode(tools, verbose = false) {
201
+ const toolMap = new Map(tools.map((tool) => [tool.metadata.name, tool]));
202
+ return async (state) => {
203
+ const actions = state.actions || [];
204
+ if (verbose) {
205
+ console.log(`[action] Executing ${actions.length} tool calls`);
206
+ }
207
+ const recentActions = actions.slice(-10);
208
+ const observations = [];
209
+ for (const action of recentActions) {
210
+ const tool = toolMap.get(action.name);
211
+ if (!tool) {
212
+ observations.push({
213
+ toolCallId: action.id,
214
+ result: null,
215
+ error: `Tool '${action.name}' not found`,
216
+ timestamp: Date.now()
217
+ });
218
+ continue;
219
+ }
220
+ try {
221
+ const result = await tool.execute(action.arguments);
222
+ observations.push({
223
+ toolCallId: action.id,
224
+ result,
225
+ timestamp: Date.now()
226
+ });
227
+ if (verbose) {
228
+ console.log(`[action] Tool '${action.name}' executed successfully`);
229
+ }
230
+ } catch (error) {
231
+ const errorMessage = error instanceof Error ? error.message : String(error);
232
+ observations.push({
233
+ toolCallId: action.id,
234
+ result: null,
235
+ error: errorMessage,
236
+ timestamp: Date.now()
237
+ });
238
+ if (verbose) {
239
+ console.error(`[action] Tool '${action.name}' failed:`, errorMessage);
240
+ }
241
+ }
242
+ }
243
+ return {
244
+ observations
245
+ };
246
+ };
247
+ }
248
+ function createObservationNode(verbose = false) {
249
+ return async (state) => {
250
+ const observations = state.observations || [];
251
+ const thoughts = state.thoughts || [];
252
+ const actions = state.actions || [];
253
+ if (verbose) {
254
+ console.log(`[observation] Processing ${observations.length} observations`);
255
+ }
256
+ const recentObservations = observations.slice(-10);
257
+ const currentStep = state.iteration;
258
+ const latestThought = thoughts[thoughts.length - 1]?.content || "";
259
+ const latestActions = actions.slice(-10);
260
+ const latestObservations = recentObservations;
261
+ const scratchpadEntry = {
262
+ step: currentStep,
263
+ thought: latestThought,
264
+ action: latestActions.map((a) => `${a.name}(${JSON.stringify(a.arguments)})`).join(", "),
265
+ observation: latestObservations.map((obs) => {
266
+ if (obs.error) {
267
+ return `Error: ${obs.error}`;
268
+ }
269
+ return typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result);
270
+ }).join("; "),
271
+ timestamp: Date.now()
272
+ };
273
+ const observationMessages = latestObservations.map((obs) => {
274
+ const content = obs.error ? `Error: ${obs.error}` : typeof obs.result === "string" ? obs.result : JSON.stringify(obs.result, null, 2);
275
+ return {
276
+ role: "tool",
277
+ content,
278
+ name: latestActions.find((a) => a.id === obs.toolCallId)?.name
279
+ };
280
+ });
281
+ return {
282
+ scratchpad: [scratchpadEntry],
283
+ messages: observationMessages
284
+ };
285
+ };
286
+ }
287
+
288
+ // src/react/agent.ts
289
+ import { StateGraph, END } from "@langchain/langgraph";
290
+ import { ToolRegistry } from "@agentforge/core";
291
+ function createReActAgent(config, options) {
292
+ const {
293
+ llm,
294
+ tools,
295
+ systemPrompt = DEFAULT_REACT_SYSTEM_PROMPT,
296
+ maxIterations = 10,
297
+ returnIntermediateSteps = false,
298
+ stopCondition
299
+ } = config;
300
+ const {
301
+ verbose = false,
302
+ nodeNames = {}
303
+ } = options || {};
304
+ const toolArray = tools instanceof ToolRegistry ? tools.getAll() : tools;
305
+ const REASONING_NODE = nodeNames.reasoning || "reasoning";
306
+ const ACTION_NODE = nodeNames.action || "action";
307
+ const OBSERVATION_NODE = nodeNames.observation || "observation";
308
+ const reasoningNode = createReasoningNode(
309
+ llm,
310
+ toolArray,
311
+ systemPrompt,
312
+ maxIterations,
313
+ verbose
314
+ );
315
+ const actionNode = createActionNode(toolArray, verbose);
316
+ const observationNode = createObservationNode(verbose);
317
+ const shouldContinue = (state) => {
318
+ if (stopCondition && stopCondition(state)) {
319
+ return END;
320
+ }
321
+ if (state.iteration >= maxIterations) {
322
+ return END;
323
+ }
324
+ if (state.response) {
325
+ return END;
326
+ }
327
+ if (state.shouldContinue === false) {
328
+ return END;
329
+ }
330
+ return ACTION_NODE;
331
+ };
332
+ const workflow = new StateGraph(ReActState).addNode(REASONING_NODE, reasoningNode).addNode(ACTION_NODE, actionNode).addNode(OBSERVATION_NODE, observationNode).addEdge("__start__", REASONING_NODE).addConditionalEdges(REASONING_NODE, shouldContinue).addEdge(ACTION_NODE, OBSERVATION_NODE).addEdge(OBSERVATION_NODE, REASONING_NODE);
333
+ return workflow.compile();
334
+ }
335
+
336
+ // src/react/builder.ts
337
+ var ReActAgentBuilder = class {
338
+ config = {};
339
+ options = {};
340
+ /**
341
+ * Set the language model (required)
342
+ *
343
+ * @param llm - LangChain chat model to use for reasoning
344
+ */
345
+ withLLM(llm) {
346
+ this.config.llm = llm;
347
+ return this;
348
+ }
349
+ /**
350
+ * Set the tools (required)
351
+ *
352
+ * @param tools - Tool registry or array of tools
353
+ */
354
+ withTools(tools) {
355
+ this.config.tools = tools;
356
+ return this;
357
+ }
358
+ /**
359
+ * Set the system prompt (optional)
360
+ *
361
+ * @param systemPrompt - System prompt for the agent
362
+ */
363
+ withSystemPrompt(systemPrompt) {
364
+ this.config.systemPrompt = systemPrompt;
365
+ return this;
366
+ }
367
+ /**
368
+ * Set the maximum iterations (optional, default: 10)
369
+ *
370
+ * @param maxIterations - Maximum number of thought-action loops
371
+ */
372
+ withMaxIterations(maxIterations) {
373
+ this.config.maxIterations = maxIterations;
374
+ return this;
375
+ }
376
+ /**
377
+ * Set whether to return intermediate steps (optional, default: false)
378
+ *
379
+ * @param returnIntermediateSteps - Whether to include reasoning steps in output
380
+ */
381
+ withReturnIntermediateSteps(returnIntermediateSteps) {
382
+ this.config.returnIntermediateSteps = returnIntermediateSteps;
383
+ return this;
384
+ }
385
+ /**
386
+ * Set a custom stop condition (optional)
387
+ *
388
+ * @param stopCondition - Function that determines when to stop the agent
389
+ */
390
+ withStopCondition(stopCondition) {
391
+ this.config.stopCondition = stopCondition;
392
+ return this;
393
+ }
394
+ /**
395
+ * Enable verbose logging (optional, default: false)
396
+ *
397
+ * @param verbose - Whether to enable verbose logging
398
+ */
399
+ withVerbose(verbose) {
400
+ this.options.verbose = verbose;
401
+ return this;
402
+ }
403
+ /**
404
+ * Set custom node names (optional)
405
+ *
406
+ * @param nodeNames - Custom names for nodes (for debugging/observability)
407
+ */
408
+ withNodeNames(nodeNames) {
409
+ this.options.nodeNames = nodeNames;
410
+ return this;
411
+ }
412
+ /**
413
+ * Build the ReAct agent
414
+ *
415
+ * @returns A compiled LangGraph StateGraph
416
+ * @throws Error if required configuration is missing
417
+ */
418
+ build() {
419
+ if (!this.config.llm) {
420
+ throw new Error("ReActAgentBuilder: llm is required. Use withLLM() to set it.");
421
+ }
422
+ if (!this.config.tools) {
423
+ throw new Error("ReActAgentBuilder: tools are required. Use withTools() to set them.");
424
+ }
425
+ const finalConfig = {
426
+ llm: this.config.llm,
427
+ tools: this.config.tools,
428
+ systemPrompt: this.config.systemPrompt || DEFAULT_REACT_SYSTEM_PROMPT,
429
+ maxIterations: this.config.maxIterations ?? 10,
430
+ returnIntermediateSteps: this.config.returnIntermediateSteps ?? false,
431
+ stopCondition: this.config.stopCondition
432
+ };
433
+ return createReActAgent(finalConfig, this.options);
434
+ }
435
+ };
436
+ function createReActAgentBuilder() {
437
+ return new ReActAgentBuilder();
438
+ }
439
+
440
+ // src/plan-execute/agent.ts
441
+ import { StateGraph as StateGraph2, END as END2 } from "@langchain/langgraph";
442
+
443
+ // src/plan-execute/state.ts
444
+ import { z as z4 } from "zod";
445
+ import { createStateAnnotation as createStateAnnotation2 } from "@agentforge/core";
446
+
447
+ // src/plan-execute/schemas.ts
448
+ import { z as z3 } from "zod";
449
+ var PlanStepSchema = z3.object({
450
+ /**
451
+ * Unique identifier for the step
452
+ */
453
+ id: z3.string().describe("Unique identifier for the step"),
454
+ /**
455
+ * Description of what this step should accomplish
456
+ */
457
+ description: z3.string().describe("Description of what this step should accomplish"),
458
+ /**
459
+ * Optional dependencies on other steps (by ID)
460
+ */
461
+ dependencies: z3.array(z3.string()).optional().describe("IDs of steps that must complete before this one"),
462
+ /**
463
+ * Optional tool to use for this step
464
+ */
465
+ tool: z3.string().optional().describe("Name of the tool to use for this step"),
466
+ /**
467
+ * Optional arguments for the tool
468
+ */
469
+ args: z3.record(z3.any()).optional().describe("Arguments to pass to the tool")
470
+ });
471
+ var CompletedStepSchema = z3.object({
472
+ /**
473
+ * The step that was executed
474
+ */
475
+ step: PlanStepSchema.describe("The step that was executed"),
476
+ /**
477
+ * The result of executing the step
478
+ */
479
+ result: z3.any().describe("The result of executing the step"),
480
+ /**
481
+ * Whether the step succeeded
482
+ */
483
+ success: z3.boolean().describe("Whether the step succeeded"),
484
+ /**
485
+ * Optional error message if the step failed
486
+ */
487
+ error: z3.string().optional().describe("Error message if the step failed"),
488
+ /**
489
+ * Timestamp when the step was completed
490
+ */
491
+ timestamp: z3.string().datetime().describe("ISO timestamp when the step was completed")
492
+ });
493
+ var PlanSchema = z3.object({
494
+ /**
495
+ * List of steps in the plan
496
+ */
497
+ steps: z3.array(PlanStepSchema).describe("List of steps in the plan"),
498
+ /**
499
+ * Overall goal of the plan
500
+ */
501
+ goal: z3.string().describe("Overall goal of the plan"),
502
+ /**
503
+ * Timestamp when the plan was created
504
+ */
505
+ createdAt: z3.string().datetime().describe("ISO timestamp when the plan was created"),
506
+ /**
507
+ * Optional confidence score (0-1)
508
+ */
509
+ confidence: z3.number().min(0).max(1).optional().describe("Confidence score for the plan (0-1)")
510
+ });
511
+ var ReplanDecisionSchema = z3.object({
512
+ /**
513
+ * Whether to replan
514
+ */
515
+ shouldReplan: z3.boolean().describe("Whether to replan based on current results"),
516
+ /**
517
+ * Reason for the decision
518
+ */
519
+ reason: z3.string().describe("Reason for the replan decision"),
520
+ /**
521
+ * Optional new goal if replanning
522
+ */
523
+ newGoal: z3.string().optional().describe("Updated goal if replanning")
524
+ });
525
+ var ExecutionStatusSchema = z3.enum([
526
+ "planning",
527
+ "executing",
528
+ "replanning",
529
+ "completed",
530
+ "failed"
531
+ ]).describe("Current status of the plan execution");
532
+
533
+ // src/plan-execute/state.ts
534
+ var PlanExecuteStateConfig = {
535
+ /**
536
+ * Original user input/query
537
+ */
538
+ input: {
539
+ schema: z4.string(),
540
+ default: () => "",
541
+ description: "Original user input or query"
542
+ },
543
+ /**
544
+ * The current plan
545
+ */
546
+ plan: {
547
+ schema: PlanSchema.optional(),
548
+ description: "The current execution plan"
549
+ },
550
+ /**
551
+ * Completed steps with their results
552
+ * Accumulates all completed steps
553
+ */
554
+ pastSteps: {
555
+ schema: z4.array(CompletedStepSchema),
556
+ reducer: (left, right) => [...left, ...right],
557
+ default: () => [],
558
+ description: "Completed steps with their results"
559
+ },
560
+ /**
561
+ * Index of the current step being executed
562
+ */
563
+ currentStepIndex: {
564
+ schema: z4.number().int().nonnegative().optional(),
565
+ description: "Index of the current step being executed"
566
+ },
567
+ /**
568
+ * Current execution status
569
+ */
570
+ status: {
571
+ schema: ExecutionStatusSchema,
572
+ default: () => "planning",
573
+ description: "Current execution status"
574
+ },
575
+ /**
576
+ * Final response
577
+ */
578
+ response: {
579
+ schema: z4.string().optional(),
580
+ description: "Final response after plan execution"
581
+ },
582
+ /**
583
+ * Error message if execution failed
584
+ */
585
+ error: {
586
+ schema: z4.string().optional(),
587
+ description: "Error message if execution failed"
588
+ },
589
+ /**
590
+ * Iteration counter for replanning
591
+ */
592
+ iteration: {
593
+ schema: z4.number().int().nonnegative(),
594
+ reducer: (left, right) => left + right,
595
+ default: () => 0,
596
+ description: "Number of planning iterations"
597
+ },
598
+ /**
599
+ * Maximum iterations allowed
600
+ */
601
+ maxIterations: {
602
+ schema: z4.number().int().positive(),
603
+ default: () => 5,
604
+ description: "Maximum number of planning iterations allowed"
605
+ }
606
+ };
607
+ var PlanExecuteState = createStateAnnotation2(PlanExecuteStateConfig);
608
+
609
+ // src/plan-execute/nodes.ts
610
+ import { HumanMessage as HumanMessage2, SystemMessage as SystemMessage2 } from "@langchain/core/messages";
611
+
612
+ // src/plan-execute/prompts.ts
613
+ var DEFAULT_PLANNER_SYSTEM_PROMPT = `You are an expert planning assistant. Your job is to create a detailed, step-by-step plan to accomplish the user's goal.
614
+
615
+ Guidelines for creating plans:
616
+ 1. Break down complex tasks into clear, actionable steps
617
+ 2. Each step should have a specific, measurable outcome
618
+ 3. Identify dependencies between steps
619
+ 4. Consider which tools are needed for each step
620
+ 5. Keep the plan focused and efficient
621
+ 6. Aim for 3-7 steps for most tasks
622
+
623
+ Output your plan as a JSON object with the following structure:
624
+ {
625
+ "goal": "The overall goal",
626
+ "steps": [
627
+ {
628
+ "id": "step-1",
629
+ "description": "What this step accomplishes",
630
+ "tool": "tool_name (optional)",
631
+ "args": {"key": "value (optional)"},
632
+ "dependencies": ["step-id (optional)"]
633
+ }
634
+ ],
635
+ "confidence": 0.9
636
+ }`;
637
+ var DEFAULT_REPLANNER_SYSTEM_PROMPT = `You are an expert replanning assistant. Your job is to decide whether the current plan needs to be adjusted based on the results so far.
638
+
639
+ Consider:
640
+ 1. Have the completed steps achieved their intended outcomes?
641
+ 2. Are there any unexpected results that require plan changes?
642
+ 3. Is the original goal still achievable with the current plan?
643
+ 4. Would a different approach be more effective?
644
+
645
+ Output your decision as a JSON object:
646
+ {
647
+ "shouldReplan": true/false,
648
+ "reason": "Explanation for the decision",
649
+ "newGoal": "Updated goal if replanning (optional)"
650
+ }`;
651
+ var PLANNING_PROMPT_TEMPLATE = `User Goal: {input}
652
+
653
+ {toolDescriptions}
654
+
655
+ Create a step-by-step plan to accomplish this goal.`;
656
+ var REPLANNING_PROMPT_TEMPLATE = `Original Goal: {goal}
657
+
658
+ Completed Steps:
659
+ {completedSteps}
660
+
661
+ Current Plan:
662
+ {remainingSteps}
663
+
664
+ Based on the results so far, should we continue with the current plan or replan?`;
665
+ var COMPLETED_STEP_TEMPLATE = `Step {stepNumber}: {description}
666
+ Result: {result}
667
+ Status: {status}`;
668
+ var REMAINING_STEP_TEMPLATE = `Step {stepNumber}: {description}
669
+ {dependencies}`;
670
+
671
+ // src/plan-execute/nodes.ts
672
+ function createPlannerNode(config) {
673
+ const {
674
+ llm,
675
+ systemPrompt = DEFAULT_PLANNER_SYSTEM_PROMPT,
676
+ maxSteps = 7,
677
+ includeToolDescriptions = false
678
+ } = config;
679
+ return async (state) => {
680
+ try {
681
+ let toolDescriptions = "";
682
+ if (includeToolDescriptions) {
683
+ toolDescriptions = "";
684
+ }
685
+ const userPrompt = PLANNING_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{toolDescriptions}", toolDescriptions);
686
+ const messages = [
687
+ new SystemMessage2(systemPrompt),
688
+ new HumanMessage2(userPrompt)
689
+ ];
690
+ const response = await llm.invoke(messages);
691
+ const content = response.content.toString();
692
+ let plan;
693
+ try {
694
+ const parsed = JSON.parse(content);
695
+ plan = {
696
+ steps: parsed.steps.slice(0, maxSteps),
697
+ // Limit to maxSteps
698
+ goal: parsed.goal || state.input || "",
699
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
700
+ confidence: parsed.confidence
701
+ };
702
+ } catch (parseError) {
703
+ throw new Error(`Failed to parse plan from LLM response: ${parseError}`);
704
+ }
705
+ return {
706
+ plan,
707
+ status: "executing",
708
+ currentStepIndex: 0,
709
+ iteration: 1
710
+ };
711
+ } catch (error) {
712
+ return {
713
+ status: "failed",
714
+ error: error instanceof Error ? error.message : "Unknown error in planner"
715
+ };
716
+ }
717
+ };
718
+ }
719
+ function createExecutorNode(config) {
720
+ const {
721
+ tools,
722
+ llm,
723
+ parallel = false,
724
+ stepTimeout = 3e4
725
+ } = config;
726
+ return async (state) => {
727
+ try {
728
+ const { plan, currentStepIndex = 0, pastSteps = [] } = state;
729
+ if (!plan || !plan.steps || plan.steps.length === 0) {
730
+ return {
731
+ status: "completed"
732
+ };
733
+ }
734
+ if (currentStepIndex >= plan.steps.length) {
735
+ return {
736
+ status: "completed"
737
+ };
738
+ }
739
+ const currentStep = plan.steps[currentStepIndex];
740
+ if (currentStep.dependencies && currentStep.dependencies.length > 0) {
741
+ const completedStepIds = new Set(pastSteps.map((ps) => ps.step.id));
742
+ const unmetDependencies = currentStep.dependencies.filter((dep) => !completedStepIds.has(dep));
743
+ if (unmetDependencies.length > 0) {
744
+ throw new Error(`Unmet dependencies for step ${currentStep.id}: ${unmetDependencies.join(", ")}`);
745
+ }
746
+ }
747
+ let result;
748
+ let success = true;
749
+ let error;
750
+ try {
751
+ if (currentStep.tool) {
752
+ const tool = tools.find((t) => t.metadata.name === currentStep.tool);
753
+ if (!tool) {
754
+ throw new Error(`Tool not found: ${currentStep.tool}`);
755
+ }
756
+ const timeoutPromise = new Promise(
757
+ (_, reject) => setTimeout(() => reject(new Error("Step execution timeout")), stepTimeout)
758
+ );
759
+ result = await Promise.race([
760
+ tool.execute(currentStep.args || {}),
761
+ timeoutPromise
762
+ ]);
763
+ } else {
764
+ result = { message: "Step completed without tool execution" };
765
+ }
766
+ } catch (execError) {
767
+ success = false;
768
+ error = execError instanceof Error ? execError.message : "Unknown execution error";
769
+ result = null;
770
+ }
771
+ const completedStep = {
772
+ step: currentStep,
773
+ result,
774
+ success,
775
+ error,
776
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
777
+ };
778
+ return {
779
+ pastSteps: [completedStep],
780
+ currentStepIndex: currentStepIndex + 1
781
+ };
782
+ } catch (error) {
783
+ return {
784
+ status: "failed",
785
+ error: error instanceof Error ? error.message : "Unknown error in executor"
786
+ };
787
+ }
788
+ };
789
+ }
790
+ function createReplannerNode(config) {
791
+ const {
792
+ llm,
793
+ replanThreshold = 0.7,
794
+ systemPrompt = DEFAULT_REPLANNER_SYSTEM_PROMPT
795
+ } = config;
796
+ return async (state) => {
797
+ try {
798
+ const { plan, pastSteps = [], currentStepIndex = 0 } = state;
799
+ if (!plan) {
800
+ return { status: "failed", error: "No plan available for replanning" };
801
+ }
802
+ const completedStepsText = pastSteps.map(
803
+ (ps, idx) => COMPLETED_STEP_TEMPLATE.replace("{stepNumber}", String(idx + 1)).replace("{description}", ps.step.description).replace("{result}", JSON.stringify(ps.result)).replace("{status}", ps.success ? "Success" : `Failed: ${ps.error}`)
804
+ ).join("\n\n");
805
+ const remainingSteps = plan.steps.slice(currentStepIndex);
806
+ const remainingStepsText = remainingSteps.map(
807
+ (step, idx) => REMAINING_STEP_TEMPLATE.replace("{stepNumber}", String(currentStepIndex + idx + 1)).replace("{description}", step.description).replace("{dependencies}", step.dependencies ? `Dependencies: ${step.dependencies.join(", ")}` : "")
808
+ ).join("\n\n");
809
+ const userPrompt = REPLANNING_PROMPT_TEMPLATE.replace("{goal}", plan.goal).replace("{completedSteps}", completedStepsText || "None").replace("{remainingSteps}", remainingStepsText || "None");
810
+ const messages = [
811
+ new SystemMessage2(systemPrompt),
812
+ new HumanMessage2(userPrompt)
813
+ ];
814
+ const response = await llm.invoke(messages);
815
+ const content = response.content.toString();
816
+ let decision;
817
+ try {
818
+ decision = JSON.parse(content);
819
+ } catch (parseError) {
820
+ throw new Error(`Failed to parse replan decision from LLM response: ${parseError}`);
821
+ }
822
+ if (decision.shouldReplan) {
823
+ return {
824
+ status: "planning",
825
+ input: decision.newGoal || plan.goal,
826
+ iteration: 1
827
+ };
828
+ } else {
829
+ return {
830
+ status: "executing"
831
+ };
832
+ }
833
+ } catch (error) {
834
+ return {
835
+ status: "failed",
836
+ error: error instanceof Error ? error.message : "Unknown error in replanner"
837
+ };
838
+ }
839
+ };
840
+ }
841
+ function createFinisherNode() {
842
+ return async (state) => {
843
+ const results = state.pastSteps?.map((ps) => ({
844
+ step: ps.step.description,
845
+ result: ps.result,
846
+ success: ps.success
847
+ })) || [];
848
+ const response = JSON.stringify({
849
+ goal: state.plan?.goal || state.input,
850
+ results,
851
+ totalSteps: state.pastSteps?.length || 0,
852
+ successfulSteps: state.pastSteps?.filter((ps) => ps.success).length || 0
853
+ }, null, 2);
854
+ return {
855
+ status: "completed",
856
+ response
857
+ };
858
+ };
859
+ }
860
+
861
+ // src/plan-execute/agent.ts
862
+ function createPlanExecuteAgent(config) {
863
+ const {
864
+ planner,
865
+ executor,
866
+ replanner,
867
+ maxIterations = 5,
868
+ verbose = false
869
+ } = config;
870
+ const plannerNode = createPlannerNode(planner);
871
+ const executorNode = createExecutorNode(executor);
872
+ const finisherNode = createFinisherNode();
873
+ const replannerNode = replanner ? createReplannerNode(replanner) : async (state) => ({ status: "executing" });
874
+ const workflow = new StateGraph2(PlanExecuteState).addNode("planner", plannerNode).addNode("executor", executorNode).addNode("finisher", finisherNode).addNode("replanner", replannerNode);
875
+ const routeAfterExecutor = (state) => {
876
+ if (state.status === "failed") {
877
+ return "error";
878
+ }
879
+ if (state.status === "completed") {
880
+ return "finish";
881
+ }
882
+ const allStepsCompleted = state.currentStepIndex !== void 0 && state.plan?.steps && state.currentStepIndex >= state.plan.steps.length;
883
+ if (allStepsCompleted) {
884
+ return "finish";
885
+ }
886
+ if (replanner && state.iteration < maxIterations) {
887
+ const recentSteps = state.pastSteps?.slice(-3) || [];
888
+ const hasFailures = recentSteps.some((step) => !step.success);
889
+ if (hasFailures) {
890
+ return "replan";
891
+ }
892
+ }
893
+ return "execute";
894
+ };
895
+ const routeAfterReplanner = (state) => {
896
+ if (state.status === "failed") {
897
+ return "error";
898
+ }
899
+ if (state.status === "planning") {
900
+ return "replan";
901
+ }
902
+ return "execute";
903
+ };
904
+ workflow.addEdge("__start__", "planner");
905
+ workflow.addEdge("planner", "executor");
906
+ workflow.addConditionalEdges(
907
+ "executor",
908
+ routeAfterExecutor,
909
+ {
910
+ execute: "executor",
911
+ // Loop back to execute next step
912
+ replan: "replanner",
913
+ finish: "finisher",
914
+ error: END2
915
+ }
916
+ );
917
+ workflow.addEdge("finisher", END2);
918
+ workflow.addConditionalEdges(
919
+ "replanner",
920
+ routeAfterReplanner,
921
+ {
922
+ replan: "planner",
923
+ execute: "executor",
924
+ error: END2,
925
+ finish: END2
926
+ }
927
+ );
928
+ return workflow.compile();
929
+ }
930
+
931
+ // src/reflection/state.ts
932
+ import { z as z6 } from "zod";
933
+ import { createStateAnnotation as createStateAnnotation3 } from "@agentforge/core";
934
+
935
+ // src/reflection/schemas.ts
936
+ import { z as z5 } from "zod";
937
+ var ReflectionSchema = z5.object({
938
+ /**
939
+ * The critique or feedback on the current response
940
+ */
941
+ critique: z5.string().describe("Critique or feedback on the current response"),
942
+ /**
943
+ * Specific issues identified
944
+ */
945
+ issues: z5.array(z5.string()).describe("Specific issues or problems identified"),
946
+ /**
947
+ * Suggestions for improvement
948
+ */
949
+ suggestions: z5.array(z5.string()).describe("Suggestions for improving the response"),
950
+ /**
951
+ * Quality score (0-10)
952
+ */
953
+ score: z5.number().min(0).max(10).optional().describe("Quality score from 0 to 10"),
954
+ /**
955
+ * Whether the response meets quality standards
956
+ */
957
+ meetsStandards: z5.boolean().describe("Whether the response meets quality standards"),
958
+ /**
959
+ * Timestamp of the reflection
960
+ */
961
+ timestamp: z5.date().optional().describe("When this reflection was created")
962
+ });
963
+ var RevisionSchema = z5.object({
964
+ /**
965
+ * The revised content
966
+ */
967
+ content: z5.string().describe("The revised content"),
968
+ /**
969
+ * Which iteration this revision is from
970
+ */
971
+ iteration: z5.number().int().nonnegative().describe("Iteration number"),
972
+ /**
973
+ * The reflection that prompted this revision
974
+ */
975
+ basedOn: ReflectionSchema.optional().describe("The reflection that prompted this revision"),
976
+ /**
977
+ * Timestamp of the revision
978
+ */
979
+ timestamp: z5.date().optional().describe("When this revision was created")
980
+ });
981
+ var ReflectionStatusSchema = z5.enum([
982
+ "generating",
983
+ // Initial generation
984
+ "reflecting",
985
+ // Critiquing current response
986
+ "revising",
987
+ // Improving based on critique
988
+ "completed",
989
+ // Quality threshold met
990
+ "failed"
991
+ // Max iterations reached without meeting standards
992
+ ]);
993
+ var QualityCriteriaSchema = z5.object({
994
+ /**
995
+ * Minimum quality score required (0-10)
996
+ */
997
+ minScore: z5.number().min(0).max(10).default(7).describe("Minimum quality score required"),
998
+ /**
999
+ * Specific criteria to evaluate
1000
+ */
1001
+ criteria: z5.array(z5.string()).optional().describe("Specific criteria to evaluate"),
1002
+ /**
1003
+ * Whether all criteria must be met
1004
+ */
1005
+ requireAll: z5.boolean().default(true).describe("Whether all criteria must be met")
1006
+ });
1007
+ var ReflectionConfigSchema = z5.object({
1008
+ /**
1009
+ * Maximum number of reflection iterations
1010
+ */
1011
+ maxIterations: z5.number().int().positive().default(3).describe("Maximum reflection iterations"),
1012
+ /**
1013
+ * Quality criteria for completion
1014
+ */
1015
+ qualityCriteria: QualityCriteriaSchema.optional().describe("Quality criteria for completion"),
1016
+ /**
1017
+ * Whether to include previous reflections in context
1018
+ */
1019
+ includeHistory: z5.boolean().default(true).describe("Include previous reflections in context")
1020
+ });
1021
+
1022
+ // src/reflection/state.ts
1023
+ var ReflectionStateConfig = {
1024
+ /**
1025
+ * Original user input/task
1026
+ */
1027
+ input: {
1028
+ schema: z6.string(),
1029
+ default: () => "",
1030
+ description: "Original user input or task"
1031
+ },
1032
+ /**
1033
+ * Current response/output
1034
+ */
1035
+ currentResponse: {
1036
+ schema: z6.string().optional(),
1037
+ description: "Current response or output"
1038
+ },
1039
+ /**
1040
+ * History of all reflections/critiques
1041
+ * Accumulates all reflections
1042
+ */
1043
+ reflections: {
1044
+ schema: z6.array(ReflectionSchema),
1045
+ reducer: (left, right) => [...left, ...right],
1046
+ default: () => [],
1047
+ description: "History of all reflections and critiques"
1048
+ },
1049
+ /**
1050
+ * History of all revisions
1051
+ * Accumulates all revisions
1052
+ */
1053
+ revisions: {
1054
+ schema: z6.array(RevisionSchema),
1055
+ reducer: (left, right) => [...left, ...right],
1056
+ default: () => [],
1057
+ description: "History of all revisions"
1058
+ },
1059
+ /**
1060
+ * Current iteration number
1061
+ */
1062
+ iteration: {
1063
+ schema: z6.number().int().nonnegative(),
1064
+ reducer: (left, right) => left + right,
1065
+ default: () => 0,
1066
+ description: "Current iteration number"
1067
+ },
1068
+ /**
1069
+ * Current status
1070
+ */
1071
+ status: {
1072
+ schema: ReflectionStatusSchema,
1073
+ default: () => "generating",
1074
+ description: "Current reflection status"
1075
+ },
1076
+ /**
1077
+ * Quality criteria for completion
1078
+ */
1079
+ qualityCriteria: {
1080
+ schema: QualityCriteriaSchema.optional(),
1081
+ description: "Quality criteria for determining completion"
1082
+ },
1083
+ /**
1084
+ * Maximum iterations allowed
1085
+ */
1086
+ maxIterations: {
1087
+ schema: z6.number().int().positive(),
1088
+ default: () => 3,
1089
+ description: "Maximum number of reflection iterations allowed"
1090
+ },
1091
+ /**
1092
+ * Final response (when completed)
1093
+ */
1094
+ response: {
1095
+ schema: z6.string().optional(),
1096
+ description: "Final response after reflection process"
1097
+ },
1098
+ /**
1099
+ * Error message if failed
1100
+ */
1101
+ error: {
1102
+ schema: z6.string().optional(),
1103
+ description: "Error message if reflection failed"
1104
+ }
1105
+ };
1106
+ var ReflectionState = createStateAnnotation3(ReflectionStateConfig);
1107
+
1108
+ // src/reflection/prompts.ts
1109
+ var DEFAULT_GENERATOR_SYSTEM_PROMPT = `You are an expert content generator. Your task is to create high-quality responses to user requests.
1110
+
1111
+ Focus on:
1112
+ - Clarity and coherence
1113
+ - Accuracy and correctness
1114
+ - Completeness and thoroughness
1115
+ - Appropriate tone and style
1116
+
1117
+ Generate the best possible response to the user's request.`;
1118
+ var DEFAULT_REFLECTOR_SYSTEM_PROMPT = `You are an expert critic and reviewer. Your task is to provide constructive feedback on responses.
1119
+
1120
+ Evaluate the response based on:
1121
+ - Clarity: Is it easy to understand?
1122
+ - Accuracy: Is the information correct?
1123
+ - Completeness: Does it fully address the request?
1124
+ - Quality: Is it well-written and professional?
1125
+
1126
+ Provide specific, actionable feedback for improvement.`;
1127
+ var DEFAULT_REVISER_SYSTEM_PROMPT = `You are an expert editor and reviser. Your task is to improve responses based on feedback.
1128
+
1129
+ Focus on:
1130
+ - Addressing all identified issues
1131
+ - Implementing suggested improvements
1132
+ - Maintaining the core message
1133
+ - Enhancing overall quality
1134
+
1135
+ Create an improved version that addresses the critique.`;
1136
+ var GENERATION_PROMPT_TEMPLATE = `Please generate a response to the following request:
1137
+
1138
+ {input}
1139
+
1140
+ {context}
1141
+
1142
+ Provide a high-quality, complete response.`;
1143
+ var REFLECTION_PROMPT_TEMPLATE = `Please review and critique the following response:
1144
+
1145
+ Original Request:
1146
+ {input}
1147
+
1148
+ Current Response:
1149
+ {currentResponse}
1150
+
1151
+ {criteria}
1152
+
1153
+ {history}
1154
+
1155
+ Provide a detailed critique including:
1156
+ 1. What works well
1157
+ 2. Specific issues or problems
1158
+ 3. Suggestions for improvement
1159
+ 4. A quality score (0-10)
1160
+ 5. Whether it meets quality standards
1161
+
1162
+ Format your response as JSON with the following structure:
1163
+ {
1164
+ "critique": "overall assessment",
1165
+ "issues": ["issue 1", "issue 2"],
1166
+ "suggestions": ["suggestion 1", "suggestion 2"],
1167
+ "score": 8,
1168
+ "meetsStandards": true
1169
+ }`;
1170
+ var REVISION_PROMPT_TEMPLATE = `Please revise the following response based on the critique:
1171
+
1172
+ Original Request:
1173
+ {input}
1174
+
1175
+ Current Response:
1176
+ {currentResponse}
1177
+
1178
+ Critique:
1179
+ {critique}
1180
+
1181
+ Issues to Address:
1182
+ {issues}
1183
+
1184
+ Suggestions:
1185
+ {suggestions}
1186
+
1187
+ {history}
1188
+
1189
+ Create an improved version that addresses all the feedback while maintaining the core message.`;
1190
+ var QUALITY_CRITERIA_TEMPLATE = `Evaluate against these specific criteria:
1191
+ {criteria}
1192
+
1193
+ Minimum required score: {minScore}/10
1194
+ {requireAll}`;
1195
+ var REFLECTION_HISTORY_TEMPLATE = `Previous Reflections:
1196
+ {reflections}
1197
+
1198
+ Previous Revisions:
1199
+ {revisions}`;
1200
+ var REFLECTION_ENTRY_TEMPLATE = `Iteration {iteration}:
1201
+ Critique: {critique}
1202
+ Score: {score}/10
1203
+ Issues: {issues}
1204
+ Suggestions: {suggestions}`;
1205
+ var REVISION_ENTRY_TEMPLATE = `Iteration {iteration}:
1206
+ {content}`;
1207
+
1208
+ // src/reflection/nodes.ts
1209
+ import { HumanMessage as HumanMessage3, SystemMessage as SystemMessage3 } from "@langchain/core/messages";
1210
+ function createGeneratorNode(config) {
1211
+ const {
1212
+ llm,
1213
+ systemPrompt = DEFAULT_GENERATOR_SYSTEM_PROMPT,
1214
+ verbose = false
1215
+ } = config;
1216
+ return async (state) => {
1217
+ try {
1218
+ if (verbose) {
1219
+ console.log("[Generator] Generating initial response...");
1220
+ }
1221
+ let context = "";
1222
+ if (state.iteration > 0 && state.reflections.length > 0) {
1223
+ const lastReflection = state.reflections[state.reflections.length - 1];
1224
+ context = `
1225
+ Previous feedback to consider:
1226
+ ${lastReflection.critique}`;
1227
+ }
1228
+ const userPrompt = GENERATION_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{context}", context);
1229
+ const messages = [
1230
+ new SystemMessage3(systemPrompt),
1231
+ new HumanMessage3(userPrompt)
1232
+ ];
1233
+ const response = await llm.invoke(messages);
1234
+ const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1235
+ if (verbose) {
1236
+ console.log("[Generator] Generated response:", content.substring(0, 100) + "...");
1237
+ }
1238
+ return {
1239
+ currentResponse: content,
1240
+ status: "reflecting",
1241
+ iteration: 1
1242
+ };
1243
+ } catch (error) {
1244
+ console.error("[Generator] Error:", error);
1245
+ return {
1246
+ status: "failed",
1247
+ error: error instanceof Error ? error.message : "Unknown error in generator"
1248
+ };
1249
+ }
1250
+ };
1251
+ }
1252
+ function createReflectorNode(config) {
1253
+ const {
1254
+ llm,
1255
+ systemPrompt = DEFAULT_REFLECTOR_SYSTEM_PROMPT,
1256
+ qualityCriteria,
1257
+ verbose = false
1258
+ } = config;
1259
+ return async (state) => {
1260
+ try {
1261
+ if (verbose) {
1262
+ console.log("[Reflector] Reflecting on response...");
1263
+ }
1264
+ if (!state.currentResponse) {
1265
+ throw new Error("No current response to reflect on");
1266
+ }
1267
+ let criteriaSection = "";
1268
+ const criteria = qualityCriteria || state.qualityCriteria;
1269
+ if (criteria) {
1270
+ const criteriaList = criteria.criteria?.join("\n- ") || "";
1271
+ criteriaSection = QUALITY_CRITERIA_TEMPLATE.replace("{criteria}", criteriaList ? `- ${criteriaList}` : "General quality standards").replace("{minScore}", criteria.minScore?.toString() || "7").replace("{requireAll}", criteria.requireAll ? "All criteria must be met." : "Meet as many criteria as possible.");
1272
+ }
1273
+ let historySection = "";
1274
+ if (state.reflections.length > 0 || state.revisions.length > 0) {
1275
+ const reflectionsText = state.reflections.map(
1276
+ (r, idx) => REFLECTION_ENTRY_TEMPLATE.replace("{iteration}", (idx + 1).toString()).replace("{critique}", r.critique).replace("{score}", r.score?.toString() || "N/A").replace("{issues}", r.issues.join(", ")).replace("{suggestions}", r.suggestions.join(", "))
1277
+ ).join("\n\n");
1278
+ const revisionsText = state.revisions.map(
1279
+ (r) => REVISION_ENTRY_TEMPLATE.replace("{iteration}", r.iteration.toString()).replace("{content}", r.content.substring(0, 200) + "...")
1280
+ ).join("\n\n");
1281
+ historySection = REFLECTION_HISTORY_TEMPLATE.replace("{reflections}", reflectionsText || "None").replace("{revisions}", revisionsText || "None");
1282
+ }
1283
+ const userPrompt = REFLECTION_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{currentResponse}", state.currentResponse).replace("{criteria}", criteriaSection).replace("{history}", historySection);
1284
+ const messages = [
1285
+ new SystemMessage3(systemPrompt),
1286
+ new HumanMessage3(userPrompt)
1287
+ ];
1288
+ const response = await llm.invoke(messages);
1289
+ const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1290
+ let reflection;
1291
+ try {
1292
+ const jsonMatch = content.match(/\{[\s\S]*\}/);
1293
+ if (jsonMatch) {
1294
+ reflection = JSON.parse(jsonMatch[0]);
1295
+ } else {
1296
+ reflection = {
1297
+ critique: content,
1298
+ issues: [],
1299
+ suggestions: [],
1300
+ score: 5,
1301
+ meetsStandards: false
1302
+ };
1303
+ }
1304
+ } catch (parseError) {
1305
+ reflection = {
1306
+ critique: content,
1307
+ issues: [],
1308
+ suggestions: [],
1309
+ score: 5,
1310
+ meetsStandards: false
1311
+ };
1312
+ }
1313
+ if (verbose) {
1314
+ console.log("[Reflector] Reflection score:", reflection.score);
1315
+ console.log("[Reflector] Meets standards:", reflection.meetsStandards);
1316
+ }
1317
+ return {
1318
+ reflections: [reflection],
1319
+ status: reflection.meetsStandards ? "completed" : "revising"
1320
+ };
1321
+ } catch (error) {
1322
+ console.error("[Reflector] Error:", error);
1323
+ return {
1324
+ status: "failed",
1325
+ error: error instanceof Error ? error.message : "Unknown error in reflector"
1326
+ };
1327
+ }
1328
+ };
1329
+ }
1330
+ function createReviserNode(config) {
1331
+ const {
1332
+ llm,
1333
+ systemPrompt = DEFAULT_REVISER_SYSTEM_PROMPT,
1334
+ verbose = false
1335
+ } = config;
1336
+ return async (state) => {
1337
+ try {
1338
+ if (verbose) {
1339
+ console.log("[Reviser] Revising response...");
1340
+ }
1341
+ if (!state.currentResponse) {
1342
+ throw new Error("No current response to revise");
1343
+ }
1344
+ if (state.reflections.length === 0) {
1345
+ throw new Error("No reflections to base revision on");
1346
+ }
1347
+ const lastReflection = state.reflections[state.reflections.length - 1];
1348
+ let historySection = "";
1349
+ if (state.revisions.length > 0) {
1350
+ const revisionsText = state.revisions.map(
1351
+ (r) => REVISION_ENTRY_TEMPLATE.replace("{iteration}", r.iteration.toString()).replace("{content}", r.content.substring(0, 200) + "...")
1352
+ ).join("\n\n");
1353
+ historySection = `
1354
+ Previous Revisions:
1355
+ ${revisionsText}`;
1356
+ }
1357
+ const userPrompt = REVISION_PROMPT_TEMPLATE.replace("{input}", state.input || "").replace("{currentResponse}", state.currentResponse).replace("{critique}", lastReflection.critique).replace("{issues}", lastReflection.issues.join("\n- ")).replace("{suggestions}", lastReflection.suggestions.join("\n- ")).replace("{history}", historySection);
1358
+ const messages = [
1359
+ new SystemMessage3(systemPrompt),
1360
+ new HumanMessage3(userPrompt)
1361
+ ];
1362
+ const response = await llm.invoke(messages);
1363
+ const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1364
+ if (verbose) {
1365
+ console.log("[Reviser] Created revision:", content.substring(0, 100) + "...");
1366
+ }
1367
+ const revision = {
1368
+ content,
1369
+ iteration: state.iteration,
1370
+ basedOn: lastReflection
1371
+ };
1372
+ return {
1373
+ currentResponse: content,
1374
+ revisions: [revision],
1375
+ status: "reflecting",
1376
+ iteration: 1
1377
+ };
1378
+ } catch (error) {
1379
+ console.error("[Reviser] Error:", error);
1380
+ return {
1381
+ status: "failed",
1382
+ error: error instanceof Error ? error.message : "Unknown error in reviser"
1383
+ };
1384
+ }
1385
+ };
1386
+ }
1387
+ function createFinisherNode2() {
1388
+ return async (state) => {
1389
+ return {
1390
+ status: "completed",
1391
+ response: state.currentResponse
1392
+ };
1393
+ };
1394
+ }
1395
+
1396
+ // src/reflection/agent.ts
1397
+ import { StateGraph as StateGraph3, END as END3 } from "@langchain/langgraph";
1398
+ function createReflectionAgent(config) {
1399
+ const {
1400
+ generator,
1401
+ reflector,
1402
+ reviser,
1403
+ maxIterations = 3,
1404
+ qualityCriteria,
1405
+ verbose = false
1406
+ } = config;
1407
+ const generatorNode = createGeneratorNode({ ...generator, verbose });
1408
+ const reflectorNode = createReflectorNode({ ...reflector, qualityCriteria, verbose });
1409
+ const reviserNode = createReviserNode({ ...reviser, verbose });
1410
+ const finisherNode = createFinisherNode2();
1411
+ const routeAfterGenerator = (state) => {
1412
+ if (state.status === "failed") {
1413
+ return "error";
1414
+ }
1415
+ return "reflect";
1416
+ };
1417
+ const routeAfterReflector = (state) => {
1418
+ if (state.status === "failed") {
1419
+ return "error";
1420
+ }
1421
+ if (state.status === "completed") {
1422
+ return "finish";
1423
+ }
1424
+ if (state.iteration >= maxIterations) {
1425
+ return "finish";
1426
+ }
1427
+ return "revise";
1428
+ };
1429
+ const routeAfterReviser = (state) => {
1430
+ if (state.status === "failed") {
1431
+ return "error";
1432
+ }
1433
+ if (state.iteration >= maxIterations) {
1434
+ return "finish";
1435
+ }
1436
+ return "reflect";
1437
+ };
1438
+ const workflow = new StateGraph3(ReflectionState).addNode("generator", generatorNode).addNode("reflector", reflectorNode).addNode("reviser", reviserNode).addNode("finisher", finisherNode);
1439
+ workflow.addEdge("__start__", "generator").addConditionalEdges(
1440
+ "generator",
1441
+ routeAfterGenerator,
1442
+ {
1443
+ reflect: "reflector",
1444
+ error: END3
1445
+ }
1446
+ ).addConditionalEdges(
1447
+ "reflector",
1448
+ routeAfterReflector,
1449
+ {
1450
+ revise: "reviser",
1451
+ finish: "finisher",
1452
+ error: END3
1453
+ }
1454
+ ).addConditionalEdges(
1455
+ "reviser",
1456
+ routeAfterReviser,
1457
+ {
1458
+ reflect: "reflector",
1459
+ finish: "finisher",
1460
+ error: END3
1461
+ }
1462
+ ).addEdge("finisher", END3);
1463
+ return workflow.compile();
1464
+ }
1465
+
1466
+ // src/multi-agent/state.ts
1467
+ import { z as z8 } from "zod";
1468
+ import { createStateAnnotation as createStateAnnotation4 } from "@agentforge/core";
1469
+
1470
+ // src/multi-agent/schemas.ts
1471
+ import { z as z7 } from "zod";
1472
+ var AgentRoleSchema = z7.enum(["supervisor", "worker"]);
1473
+ var MessageTypeSchema = z7.enum([
1474
+ "user_input",
1475
+ // Initial user message
1476
+ "task_assignment",
1477
+ // Supervisor assigns task to worker
1478
+ "task_result",
1479
+ // Worker returns result to supervisor
1480
+ "handoff",
1481
+ // Worker hands off to another worker
1482
+ "error",
1483
+ // Error message
1484
+ "completion"
1485
+ // Final completion message
1486
+ ]);
1487
+ var AgentMessageSchema = z7.object({
1488
+ /**
1489
+ * Unique identifier for the message
1490
+ */
1491
+ id: z7.string().describe("Unique message identifier"),
1492
+ /**
1493
+ * Type of message
1494
+ */
1495
+ type: MessageTypeSchema.describe("Type of message"),
1496
+ /**
1497
+ * Agent that sent the message
1498
+ */
1499
+ from: z7.string().describe("Agent identifier that sent the message"),
1500
+ /**
1501
+ * Agent(s) that should receive the message
1502
+ */
1503
+ to: z7.union([z7.string(), z7.array(z7.string())]).describe("Target agent(s)"),
1504
+ /**
1505
+ * Message content
1506
+ */
1507
+ content: z7.string().describe("Message content"),
1508
+ /**
1509
+ * Optional metadata
1510
+ */
1511
+ metadata: z7.record(z7.any()).optional().describe("Additional message metadata"),
1512
+ /**
1513
+ * Timestamp when message was created
1514
+ */
1515
+ timestamp: z7.number().describe("Timestamp when message was created")
1516
+ });
1517
+ var RoutingStrategySchema = z7.enum([
1518
+ "llm-based",
1519
+ // LLM decides which agent to route to
1520
+ "rule-based",
1521
+ // Predefined rules determine routing
1522
+ "round-robin",
1523
+ // Distribute tasks evenly across agents
1524
+ "skill-based",
1525
+ // Route based on agent capabilities
1526
+ "load-balanced"
1527
+ // Route based on agent workload
1528
+ ]);
1529
+ var RoutingDecisionSchema = z7.object({
1530
+ /**
1531
+ * Target agent to route to
1532
+ */
1533
+ targetAgent: z7.string().describe("Agent to route the task to"),
1534
+ /**
1535
+ * Reasoning for the routing decision
1536
+ */
1537
+ reasoning: z7.string().optional().describe("Explanation for routing decision"),
1538
+ /**
1539
+ * Confidence in the routing decision (0-1)
1540
+ */
1541
+ confidence: z7.number().min(0).max(1).optional().describe("Confidence score"),
1542
+ /**
1543
+ * Strategy used for routing
1544
+ */
1545
+ strategy: RoutingStrategySchema.describe("Strategy used for this decision"),
1546
+ /**
1547
+ * Timestamp of the routing decision
1548
+ */
1549
+ timestamp: z7.number().optional().describe("Timestamp of the decision")
1550
+ });
1551
+ var WorkerCapabilitiesSchema = z7.object({
1552
+ /**
1553
+ * Skills/capabilities the agent has
1554
+ */
1555
+ skills: z7.array(z7.string()).describe("List of agent skills"),
1556
+ /**
1557
+ * Tools available to the agent
1558
+ */
1559
+ tools: z7.array(z7.string()).describe("List of tool names available to agent"),
1560
+ /**
1561
+ * Whether the agent is currently available
1562
+ */
1563
+ available: z7.boolean().default(true).describe("Whether agent is available"),
1564
+ /**
1565
+ * Current workload (number of active tasks)
1566
+ */
1567
+ currentWorkload: z7.number().int().nonnegative().default(0).describe("Current number of active tasks")
1568
+ });
1569
+ var TaskAssignmentSchema = z7.object({
1570
+ /**
1571
+ * Unique assignment identifier
1572
+ */
1573
+ id: z7.string().describe("Unique assignment identifier"),
1574
+ /**
1575
+ * Worker ID assigned to the task
1576
+ */
1577
+ workerId: z7.string().describe("Worker identifier assigned to task"),
1578
+ /**
1579
+ * Task description
1580
+ */
1581
+ task: z7.string().describe("Description of the task"),
1582
+ /**
1583
+ * Task priority (1-10, higher is more urgent)
1584
+ */
1585
+ priority: z7.number().int().min(1).max(10).default(5).describe("Task priority"),
1586
+ /**
1587
+ * Timestamp when task was assigned
1588
+ */
1589
+ assignedAt: z7.number().describe("Timestamp when task was assigned"),
1590
+ /**
1591
+ * Optional deadline for task completion
1592
+ */
1593
+ deadline: z7.number().optional().describe("Optional task deadline timestamp")
1594
+ });
1595
+ var TaskResultSchema = z7.object({
1596
+ /**
1597
+ * Assignment identifier
1598
+ */
1599
+ assignmentId: z7.string().describe("Assignment identifier"),
1600
+ /**
1601
+ * Worker that completed the task
1602
+ */
1603
+ workerId: z7.string().describe("Worker that completed the task"),
1604
+ /**
1605
+ * Whether the task succeeded
1606
+ */
1607
+ success: z7.boolean().describe("Whether the task succeeded"),
1608
+ /**
1609
+ * Task result/output
1610
+ */
1611
+ result: z7.string().describe("Task result or output"),
1612
+ /**
1613
+ * Optional error message if task failed
1614
+ */
1615
+ error: z7.string().optional().describe("Error message if task failed"),
1616
+ /**
1617
+ * Timestamp when task was completed
1618
+ */
1619
+ completedAt: z7.number().describe("Timestamp when task was completed"),
1620
+ /**
1621
+ * Optional metadata about execution
1622
+ */
1623
+ metadata: z7.record(z7.any()).optional().describe("Execution metadata")
1624
+ });
1625
+ var MultiAgentStatusSchema = z7.enum([
1626
+ "initializing",
1627
+ // System is initializing
1628
+ "routing",
1629
+ // Supervisor is routing the task
1630
+ "executing",
1631
+ // Worker is executing the task
1632
+ "coordinating",
1633
+ // Multiple workers are coordinating
1634
+ "aggregating",
1635
+ // Results are being aggregated
1636
+ "completed",
1637
+ // Task is completed
1638
+ "failed"
1639
+ // Task failed
1640
+ ]);
1641
+ var HandoffRequestSchema = z7.object({
1642
+ /**
1643
+ * Agent requesting the handoff
1644
+ */
1645
+ from: z7.string().describe("Agent requesting handoff"),
1646
+ /**
1647
+ * Target agent for handoff
1648
+ */
1649
+ to: z7.string().describe("Target agent for handoff"),
1650
+ /**
1651
+ * Reason for handoff
1652
+ */
1653
+ reason: z7.string().describe("Reason for requesting handoff"),
1654
+ /**
1655
+ * Context to pass to next agent
1656
+ */
1657
+ context: z7.any().describe("Context to pass to next agent"),
1658
+ /**
1659
+ * Timestamp of handoff request
1660
+ */
1661
+ timestamp: z7.string().datetime().describe("ISO timestamp of handoff request")
1662
+ });
1663
+
1664
+ // src/multi-agent/state.ts
1665
+ var MultiAgentStateConfig = {
1666
+ /**
1667
+ * Original user input/query
1668
+ */
1669
+ input: {
1670
+ schema: z8.string(),
1671
+ default: () => "",
1672
+ description: "Original user input or query"
1673
+ },
1674
+ /**
1675
+ * All messages in the multi-agent conversation
1676
+ * Accumulates all messages between agents
1677
+ */
1678
+ messages: {
1679
+ schema: z8.array(AgentMessageSchema),
1680
+ reducer: (left, right) => [...left, ...right],
1681
+ default: () => [],
1682
+ description: "All messages in the multi-agent conversation"
1683
+ },
1684
+ /**
1685
+ * Available worker agents and their capabilities
1686
+ */
1687
+ workers: {
1688
+ schema: z8.record(z8.string(), WorkerCapabilitiesSchema),
1689
+ reducer: (left, right) => ({
1690
+ ...left,
1691
+ ...right
1692
+ }),
1693
+ default: () => ({}),
1694
+ description: "Available worker agents and their capabilities"
1695
+ },
1696
+ /**
1697
+ * Current active agent
1698
+ */
1699
+ currentAgent: {
1700
+ schema: z8.string().optional(),
1701
+ description: "Identifier of the currently active agent"
1702
+ },
1703
+ /**
1704
+ * Routing decisions made by the supervisor
1705
+ * Accumulates all routing decisions
1706
+ */
1707
+ routingHistory: {
1708
+ schema: z8.array(RoutingDecisionSchema),
1709
+ reducer: (left, right) => [...left, ...right],
1710
+ default: () => [],
1711
+ description: "History of routing decisions"
1712
+ },
1713
+ /**
1714
+ * Active task assignments
1715
+ */
1716
+ activeAssignments: {
1717
+ schema: z8.array(TaskAssignmentSchema),
1718
+ reducer: (left, right) => [...left, ...right],
1719
+ default: () => [],
1720
+ description: "Currently active task assignments"
1721
+ },
1722
+ /**
1723
+ * Completed task results
1724
+ * Accumulates all completed tasks
1725
+ */
1726
+ completedTasks: {
1727
+ schema: z8.array(TaskResultSchema),
1728
+ reducer: (left, right) => [...left, ...right],
1729
+ default: () => [],
1730
+ description: "Completed task results"
1731
+ },
1732
+ /**
1733
+ * Handoff requests between agents
1734
+ * Accumulates all handoff requests
1735
+ */
1736
+ handoffs: {
1737
+ schema: z8.array(HandoffRequestSchema),
1738
+ reducer: (left, right) => [...left, ...right],
1739
+ default: () => [],
1740
+ description: "Handoff requests between agents"
1741
+ },
1742
+ /**
1743
+ * Current execution status
1744
+ */
1745
+ status: {
1746
+ schema: MultiAgentStatusSchema,
1747
+ default: () => "initializing",
1748
+ description: "Current multi-agent execution status"
1749
+ },
1750
+ /**
1751
+ * Iteration counter
1752
+ */
1753
+ iteration: {
1754
+ schema: z8.number().int().nonnegative(),
1755
+ reducer: (left, right) => left + right,
1756
+ default: () => 0,
1757
+ description: "Current iteration number"
1758
+ },
1759
+ /**
1760
+ * Maximum iterations allowed
1761
+ */
1762
+ maxIterations: {
1763
+ schema: z8.number().int().positive(),
1764
+ default: () => 10,
1765
+ description: "Maximum number of iterations allowed"
1766
+ },
1767
+ /**
1768
+ * Final aggregated response
1769
+ */
1770
+ response: {
1771
+ schema: z8.string().optional(),
1772
+ description: "Final aggregated response"
1773
+ },
1774
+ /**
1775
+ * Error message if execution failed
1776
+ */
1777
+ error: {
1778
+ schema: z8.string().optional(),
1779
+ description: "Error message if execution failed"
1780
+ }
1781
+ };
1782
+ var MultiAgentState = createStateAnnotation4(MultiAgentStateConfig);
1783
+
1784
+ // src/multi-agent/routing.ts
1785
+ import { HumanMessage as HumanMessage4, SystemMessage as SystemMessage4 } from "@langchain/core/messages";
1786
+ var DEFAULT_SUPERVISOR_SYSTEM_PROMPT = `You are a supervisor agent responsible for routing tasks to specialized worker agents.
1787
+
1788
+ Your job is to:
1789
+ 1. Analyze the current task and context
1790
+ 2. Review available worker capabilities
1791
+ 3. Select the most appropriate worker for the task
1792
+ 4. Provide clear reasoning for your decision
1793
+
1794
+ Respond with a JSON object containing:
1795
+ {
1796
+ "targetAgent": "worker_id",
1797
+ "reasoning": "explanation of why this worker is best suited",
1798
+ "confidence": 0.0-1.0,
1799
+ "strategy": "llm-based"
1800
+ }`;
1801
+ var llmBasedRouting = {
1802
+ name: "llm-based",
1803
+ async route(state, config) {
1804
+ if (!config.llm) {
1805
+ throw new Error("LLM-based routing requires an LLM to be configured");
1806
+ }
1807
+ const systemPrompt = config.systemPrompt || DEFAULT_SUPERVISOR_SYSTEM_PROMPT;
1808
+ const workerInfo = Object.entries(state.workers).map(([id, caps]) => {
1809
+ const skills = caps.skills.join(", ");
1810
+ const tools = caps.tools.join(", ");
1811
+ const available = caps.available ? "available" : "busy";
1812
+ return `- ${id}: Skills: [${skills}], Tools: [${tools}], Status: ${available}, Workload: ${caps.currentWorkload}`;
1813
+ }).join("\n");
1814
+ const lastMessage = state.messages[state.messages.length - 1];
1815
+ const taskContext = lastMessage?.content || state.input;
1816
+ const userPrompt = `Current task: ${taskContext}
1817
+
1818
+ Available workers:
1819
+ ${workerInfo}
1820
+
1821
+ Select the best worker for this task and explain your reasoning.`;
1822
+ const messages = [
1823
+ new SystemMessage4(systemPrompt),
1824
+ new HumanMessage4(userPrompt)
1825
+ ];
1826
+ const response = await config.llm.invoke(messages);
1827
+ const content = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
1828
+ try {
1829
+ const decision = JSON.parse(content);
1830
+ return {
1831
+ targetAgent: decision.targetAgent,
1832
+ reasoning: decision.reasoning,
1833
+ confidence: decision.confidence,
1834
+ strategy: "llm-based",
1835
+ timestamp: Date.now()
1836
+ };
1837
+ } catch (error) {
1838
+ throw new Error(`Failed to parse routing decision from LLM: ${error}`);
1839
+ }
1840
+ }
1841
+ };
1842
+ var roundRobinRouting = {
1843
+ name: "round-robin",
1844
+ async route(state, config) {
1845
+ const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id]) => id);
1846
+ if (availableWorkers.length === 0) {
1847
+ throw new Error("No available workers for round-robin routing");
1848
+ }
1849
+ const lastRoutingIndex = state.routingHistory.length % availableWorkers.length;
1850
+ const targetAgent = availableWorkers[lastRoutingIndex];
1851
+ return {
1852
+ targetAgent,
1853
+ reasoning: `Round-robin selection: worker ${lastRoutingIndex + 1} of ${availableWorkers.length}`,
1854
+ confidence: 1,
1855
+ strategy: "round-robin",
1856
+ timestamp: Date.now()
1857
+ };
1858
+ }
1859
+ };
1860
+ var skillBasedRouting = {
1861
+ name: "skill-based",
1862
+ async route(state, config) {
1863
+ const lastMessage = state.messages[state.messages.length - 1];
1864
+ const taskContent = (lastMessage?.content || state.input).toLowerCase();
1865
+ const workerScores = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => {
1866
+ const skillMatches = caps.skills.filter(
1867
+ (skill) => taskContent.includes(skill.toLowerCase())
1868
+ ).length;
1869
+ const toolMatches = caps.tools.filter(
1870
+ (tool) => taskContent.includes(tool.toLowerCase())
1871
+ ).length;
1872
+ const score = skillMatches * 2 + toolMatches;
1873
+ return { id, score, skills: caps.skills, tools: caps.tools };
1874
+ }).filter((w) => w.score > 0).sort((a, b) => b.score - a.score);
1875
+ if (workerScores.length === 0) {
1876
+ const firstAvailable = Object.entries(state.workers).find(([_, caps]) => caps.available);
1877
+ if (!firstAvailable) {
1878
+ throw new Error("No available workers for skill-based routing");
1879
+ }
1880
+ return {
1881
+ targetAgent: firstAvailable[0],
1882
+ reasoning: "No skill matches found, using first available worker",
1883
+ confidence: 0.5,
1884
+ strategy: "skill-based",
1885
+ timestamp: Date.now()
1886
+ };
1887
+ }
1888
+ const best = workerScores[0];
1889
+ const confidence = Math.min(best.score / 5, 1);
1890
+ return {
1891
+ targetAgent: best.id,
1892
+ reasoning: `Best skill match with score ${best.score} (skills: ${best.skills.join(", ")})`,
1893
+ confidence,
1894
+ strategy: "skill-based",
1895
+ timestamp: Date.now()
1896
+ };
1897
+ }
1898
+ };
1899
+ var loadBalancedRouting = {
1900
+ name: "load-balanced",
1901
+ async route(state, config) {
1902
+ const availableWorkers = Object.entries(state.workers).filter(([_, caps]) => caps.available).map(([id, caps]) => ({ id, workload: caps.currentWorkload })).sort((a, b) => a.workload - b.workload);
1903
+ if (availableWorkers.length === 0) {
1904
+ throw new Error("No available workers for load-balanced routing");
1905
+ }
1906
+ const targetWorker = availableWorkers[0];
1907
+ const avgWorkload = availableWorkers.reduce((sum, w) => sum + w.workload, 0) / availableWorkers.length;
1908
+ const confidence = targetWorker.workload === 0 ? 1 : Math.max(0.5, 1 - targetWorker.workload / (avgWorkload * 2));
1909
+ return {
1910
+ targetAgent: targetWorker.id,
1911
+ reasoning: `Lowest workload: ${targetWorker.workload} tasks (avg: ${avgWorkload.toFixed(1)})`,
1912
+ confidence,
1913
+ strategy: "load-balanced",
1914
+ timestamp: Date.now()
1915
+ };
1916
+ }
1917
+ };
1918
+ var ruleBasedRouting = {
1919
+ name: "rule-based",
1920
+ async route(state, config) {
1921
+ if (!config.routingFn) {
1922
+ throw new Error("Rule-based routing requires a custom routing function");
1923
+ }
1924
+ return await config.routingFn(state);
1925
+ }
1926
+ };
1927
+ function getRoutingStrategy(name) {
1928
+ switch (name) {
1929
+ case "llm-based":
1930
+ return llmBasedRouting;
1931
+ case "round-robin":
1932
+ return roundRobinRouting;
1933
+ case "skill-based":
1934
+ return skillBasedRouting;
1935
+ case "load-balanced":
1936
+ return loadBalancedRouting;
1937
+ case "rule-based":
1938
+ return ruleBasedRouting;
1939
+ default:
1940
+ throw new Error(`Unknown routing strategy: ${name}`);
1941
+ }
1942
+ }
1943
+
1944
+ // src/multi-agent/nodes.ts
1945
+ import { HumanMessage as HumanMessage5, SystemMessage as SystemMessage5 } from "@langchain/core/messages";
1946
+ import { toLangChainTools as toLangChainTools2 } from "@agentforge/core";
1947
+ var DEFAULT_AGGREGATOR_SYSTEM_PROMPT = `You are an aggregator agent responsible for combining results from multiple worker agents.
1948
+
1949
+ Your job is to:
1950
+ 1. Review all completed task results
1951
+ 2. Synthesize the information into a coherent response
1952
+ 3. Ensure all aspects of the original query are addressed
1953
+ 4. Provide a clear, comprehensive final answer
1954
+
1955
+ Be concise but thorough in your aggregation.`;
1956
+ function createSupervisorNode(config) {
1957
+ const {
1958
+ strategy,
1959
+ verbose = false,
1960
+ maxIterations = 10
1961
+ } = config;
1962
+ return async (state) => {
1963
+ try {
1964
+ if (verbose) {
1965
+ console.log(`[Supervisor] Routing iteration ${state.iteration}/${maxIterations}`);
1966
+ }
1967
+ if (state.iteration >= maxIterations) {
1968
+ if (verbose) {
1969
+ console.log("[Supervisor] Max iterations reached, moving to aggregation");
1970
+ }
1971
+ return {
1972
+ status: "aggregating",
1973
+ currentAgent: "aggregator"
1974
+ };
1975
+ }
1976
+ const allCompleted = state.activeAssignments.every(
1977
+ (assignment2) => state.completedTasks.some((task) => task.assignmentId === assignment2.id)
1978
+ );
1979
+ if (allCompleted && state.activeAssignments.length > 0) {
1980
+ if (verbose) {
1981
+ console.log("[Supervisor] All tasks completed, moving to aggregation");
1982
+ }
1983
+ return {
1984
+ status: "aggregating",
1985
+ currentAgent: "aggregator"
1986
+ };
1987
+ }
1988
+ const routingImpl = getRoutingStrategy(strategy);
1989
+ const decision = await routingImpl.route(state, config);
1990
+ if (verbose) {
1991
+ console.log(`[Supervisor] Routing to ${decision.targetAgent}: ${decision.reasoning}`);
1992
+ }
1993
+ const assignment = {
1994
+ id: `task_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
1995
+ workerId: decision.targetAgent,
1996
+ task: state.messages[state.messages.length - 1]?.content || state.input,
1997
+ priority: 5,
1998
+ assignedAt: Date.now()
1999
+ };
2000
+ const message = {
2001
+ id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2002
+ from: "supervisor",
2003
+ to: [decision.targetAgent],
2004
+ type: "task_assignment",
2005
+ content: assignment.task,
2006
+ timestamp: Date.now(),
2007
+ metadata: {
2008
+ assignmentId: assignment.id,
2009
+ priority: assignment.priority
2010
+ }
2011
+ };
2012
+ return {
2013
+ currentAgent: decision.targetAgent,
2014
+ status: "executing",
2015
+ routingHistory: [decision],
2016
+ activeAssignments: [assignment],
2017
+ messages: [message],
2018
+ iteration: state.iteration + 1
2019
+ };
2020
+ } catch (error) {
2021
+ console.error("[Supervisor] Error:", error);
2022
+ return {
2023
+ status: "failed",
2024
+ error: error instanceof Error ? error.message : "Unknown error in supervisor"
2025
+ };
2026
+ }
2027
+ };
2028
+ }
2029
+ function createWorkerNode(config) {
2030
+ const {
2031
+ id,
2032
+ capabilities,
2033
+ llm,
2034
+ tools = [],
2035
+ systemPrompt,
2036
+ verbose = false,
2037
+ executeFn
2038
+ } = config;
2039
+ return async (state) => {
2040
+ try {
2041
+ if (verbose) {
2042
+ console.log(`[Worker:${id}] Executing task`);
2043
+ }
2044
+ const currentAssignment = state.activeAssignments.find(
2045
+ (assignment) => assignment.workerId === id && !state.completedTasks.some((task) => task.assignmentId === assignment.id)
2046
+ );
2047
+ if (!currentAssignment) {
2048
+ if (verbose) {
2049
+ console.log(`[Worker:${id}] No active assignment found`);
2050
+ }
2051
+ return {
2052
+ currentAgent: "supervisor",
2053
+ status: "routing"
2054
+ };
2055
+ }
2056
+ if (executeFn) {
2057
+ return await executeFn(state);
2058
+ }
2059
+ if (!llm) {
2060
+ throw new Error(`Worker ${id} requires either an LLM or custom execution function`);
2061
+ }
2062
+ const defaultSystemPrompt = `You are a specialized worker agent with the following capabilities:
2063
+ Skills: ${capabilities.skills.join(", ")}
2064
+ Tools: ${capabilities.tools.join(", ")}
2065
+
2066
+ Execute the assigned task using your skills and tools. Provide a clear, actionable result.`;
2067
+ const messages = [
2068
+ new SystemMessage5(systemPrompt || defaultSystemPrompt),
2069
+ new HumanMessage5(currentAssignment.task)
2070
+ ];
2071
+ let llmToUse = llm;
2072
+ if (tools.length > 0 && llm.bindTools) {
2073
+ const langchainTools = toLangChainTools2(tools);
2074
+ llmToUse = llm.bindTools(langchainTools);
2075
+ }
2076
+ const response = await llmToUse.invoke(messages);
2077
+ const result = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2078
+ if (verbose) {
2079
+ console.log(`[Worker:${id}] Task completed:`, result.substring(0, 100) + "...");
2080
+ }
2081
+ const taskResult = {
2082
+ assignmentId: currentAssignment.id,
2083
+ workerId: id,
2084
+ success: true,
2085
+ result,
2086
+ completedAt: Date.now(),
2087
+ metadata: {
2088
+ skills_used: capabilities.skills
2089
+ }
2090
+ };
2091
+ const message = {
2092
+ id: `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
2093
+ from: id,
2094
+ to: ["supervisor"],
2095
+ type: "task_result",
2096
+ content: result,
2097
+ timestamp: Date.now(),
2098
+ metadata: {
2099
+ assignmentId: currentAssignment.id,
2100
+ success: true
2101
+ }
2102
+ };
2103
+ const updatedWorkers = {
2104
+ ...state.workers,
2105
+ [id]: {
2106
+ ...capabilities,
2107
+ currentWorkload: Math.max(0, capabilities.currentWorkload - 1)
2108
+ }
2109
+ };
2110
+ return {
2111
+ completedTasks: [taskResult],
2112
+ messages: [message],
2113
+ workers: updatedWorkers,
2114
+ currentAgent: "supervisor",
2115
+ status: "routing"
2116
+ };
2117
+ } catch (error) {
2118
+ console.error(`[Worker:${id}] Error:`, error);
2119
+ const currentAssignment = state.activeAssignments.find(
2120
+ (assignment) => assignment.workerId === id
2121
+ );
2122
+ if (currentAssignment) {
2123
+ const errorResult = {
2124
+ assignmentId: currentAssignment.id,
2125
+ workerId: id,
2126
+ success: false,
2127
+ result: "",
2128
+ error: error instanceof Error ? error.message : "Unknown error",
2129
+ completedAt: Date.now()
2130
+ };
2131
+ return {
2132
+ completedTasks: [errorResult],
2133
+ currentAgent: "supervisor",
2134
+ status: "routing"
2135
+ };
2136
+ }
2137
+ return {
2138
+ status: "failed",
2139
+ error: error instanceof Error ? error.message : `Unknown error in worker ${id}`
2140
+ };
2141
+ }
2142
+ };
2143
+ }
2144
+ function createAggregatorNode(config = {}) {
2145
+ const {
2146
+ llm,
2147
+ systemPrompt = DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
2148
+ aggregateFn,
2149
+ verbose = false
2150
+ } = config;
2151
+ return async (state) => {
2152
+ try {
2153
+ if (verbose) {
2154
+ console.log("[Aggregator] Combining results from workers");
2155
+ }
2156
+ if (aggregateFn) {
2157
+ const response2 = await aggregateFn(state);
2158
+ return {
2159
+ response: response2,
2160
+ status: "completed"
2161
+ };
2162
+ }
2163
+ if (state.completedTasks.length === 0) {
2164
+ return {
2165
+ response: "No tasks were completed.",
2166
+ status: "completed"
2167
+ };
2168
+ }
2169
+ if (!llm) {
2170
+ const combinedResults = state.completedTasks.filter((task) => task.success).map((task) => task.result).join("\n\n");
2171
+ return {
2172
+ response: combinedResults || "No successful results to aggregate.",
2173
+ status: "completed"
2174
+ };
2175
+ }
2176
+ const taskResults = state.completedTasks.map((task, idx) => {
2177
+ const status = task.success ? "\u2713" : "\u2717";
2178
+ const result = task.success ? task.result : `Error: ${task.error}`;
2179
+ return `${idx + 1}. [${status}] Worker ${task.workerId}:
2180
+ ${result}`;
2181
+ }).join("\n\n");
2182
+ const userPrompt = `Original query: ${state.input}
2183
+
2184
+ Worker results:
2185
+ ${taskResults}
2186
+
2187
+ Please synthesize these results into a comprehensive response that addresses the original query.`;
2188
+ const messages = [
2189
+ new SystemMessage5(systemPrompt),
2190
+ new HumanMessage5(userPrompt)
2191
+ ];
2192
+ const response = await llm.invoke(messages);
2193
+ const aggregatedResponse = typeof response.content === "string" ? response.content : JSON.stringify(response.content);
2194
+ if (verbose) {
2195
+ console.log("[Aggregator] Aggregation complete");
2196
+ }
2197
+ return {
2198
+ response: aggregatedResponse,
2199
+ status: "completed"
2200
+ };
2201
+ } catch (error) {
2202
+ console.error("[Aggregator] Error:", error);
2203
+ return {
2204
+ status: "failed",
2205
+ error: error instanceof Error ? error.message : "Unknown error in aggregator"
2206
+ };
2207
+ }
2208
+ };
2209
+ }
2210
+
2211
+ // src/multi-agent/agent.ts
2212
+ import { StateGraph as StateGraph4, END as END4 } from "@langchain/langgraph";
2213
+ function createMultiAgentSystem(config) {
2214
+ const {
2215
+ supervisor,
2216
+ workers,
2217
+ aggregator,
2218
+ maxIterations = 10,
2219
+ verbose = false
2220
+ } = config;
2221
+ const workflow = new StateGraph4(MultiAgentState);
2222
+ const supervisorNode = createSupervisorNode({
2223
+ ...supervisor,
2224
+ maxIterations,
2225
+ verbose
2226
+ });
2227
+ workflow.addNode("supervisor", supervisorNode);
2228
+ const workerIds = [];
2229
+ const workerCapabilities = {};
2230
+ for (const workerConfig of workers) {
2231
+ const workerNode = createWorkerNode({
2232
+ ...workerConfig,
2233
+ verbose
2234
+ });
2235
+ workflow.addNode(workerConfig.id, workerNode);
2236
+ workerIds.push(workerConfig.id);
2237
+ workerCapabilities[workerConfig.id] = workerConfig.capabilities;
2238
+ }
2239
+ const aggregatorNode = createAggregatorNode({
2240
+ ...aggregator,
2241
+ verbose
2242
+ });
2243
+ workflow.addNode("aggregator", aggregatorNode);
2244
+ const supervisorRouter = (state) => {
2245
+ if (state.status === "completed" || state.status === "failed") {
2246
+ return END4;
2247
+ }
2248
+ if (state.status === "aggregating") {
2249
+ return "aggregator";
2250
+ }
2251
+ if (state.currentAgent && state.currentAgent !== "supervisor") {
2252
+ return state.currentAgent;
2253
+ }
2254
+ return "supervisor";
2255
+ };
2256
+ const workerRouter = (state) => {
2257
+ return "supervisor";
2258
+ };
2259
+ const aggregatorRouter = (state) => {
2260
+ return END4;
2261
+ };
2262
+ workflow.setEntryPoint("supervisor");
2263
+ workflow.addConditionalEdges("supervisor", supervisorRouter, [
2264
+ "aggregator",
2265
+ END4,
2266
+ ...workerIds
2267
+ ]);
2268
+ for (const workerId of workerIds) {
2269
+ workflow.addConditionalEdges(workerId, workerRouter, ["supervisor"]);
2270
+ }
2271
+ workflow.addConditionalEdges("aggregator", aggregatorRouter, [END4]);
2272
+ const compiled = workflow.compile();
2273
+ const originalInvoke = compiled.invoke.bind(compiled);
2274
+ compiled.invoke = async function(input, config2) {
2275
+ const mergedInput = {
2276
+ ...input,
2277
+ workers: {
2278
+ ...workerCapabilities,
2279
+ ...input.workers || {}
2280
+ }
2281
+ };
2282
+ return originalInvoke(mergedInput, config2);
2283
+ };
2284
+ return compiled;
2285
+ }
2286
+ var MultiAgentSystemBuilder = class {
2287
+ config;
2288
+ additionalWorkers = [];
2289
+ compiled = false;
2290
+ constructor(config) {
2291
+ this.config = {
2292
+ ...config,
2293
+ workers: config.workers || []
2294
+ };
2295
+ }
2296
+ /**
2297
+ * Register workers with the system builder
2298
+ *
2299
+ * @param workers - Array of worker configurations
2300
+ * @returns this builder for chaining
2301
+ *
2302
+ * @example
2303
+ * ```typescript
2304
+ * const builder = new MultiAgentSystemBuilder({
2305
+ * supervisor: { llm, strategy: 'skill-based' },
2306
+ * aggregator: { llm },
2307
+ * });
2308
+ *
2309
+ * builder.registerWorkers([
2310
+ * {
2311
+ * name: 'math_worker',
2312
+ * capabilities: ['math', 'calculations'],
2313
+ * tools: [calculatorTool],
2314
+ * },
2315
+ * ]);
2316
+ *
2317
+ * const system = builder.build();
2318
+ * ```
2319
+ */
2320
+ registerWorkers(workers) {
2321
+ if (this.compiled) {
2322
+ throw new Error("Cannot register workers after the system has been compiled");
2323
+ }
2324
+ for (const worker of workers) {
2325
+ this.additionalWorkers.push({
2326
+ id: worker.name,
2327
+ capabilities: {
2328
+ skills: worker.capabilities,
2329
+ tools: worker.tools?.map((t) => t.name || "unknown") || [],
2330
+ available: true,
2331
+ currentWorkload: 0
2332
+ },
2333
+ llm: worker.llm || this.config.supervisor.llm,
2334
+ tools: worker.tools,
2335
+ systemPrompt: worker.systemPrompt
2336
+ });
2337
+ }
2338
+ return this;
2339
+ }
2340
+ /**
2341
+ * Build and compile the multi-agent system
2342
+ *
2343
+ * @returns Compiled LangGraph workflow
2344
+ */
2345
+ build() {
2346
+ if (this.compiled) {
2347
+ throw new Error("System has already been compiled");
2348
+ }
2349
+ const allWorkers = [...this.config.workers, ...this.additionalWorkers];
2350
+ if (allWorkers.length === 0) {
2351
+ throw new Error("At least one worker must be registered before building the system");
2352
+ }
2353
+ this.compiled = true;
2354
+ return createMultiAgentSystem({
2355
+ ...this.config,
2356
+ workers: allWorkers
2357
+ });
2358
+ }
2359
+ };
2360
+ function registerWorkers(system, workers) {
2361
+ console.warn(
2362
+ "[AgentForge] registerWorkers() on a compiled system only updates worker capabilities in state.\nIt does NOT add worker nodes to the graph. Use MultiAgentSystemBuilder for proper worker registration.\nSee: https://github.com/agentforge/agentforge/blob/main/packages/patterns/docs/multi-agent-pattern.md"
2363
+ );
2364
+ if (!system._workerRegistry) {
2365
+ system._workerRegistry = {};
2366
+ }
2367
+ for (const worker of workers) {
2368
+ system._workerRegistry[worker.name] = {
2369
+ skills: worker.capabilities,
2370
+ tools: worker.tools?.map((t) => t.name || "unknown") || [],
2371
+ available: true,
2372
+ currentWorkload: 0
2373
+ };
2374
+ }
2375
+ if (!system._originalInvoke) {
2376
+ system._originalInvoke = system.invoke.bind(system);
2377
+ system.invoke = async function(input, config) {
2378
+ const mergedInput = {
2379
+ ...input,
2380
+ workers: {
2381
+ ...system._workerRegistry || {},
2382
+ ...input.workers || {}
2383
+ }
2384
+ };
2385
+ return system._originalInvoke(mergedInput, config);
2386
+ };
2387
+ }
2388
+ }
2389
+ export {
2390
+ AgentMessageSchema,
2391
+ AgentRoleSchema,
2392
+ CompletedStepSchema,
2393
+ DEFAULT_AGGREGATOR_SYSTEM_PROMPT,
2394
+ DEFAULT_GENERATOR_SYSTEM_PROMPT,
2395
+ DEFAULT_PLANNER_SYSTEM_PROMPT,
2396
+ DEFAULT_REACT_SYSTEM_PROMPT,
2397
+ DEFAULT_REFLECTOR_SYSTEM_PROMPT,
2398
+ DEFAULT_REPLANNER_SYSTEM_PROMPT,
2399
+ DEFAULT_REVISER_SYSTEM_PROMPT,
2400
+ DEFAULT_SUPERVISOR_SYSTEM_PROMPT,
2401
+ ExecutionStatusSchema,
2402
+ GENERATION_PROMPT_TEMPLATE,
2403
+ HandoffRequestSchema,
2404
+ MessageSchema,
2405
+ MessageTypeSchema,
2406
+ MultiAgentState,
2407
+ MultiAgentStateConfig,
2408
+ MultiAgentStatusSchema,
2409
+ MultiAgentSystemBuilder,
2410
+ PLANNING_PROMPT_TEMPLATE,
2411
+ PlanExecuteState,
2412
+ PlanExecuteStateConfig,
2413
+ PlanSchema,
2414
+ PlanStepSchema,
2415
+ QUALITY_CRITERIA_TEMPLATE,
2416
+ QualityCriteriaSchema,
2417
+ REFLECTION_ENTRY_TEMPLATE,
2418
+ REFLECTION_HISTORY_TEMPLATE,
2419
+ REFLECTION_PROMPT_TEMPLATE,
2420
+ REPLANNING_PROMPT_TEMPLATE,
2421
+ REVISION_ENTRY_TEMPLATE,
2422
+ REVISION_PROMPT_TEMPLATE,
2423
+ ReActAgentBuilder,
2424
+ ReActState,
2425
+ ReflectionConfigSchema,
2426
+ ReflectionSchema,
2427
+ ReflectionState,
2428
+ ReflectionStateConfig,
2429
+ ReflectionStatusSchema,
2430
+ ReplanDecisionSchema,
2431
+ RevisionSchema,
2432
+ RoutingDecisionSchema,
2433
+ RoutingStrategySchema,
2434
+ ScratchpadEntrySchema,
2435
+ TaskAssignmentSchema,
2436
+ TaskResultSchema,
2437
+ ThoughtSchema,
2438
+ ToolCallSchema,
2439
+ ToolResultSchema,
2440
+ WorkerCapabilitiesSchema,
2441
+ createAggregatorNode,
2442
+ createExecutorNode,
2443
+ createFinisherNode,
2444
+ createGeneratorNode,
2445
+ createMultiAgentSystem,
2446
+ createPlanExecuteAgent,
2447
+ createPlannerNode,
2448
+ createReActAgent,
2449
+ createReActAgentBuilder,
2450
+ createReflectionAgent,
2451
+ createFinisherNode2 as createReflectionFinisherNode,
2452
+ createReflectorNode,
2453
+ createReplannerNode,
2454
+ createReviserNode,
2455
+ createSupervisorNode,
2456
+ createWorkerNode,
2457
+ getRoutingStrategy,
2458
+ llmBasedRouting,
2459
+ loadBalancedRouting,
2460
+ registerWorkers,
2461
+ roundRobinRouting,
2462
+ ruleBasedRouting,
2463
+ skillBasedRouting
2464
+ };