
Product
Announcing Socket Fix 2.0
Socket Fix 2.0 brings targeted CVE remediation, smarter upgrade planning, and broader ecosystem support to help developers get to zero alerts.
directed-agent
Advanced tools
A powerful TypeScript library for building conversational AI flows and state machines using graph-based structures. Create complex agent interactions with nodes, edges, and seamless traversal capabilities.
npm install directed-agent
import { Graph, AgentGraphNode, GraphTraverser } from "directed-agent";
// Define a custom send node
class GreetingNode_Send extends AgentGraphNode {
async onEnter(context: FormContext) {
return {
messages: [{ text: "Hello! How can I help you today?" }],
nextNode: "greeting_receive",
};
}
async getNextNodes(context: FormContext) {
return ["greeting_receive"];
}
}
// Define a custom receive node
class GreetingNode_Receive extends AgentGraphNode {
async onEnter(context: FormContext) {
const messages = context.messages;
const userResponse = messages[messages.length - 1]?.content || "nothing";
return {
messages: [{ text: `Thanks for telling me about: ${userResponse}` }],
nextNode: "greeting_send", // Loop back
};
}
async getNextNodes(context: FormContext) {
return ["greeting_send"];
}
}
const graphRepresentation = {
greeting_send: new GreetingNode_Send(),
greeting_receive: new GreetingNode_Receive(),
};
// Create and populate the graph
const graph = Graph.fromAgentNodes(graphRepresentation);
// Set up traversal
const traverser = new GraphTraverser({
graph,
sendMessage: async (message) => console.log(message),
updateState: async (state) => console.log(`State: ${state}`),
getContext: async () => ({
messages: [],
formData: {},
setFormData: () => {},
}),
});
// Execute the flow
await traverser.execute("greeting_send");
The library uses a directed graph structure where:
Agent Graph uses a convention-based approach for managing conversation flow with two types of states:
_send
)greeting_send
, welcome_send
, process_order_send
_receive
)greeting_receive
, get_name_receive
, collect_address_receive
class WelcomeNode_Send extends AgentGraphNode {
async onEnter(context) {
return {
messages: [{ text: "Welcome! Please enter your name:" }],
nextNode: "welcome_receive", // Transitions to receive state
};
}
}
class WelcomeNode_Receive extends AgentGraphNode {
async onEnter(context) {
const messages = context.messages;
const userName = messages[messages.length - 1]?.content || "Guest";
return {
messages: [{ text: `Hello ${userName}! How can I help you?` }],
nextNode: "main_menu_send", // Continue the flow
};
}
}
This pattern ensures smooth conversation flow where:
The base class for all nodes in your graph. Implement custom logic by extending this class:
class CustomNode extends AgentGraphNode {
async onEnter(context: FormContext): Promise<{
messages?: { text: string }[];
nextNode: string;
}> {
// Your custom logic here
return {
messages: [{ text: "Custom message" }],
nextNode: "next-node-id",
};
}
async getNextNodes(context: FormContext): Promise<string[]> {
// Return possible next nodes
return ["next-node-id"];
}
}
const graph = new Graph();
// Add nodes
graph.addNode({ id: "node1", node: new CustomNode(), data: {} });
graph.addNode({ id: "node2", node: new CustomNode(), data: {} });
// Add edges
graph.addEdge({ id: "edge1", source: "node1", target: "node2" });
// Query the graph
const node = graph.getNode("node1");
const connectedNodes = graph.getConnectedNodes("node1");
const hasPath = graph.hasPath("node1", "node2");
Generate visual representations of your graphs:
import { GraphVisualizer } from "directed-agent";
const visualizer = new GraphVisualizer(graph);
// Generate Mermaid diagram
const mermaid = visualizer.toMermaid();
console.log(mermaid);
// Generate DOT graph (for Graphviz)
const dot = visualizer.toDOT();
// Generate ASCII representation
const ascii = visualizer.toASCII();
// Export as JSON
const json = visualizer.toJSON();
Customize the appearance of your visualizations:
const visualizer = new GraphVisualizer(graph, {
nodeSize: 25,
nodeColor: "#4f46e5",
edgeColor: "#6b7280",
fontSize: 14,
width: 1000,
height: 800,
});
The TerminalTraverser
class provides an easy way to test your conversational agents directly from your terminal. It's perfect for rapid prototyping and debugging your agent flows:
import { TerminalTraverser, Graph, AgentGraphNode } from "../src/";
// Simple greeting node
class GreetingNode_Send extends AgentGraphNode {
async onEnter(context) {
return {
messages: [{ text: "Hello! What's your name?" }],
nextNode: "greeting_receive",
};
}
async getNextNodes() {
return ["greeting_receive"];
}
}
// Simple response node
class GreetingNode_Receive extends AgentGraphNode {
async onEnter(context) {
const messages = context.messages;
const lastMessage =
messages.length > 0 ? messages[messages.length - 1] : null;
const name = lastMessage?.content || "friend";
return {
messages: [{ text: `Nice to meet you, ${name}!` }],
nextNode: "greeting_send", // Loop back
};
}
async getNextNodes() {
return ["greeting_send"];
}
}
async function runDemo() {
// Create graph from service definition
const serviceGraph = {
greeting_send: new GreetingNode_Send(),
greeting_receive: new GreetingNode_Receive(),
};
const graph = await Graph.fromAgentNodes(serviceGraph);
const terminal = new TerminalTraverser({
graph,
initialState: "greeting_send",
});
await terminal.start();
}
// Run the demo
runDemo().catch(console.error);
The TerminalTraverser
:
This makes it incredibly easy to test complex conversation flows without building a full UI, allowing you to focus on perfecting your agent's logic and responses.
Create specialized traversers for different environments:
import { GraphTraverser, GraphTraverserOptions } from "directed-agent";
class TerminalTraverser extends GraphTraverser {
constructor(
options: GraphTraverserOptions & {
initialState: string;
initialContext: any;
}
) {
super({
...options,
sendMessage: async (message) => {
console.log(`๐ค ${message}`);
},
updateState: async (state) => {
this.currentState = state;
},
getContext: async () => this.context,
});
}
async start() {
await this.execute(this.initialState);
}
}
Track visited states and conversation flow:
const traverser = new GraphTraverser({
graph,
sendMessage: async (message) => console.log(message),
updateState: async (state) => await saveState(state),
getContext: async () => await loadContext(),
steps: {
getVisited: async () => await loadVisitedSteps(),
addVisited: (step) => saveVisitedStep(step),
},
});
Export and import graph structures:
// Export graph to JSON
const graphData = graph.toJSON();
// Create graph from service definition
const serviceGraph = {
node1: new GreetingNode(),
node2: new QuestionNode(),
node3: new ResponseNode(),
};
const newGraph = await Graph.fromAgentNodes(serviceGraph);
class IntroNode_Send extends AgentGraphNode {
async onEnter(context: FormContext) {
return {
messages: [
{
text: "๐ Welcome to Tony's Pizza! What would you like to order?",
},
],
nextNode: "intro_receive",
};
}
}
class IntroNode_Receive extends AgentGraphNode {
async onEnter(context: FormContext) {
const messages = context.messages;
const order = messages[messages.length - 1]?.content || "pizza";
return {
messages: [
{
text: `Great choice! You want ${order}. What's your address?`,
},
],
nextNode: "address_receive",
};
}
}
class AddressNode_Receive extends AgentGraphNode {
async onEnter(context: FormContext) {
const messages = context.messages;
const address = messages[messages.length - 1]?.content || "unknown address";
return {
messages: [
{
text: `Perfect! We'll deliver to ${address}. Your order is confirmed!`,
},
],
nextNode: "intro_send", // Start over
};
}
}
// Build the graph
const graph = new Graph();
graph.addNode({ id: "intro_send", node: new IntroNode_Send(), data: {} });
graph.addNode({ id: "intro_receive", node: new IntroNode_Receive(), data: {} });
graph.addNode({
id: "address_receive",
node: new AddressNode_Receive(),
data: {},
});
The library includes comprehensive test utilities:
import { TestNode } from "directed-agent/test-utils";
// Use TestNode for easy testing
const testNode = new TestNode("next-node", [{ text: "Test message" }]);
const result = await testNode.onEnter({});
expect(result.nextNode).toBe("next-node");
expect(result.messages).toEqual([{ text: "Test message" }]);
addNode(node: AgentNode): void
- Add a node to the graphaddEdge(edge: AgentEdge): void
- Add an edge to the graphgetNode(id: string): AgentNode | undefined
- Get node by IDgetConnectedNodes(nodeId: string): AgentNode[]
- Get connected nodeshasPath(source: string, target: string): boolean
- Check if path existstoJSON(): AgentGraph
- Export graph structureonEnter(context: FormContext): Promise<{ messages?, nextNode }>
- Execute node logicgetNextNodes(context: FormContext): Promise<string[]>
- Get possible next nodestoMermaid(): string
- Generate Mermaid diagramtoDOT(): string
FAQs
A TypeScript library for agent graph functionality
We found that directed-agent demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.ย It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Socket Fix 2.0 brings targeted CVE remediation, smarter upgrade planning, and broader ecosystem support to help developers get to zero alerts.
Security News
Socket CEO Feross Aboukhadijeh joins Risky Business Weekly to unpack recent npm phishing attacks, their limited impact, and the risks if attackers get smarter.
Product
Socketโs new Tier 1 Reachability filters out up to 80% of irrelevant CVEs, so security teams can focus on the vulnerabilities that matter.