# Events

Events provide visibility into agent execution and enable reactive behavior. Each event type has specific input structure, output requirements, and use cases.

## History Structure

All events provide access to `state.history`, an array of `HistoryStep` objects tracking execution flow.

### HistoryStep Structure

* `step`: Sequential step number
* `type`: Step type (TRIGGER\_NODE, TOOL\_NODE, LLM\_NODE, etc.)
* `nodeId`: ID of the executed node
* `nodeDisplayName`: Human-readable node name
* `raw`: Raw data (trigger input, tool output, etc.)
* `messageIds`: Associated message IDs

Additional fields:

* `APP_TRIGGER_NODE`: `appName` identifies source application
* `TOOL_NODE`: Contains tool input/output information

**Example:**

```typescript
agent.on(events.AI_MESSAGE, async ({ state }) => {
  const triggerStep = state.history.find((step) => step.type === 'TRIGGER_NODE' || step.type === 'APP_TRIGGER_NODE');
  const toolSteps = state.history.filter((step) => step.type === 'TOOL_NODE');
  console.log(`Trigger: ${triggerStep?.nodeDisplayName}, Tools used: ${toolSteps.length}`);
});
```

## INIT

Emitted when agent's graph state is initialized (new session begins).

**Input:**

```typescript
{
  state: {
    messages: BaseMessage[];  // Empty initially
    memory: Memory;          // Initial memory from schema
    history: HistoryStep[];  // Empty initially
    sessionId: string;
    sessionType: SessionType;
  }
}
```

**Return:** `void` (handlers for side effects only)

**Example:**

```typescript
agent.on(events.INIT, async ({ state }) => {
  await initializeSessionResources(state.sessionId);
  await logSessionStart({ sessionId: state.sessionId, sessionType: state.sessionType });
  if (state.sessionType === 'VOICE') {
    await setupVoiceSession(state.sessionId);
  }
});
```

**Use cases:** Session logging, resource initialization, external service setup, debugging

## AI\_MESSAGE

Emitted when AI generates a message for the user.

**Input:**

```typescript
{
  message: string;
  state: {
    messages: BaseMessage[];
    memory: Memory;
    history: HistoryStep[];
    sessionId: string;
  }
}
```

**Return:** `void` (handlers for side effects only)

**Example:**

```typescript
agent.on(events.AI_MESSAGE, async ({ message, state }) => {
  await sendMessageToUser(message, state.sessionId);
  await websocket.send(
    JSON.stringify({
      type: 'ai_message',
      content: message,
      sessionId: state.sessionId,
    }),
  );
});
```

**Use cases:** Real-time chat UI, logging, message formatting, notifications, session management

## TRIGGER\_EVENT

Emitted when a trigger node is executed. Allows qualifying triggers and modifying state before processing.

**Input:**

```typescript
{
  triggerName: string;
  triggerBody: any;
  state: State<Memory>; // Current state (can be modified by reference)
}
```

**Return:** Must return `{ isQualified: boolean }`

**Important:** State is modified by reference (v2.0). Modify `state` directly, don't return state updates.

**Return patterns:**

```typescript
// Qualify
return { isQualified: true };

// Disqualify
return { isQualified: false };
```

**Examples:**

```typescript
// Input validation
agent.on(events.TRIGGER_EVENT, async ({ triggerBody, state }) => {
  if (!isValidInput(triggerBody)) {
    state.memory.emailSubject = triggerBody.body.subject;
    return { isQualified: false };
  }
  state.memory.validatedInput = triggerBody;
  return { isQualified: true };
});
```

```typescript
// Query a knowledge base during trigger qualification
//
// Use this pattern when you want full control over *when* retrieval happens
// and *how* the retrieved context is applied to the conversation.
import { SystemMessage } from '@langchain/core/messages';
import { v4 as uuidv4 } from 'uuid';
import { retrieveFromKnowledgeBase } from '@minded-ai/mindedjs';

agent.on(events.TRIGGER_EVENT, async ({ triggerBody, state }) => {
  const query = typeof triggerBody?.content === 'string' ? triggerBody.content : JSON.stringify(triggerBody);

  // knowledgeBaseId is optional - backend auto-selects if agent has exactly 1 KB
  // Pass knowledgeBaseId explicitly if you have multiple KBs
  const { retrievalResults } = await retrieveFromKnowledgeBase({
    environment: 'production',
    query,
    numberOfResults: 5,
  });

  if (retrievalResults.length > 0) {
    const context = retrievalResults.map((r) => `#### ${r.location.s3Location.uri}\n${r.content.text}`).join('\n\n');

    state.messages.push(
      new SystemMessage({
        id: uuidv4(),
        content: `### Knowledge Base Context\n\n${context}`,
      }),
    );
  }

  return { isQualified: true };
});
```

> **Tip:** If you want retrieval to happen automatically after `TRIGGER_EVENT`, enable “default retrieval” from the platform. See [Knowledge Bases](https://docs.minded.com/platform/knowledge-bases#enable-knowledge-base-usage-in-an-agent).

> **Note:** `goto` jump occurs after current invocation completes.

**Use cases:** Input validation, data transformation, context setting, access control, routing logic

## ERROR

Emitted when an error occurs during execution. Control whether error is thrown or caught.

**Input:**

```typescript
{
  error: Error;
  state: State<Memory>;
}
```

**Return:**

```typescript
{
  throwError: boolean; // true = throw error and stop, false = catch and continue
}
```

**Important:** State is modified by reference (v2.0).

**Example:**

```typescript
agent.on(events.ERROR, async ({ error, state }) => {
  await logger.error({
    message: 'Agent execution error',
    error: error.message,
    sessionId: state.sessionId,
  });

  if (error.message.includes('rate limit')) {
    state.memory.errorRecovered = true;
    return { throwError: false }; // Continue
  }

  if (error.message.includes('authentication failed')) {
    await notificationService.alert({ type: 'critical_error', error: error.message });
    return { throwError: true }; // Stop
  }

  return { throwError: false };
});
```

**Use cases:** Error logging, recovery logic, critical error handling, monitoring, session cleanup, analytics

## ON\_LOGICAL\_CONDITION

Emitted before evaluating a logical condition on an edge.

**Input:**

```typescript
{
  edge: LogicalConditionEdge;
  state: State<Memory>;
  condition: string;
}
```

**Return:** `void` (handlers for debugging/logging only)

**Example:**

```typescript
agent.on(events.ON_LOGICAL_CONDITION, async ({ edge, condition, state }) => {
  await logger.debug({
    event: 'condition_evaluation_start',
    condition,
    edge: { source: edge.source, target: edge.target },
    sessionId: state.sessionId,
  });
});
```

**Use cases:** Debugging flow logic, performance monitoring, analytics, testing, audit logging

## ON\_LOGICAL\_CONDITION\_RESULT

Emitted after a logical condition is evaluated, providing result and execution metrics.

**Input:**

```typescript
{
  edge: LogicalConditionEdge;
  state: State<Memory>;
  condition: string;
  result: boolean;
  executionTimeMs: number;
  error?: Error;
}
```

**Return:** `void` (handlers for logging/monitoring only)

**Example:**

```typescript
agent.on(events.ON_LOGICAL_CONDITION_RESULT, async ({ condition, result, executionTimeMs, error }) => {
  if (error) {
    console.error('Condition evaluation failed:', error.message);
  }
  if (executionTimeMs > 10) {
    await logger.warn({ message: 'Slow condition detected', condition, executionTimeMs });
  }
  if (!result) {
    await analytics.track('condition_failed', { condition });
  }
});
```

**Use cases:** Performance monitoring, debugging failed conditions, flow analytics, error tracking, optimization

## ANALYTICS\_EVENT

Emitted when you call `trackAnalyticsEvent()` to send custom analytics data.

**Function signature:**

```typescript
async function trackAnalyticsEvent(eventName: string, payload: Record<string, any>, sessionId: string): Promise<void>;
```

**Handler input:**

```typescript
{
  eventName: string;
  payload: Record<string, any>;
  sessionId: string;
}
```

**Return:** `void`

**Example:**

```typescript
import { trackAnalyticsEvent } from '@minded-ai/mindedjs';

// Track custom events
agent.on(events.AI_MESSAGE, async ({ state }) => {
  if (state.memory.bookingCompleted) {
    await trackAnalyticsEvent(
      'booking_completed',
      {
        bookingId: state.memory.bookingId,
        amount: state.memory.totalAmount,
      },
      state.sessionId,
    );
  }
});

// Listen to analytics events
agent.on(events.ANALYTICS_EVENT, async ({ eventName, payload, sessionId }) => {
  // Send to additional analytics services, filter, transform, etc.
});
```

**Query analytics:** Use the [Analytics API](https://docs.minded.com/api/analytics)

## TURN\_END

Emitted when a trigger invocation completes, just before returning the result. Allows customizing the return value.

**Input:**

```typescript
{
  state: State<Memory>;
}
```

**Return:**

```typescript
{
  returnValue: any; // Custom return value (optional)
}
```

**Example:**

```typescript
agent.on(events.TURN_END, async ({ state }) => {
  return {
    success: true,
    sessionId: state.sessionId,
    result: {
      message: 'Operation completed',
      metadata: {
        executionTime: state.memory.executionTime,
        stepsExecuted: state.history.length,
      },
    },
  };
});
```

**Use cases:** Response formatting, metadata addition, data extraction, API compatibility, result enrichment, response filtering
