# 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](/platform/knowledge-bases.md#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](/api/analytics.md)

## 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.minded.com/sdk/events.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
