# Edges

Edges are the **routing logic** that connects nodes in your MindedJS flows. They determine how your agent moves through the workflow, making intelligent decisions about which path to take based on different conditions and contexts.

## What are Edges?

Edges define the **transitions between nodes** in your flow. Each edge specifies:

* **Source** - The starting node
* **Target** - The destination node
* **Type** - The routing logic type
* **Condition** - The criteria for taking this path
* **Prompt** - The prompt for the LLM to make a decision

## Edge Types

MindedJS provides three types of edges for different routing scenarios:

| Edge Type                                         | Purpose       | When to Use                                      |
| ------------------------------------------------- | ------------- | ------------------------------------------------ |
| [**Step Forward**](#step-forward-edges)           | Unconditional | Sequential processing, guaranteed transitions    |
| [**Prompt Condition**](#prompt-condition-edges)   | LLM-based     | Intelligent routing based on context and content |
| [**Logical Condition**](#logical-condition-edges) | Code-based    | Deterministic routing based on memory/data       |

## Step Forward Edges

Step forward edges create **unconditional transitions** between nodes. They're useful for sequential processing where you always want to move to the next step.

```yaml
- source: Customer Support Request
  target: Support Agent
  type: stepForward
```

**Use Cases:** Data transformation pipelines, required validation steps, guaranteed notifications, logging and audit trails.

## Prompt Condition Edges

Prompt condition edges use **language models** to make intelligent routing decisions based on conversation context, memory state, and complex reasoning.

```yaml
- source: Support Agent
  target: Escalate to Human
  type: promptCondition
  prompt: 'Based on the conversation, should this be escalated to a human agent?'
```

### Context-Aware Routing

```yaml
- source: Customer Service Bot
  target: VIP Support
  type: promptCondition
  prompt: |
    Should this customer be routed to VIP support?

    Consider:
    - Customer tier
    - Issue severity 
    - Previous escalations
    - Current sentiment: (analyze from conversation)
```

### Multi-Path Routing

```yaml
# Route to different specialists based on issue type
- source: Issue Classifier
  target: Technical Support
  type: promptCondition
  prompt: 'Is this a technical/software issue requiring troubleshooting?'

- source: Issue Classifier
  target: Billing Support
  type: promptCondition
  prompt: 'Is this a billing, payment, or account-related issue?'

- source: Issue Classifier
  target: General Support
  type: promptCondition
  prompt: "Is this a general inquiry that doesn't fit other categories?"
```

## Logical Condition Edges

Logical condition edges use **JavaScript expressions** to make deterministic routing decisions based on memory state, providing precise control over flow logic. Prefer using prompt condition edges, unless you need an exact match of an ouput of a previous node.

```yaml
- source: Order Lookup
  target: Refund Processing
  type: logicalCondition
  condition: "state.memory.orderStatus === 'delivered'"
```

### How Condition Expressions Work

The condition field accepts a **JavaScript expression** that will be evaluated to determine if the edge should be taken. The expression has access to the current agent state through the `state` variable and the previous node's result through the `lastNodeResult` variable.

The condition is automatically wrapped in a function that provides the state and lastNodeResult result context:

```javascript
// Your condition: state.memory.orderStatus === 'delivered'
// Becomes:
(function ({ state, lastNodeResult }) {
  return state.memory.orderStatus === 'delivered';
});
```

```javascript
// Your condition: lastNodeResult.price > 100
// Becomes:
(function ({ state, lastNodeResult }) {
  return lastNodeResult.price > 100;
});
```

### Supported Expression Types

#### Simple Comparisons

```yaml
# Equality check
condition: "state.memory.status === 'active'"

# Numeric comparison
condition: "state.memory.orderValue > 1000"

# String comparison
condition: "state.memory.customerTier !== 'basic'"

# Boolean check
condition: "state.memory.isVerified"
condition: "!state.memory.hasErrors"
```

#### Complex Expressions

```yaml
# Multiple conditions with AND
condition: "state.memory.orderValue > 500 && state.memory.customerAge < 30"

# OR conditions
condition: "state.memory.priority === 'high' || state.memory.customerTier === 'vip'"

# Nested property access
condition: "state.memory.order.items.length > 5"

# Array methods
condition: "state.memory.tags.includes('urgent')"

# String methods
condition: "state.memory.email.endsWith('@company.com')"

# Combining state and lastNodeResult
condition: "lastNodeResult.status === 'success' && state.memory.retryCount === 0"

# lastNodeResult result with fallback
condition: "(lastNodeResult?.productName || state.memory.defaultProduct) === 'iPhone 15'"
```

#### Advanced JavaScript Operations

```yaml
# Ternary operators
condition: "state.memory.score > 80 ? state.memory.level === 'expert' : state.memory.level === 'beginner'"

# Date comparisons
condition: "new Date(state.memory.orderDate) < new Date()"

# Regular expressions
condition: "/^[A-Z]{2}\\d{4}$/.test(state.memory.orderId)"

# Math operations
condition: "Math.abs(state.memory.temperature - 72) < 5"

# JSON operations
condition: "JSON.parse(state.memory.configString).enabled === true"
```

### Available Context

Your condition expressions have access to:

* **state**: The full agent state object (access as `state.memory`, `state.messages`, etc.)
* **lastNodeResult**: The result from the previous node in the flow
* **Standard JavaScript globals**: `Math`, `Date`, `JSON`, `console`
* **String/Array/Object methods**: All standard JavaScript methods

#### Accessing State Properties

```yaml
# Access memory through state
condition: "state.memory.orderStatus === 'pending'"

# Access messages
condition: "state.messages.length > 10"

# Access other state properties
condition: "state.conversationId && state.memory.isAuthenticated"
```

#### Accessing Previous Node Results

The `lastNodeResult` object provides access to the result of the last executed node in the flow. This is particularly useful when you need to route based on what the previous operator or tool returned.

```yaml
# Route based on operator output
- source: Search Products
  target: Show Results
  type: logicalCondition
  condition: 'lastNodeResult && lastNodeResult.productCount > 0'

# Route based on tool result
- source: Validate Order
  target: Process Payment
  type: logicalCondition
  condition: "lastNodeResult.status === 'valid'"

# Check specific fields from operator outputSchema
- source: Extract Customer Data
  target: VIP Handler
  type: logicalCondition
  condition: "lastNodeResult.customerTier === 'premium'"
```

**How `lastNodeResult` Works**

MindedJS automatically extracts and normalizes results from different node types:

* **Operator (Browser Task) nodes**: Extracts from `outputSchema` fields
* **Tool nodes**: Extracts the `result` property returned by the tool
* **RPA nodes**: Extracts collected data from RPA steps
* **App Tool nodes**: Extracts the tool execution result

**Example with Operator:**

```yaml
# Operator node with outputSchema
- name: search-amazon
  type: browserTask
  displayName: Search Amazon
  prompt: Search for iPhone 15 on Amazon and extract the price
  outputSchema:
    - name: productName
      type: string
    - name: price
      type: number

# Next node with logical condition using lastNodeResult
- source: search-amazon
  target: High Price Handler
  type: logicalCondition
  condition: 'lastNodeResult.price > 1000'

- source: search-amazon
  target: Low Price Handler
  type: logicalCondition
  condition: 'lastNodeResult.price <= 1000'
```

**Example with Tool:**

```typescript
// Tool that returns a result
const validateOrderTool: Tool = {
  name: 'validateOrder',
  execute: async ({ input, state }) => {
    return {
      result: {
        status: 'valid',
        orderId: '12345',
        total: 299.99,
      },
    };
  },
};
```

```yaml
# Route based on tool result
- source: Validate Order Tool
  target: Process Payment
  type: logicalCondition
  condition: "lastNodeResult.status === 'valid' && lastNodeResult.total < 500"
```

**Safety Checks:**

Always check if `lastNodeResult` exists before accessing its properties:

```yaml
# Safe condition with null check
condition: "lastNodeResult && lastNodeResult.status === 'success'"

# Alternative with optional chaining syntax
condition: "lastNodeResult?.productName === 'iPhone 15'"
```

#### Example: Using lastNodeResult After a Tool

When a tool executes and returns a result, you can route based on that result using logical conditions. Here's a complete example with the `refundOrder` tool:

**Tool Implementation:**

```typescript
const refundOrderTool: Tool<typeof schema, Memory> = {
  name: 'refundOrder',
  description: 'Refund order',
  input: schema,
  execute: async ({ input, state }) => {
    return {
      result: {
        status: 'success', // or 'failure'
        refundAmount: 100,
      },
    };
  },
};
```

**Flow Configuration with Logical Conditions:**

```yaml
edges:
  - source: refundOrder
    target: Success
    type: logicalCondition
    condition: "lastNodeResult.status === 'success'"

  - source: refundOrder
    target: Failure
    type: logicalCondition
    condition: "else"
```

In this example, the tool returns a `result` object with a `status` property. The edges then route to different nodes based on whether the refund was successful or failed.

<figure><img src="https://3102911747-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHU1mpSPLlxAD3fAGAhf0%2Fuploads%2Fgit-blob-6797052a0088b72740d3c0bd20fd321672ece60d%2Fprevious-after-tool.png?alt=media" alt="Using lastNodeResult after a tool"><figcaption></figcaption></figure>

#### Example: Using lastNodeResult After an Operator

For Operator (Browser Task) nodes, you need to define the output schema first, then you can route based on the operator's result.

**Step 1: Configure Operator Output Schema**

In your operator node, add an output schema field to capture the result:

* **Field name**: `result`
* **Field type**: `Number`
* **Description**: `0 = failure, 1 = success`

<figure><img src="https://3102911747-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHU1mpSPLlxAD3fAGAhf0%2Fuploads%2Fgit-blob-d8e0d242948e2d51bc4dbe9c415c5f6f0846452a%2Foperator-setup.png?alt=media" alt="Operator setup with output schema"><figcaption></figcaption></figure>

**Step 2: Configure Edges with Logical Conditions**

```yaml
edges:
  - source: Operator
    target: Success
    type: logicalCondition
    condition: 'lastNodeResult.result === 1'

  - source: Operator
    target: Failure
    type: logicalCondition
    condition: 'lastNodeResult.result === 0'
```

The operator will extract the result value according to the prompt and output schema, and the edges will route based on whether the result is `1` (success) or `0` (failure).

<figure><img src="https://3102911747-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FHU1mpSPLlxAD3fAGAhf0%2Fuploads%2Fgit-blob-df57c964b3ea72db58df14d3fdc7c9ccaf16f0ac%2Fprevious-after-operator.png?alt=media" alt="Using lastNodeResult after an operator"><figcaption></figcaption></figure>

**Note:** For security, the following are NOT available:

* `process`, `require`, `global`, `Buffer`
* File system or network operations
* Module imports

### Memory-Based Routing

```yaml
# Route based on customer tier
- source: Customer Routing
  target: Premium Support
  type: logicalCondition
  condition: "state.memory.customerTier === 'premium'"

# Route based on order value
- source: Order Processing
  target: Manual Review
  type: logicalCondition
  condition: 'state.memory.orderValue > 1000'
```

### Complex Logic Conditions

```yaml
# Multiple conditions with AND logic
- source: Payment Processor
  target: Fraud Check
  type: logicalCondition
  condition: |
    state.memory.orderValue > 500 && 
    state.memory.customerAge < 30 &&
    state.memory.shippingCountry !== state.memory.billingCountry

# Date-based conditions
- source: Order Validator
  target: Late Order Handler
  type: logicalCondition
  condition: |
    (Date.now() - new Date(state.memory.orderDate).getTime()) / (1000 * 60 * 60 * 24) > 30
```

### Debugging Logical Conditions

For comprehensive debugging of logical conditions, see the [Debugging Guide](https://docs.minded.com/sdk/debugging#debugging-logical-conditions).

## Edge Evaluation Order

When multiple edges exist from the same source node, MindedJS evaluates them by **edge type priority**, not declaration order:

### Priority System

| Priority | Edge Type          | Behavior                                                |
| -------- | ------------------ | ------------------------------------------------------- |
| **1st**  | `stepForward`      | Always executes first (max 1 per source)                |
| **2nd**  | `logicalCondition` | Executes if no step forward edge                        |
| **3rd**  | `promptCondition`  | Executes if no step forward and logical conditions fail |

```yaml
# Priority-based evaluation regardless of declaration order
edges:
  - source: Decision Node
    target: AI Router # 3rd priority: Only if logical fails
    type: promptCondition
    prompt: 'Which handler should process this request?'

  - source: Decision Node
    target: Default Handler # 1st priority: Always executes
    type: stepForward

  - source: Decision Node
    target: High Priority # 2nd priority: Checked if no stepForward are present
    type: logicalCondition
    condition: "state.memory.priority === 'high'"
```

### Logical Condition Order

Within logical condition edges from the same source, evaluation follows **declaration order**:

```yaml
edges:
  # Logical conditions evaluated in declaration order: 1 -> 2 -> 3
  - source: Order Processor
    target: VIP Handler # 1st: Checked first
    type: logicalCondition
    condition: "state.memory.customerTier === 'vip'"

  - source: Order Processor
    target: Large Order # 2nd: Checked if VIP fails
    type: logicalCondition
    condition: 'state.memory.orderValue > 1000'

  - source: Order Processor
    target: Standard Handler # 3rd: Checked if both above fail
    type: logicalCondition
    condition: 'true' # Fallback condition
```

### Mixed Edge Example

```yaml
edges:
  # This stepForward will ALWAYS execute first, regardless of order
  - source: Payment Processor
    target: Success Page
    type: stepForward

  # These will never execute because stepForward takes priority
  - source: Payment Processor
    target: Error Handler
    type: logicalCondition
    condition: "state.memory.status === 'error'"

  - source: Payment Processor
    target: AI Router
    type: promptCondition
    prompt: 'Should we route to error handling?'
```

## Advanced Patterns

### Fallback Chains

When you have multiple `logicalCondition` edges from the same source, you can define a final fallback edge by using `condition: "else"`. This fallback edge is taken only if none of the previous conditions evaluate to `true`.

```yaml
# Try specific handlers first, fall back to general
- source: Issue Classifier
  target: Billing Specialist
  type: logicalCondition
  condition: "state.memory.issueType === 'billing'"

- source: Issue Classifier
  target: General Support
  type: logicalCondition
  condition: "else"
```

### Error Handling

```yaml
- source: Payment Processor
  target: Success Handler
  type: logicalCondition
  condition: "state.memory.paymentStatus === 'success'"

- source: Payment Processor
  target: Failure Handler
  type: logicalCondition
  condition: "state.memory.paymentStatus === 'failed' || state.memory.retryCount >= 3"
```

## Next Steps

* [**Tools**](https://docs.minded.com/low-code-editor/tools) - Build powerful functions that your edges can route to
* [**Memory Types**](https://github.com/minded-ai/mindedjs/blob/main/docs/platform/memory.md) - Design state that supports effective routing
* [**Implementation Examples**](https://github.com/minded-ai/mindedjs/blob/main/docs/implementation-examples/edge-examples.md) - See complete edge examples

Master edges to create intelligent, responsive flows that adapt to any situation! 🔄
