# Events

An agent run publishes events: run start and end, tool calls, text deltas, errors and more. Every event is a record in the sealed `TnsAIEvent` hierarchy in `com.tnsai.events`, so the set of types is fixed.

## Subscribe for one chat turn

Pass a `Consumer<TnsAIEvent>` to `chatWithEvents`. The callback receives every event the turn publishes.

```java
String response = agent.chatWithEvents("Do research on AI", event -> {
    switch (event) {
        case RunStartEvent e -> log.info("Agent run started");
        case ToolCallStartEvent e -> log.info("Tool: {}", e.toolName());
        case ToolCallEndEvent e -> log.info("Result: {}", e.result());
        case TextDeltaEvent e -> log.debug("Delta: {}", e.content());
        case ErrorEvent e -> log.error("Error: {}", e.message());
        case RunEndEvent e -> log.info("Run completed");
        default -> {}
    }
});
```

## Event types

The left column is the type you match on. The right column is the name an `@EventHandler` method subscribes to.

| Event | Handler name | When |
|-------|--------------|------|
| `RunStartEvent` | `agent.started` | Run begins |
| `RunEndEvent` | `agent.stopped` | Run finishes |
| `ToolCallStartEvent` | `action.started` | Tool invocation begins |
| `ToolCallEndEvent` | `action.completed` | Tool invocation completes |
| `TextDeltaEvent` | `llm.response` | Streaming text chunk |
| `ThoughtEvent` | `llm.thought` | Reasoning / thought |
| `ErrorEvent` | `error.occurred` | Failure during the run |
| `StatusEvent` | `status.updated` | Progress update |
| `BDIUpdateEvent` | `bdi.updated` | BDI state change |
| `ArtifactEvent` | `artifact.created` | Artifact created |
| `ApprovalRequestEvent` | `approval.requested` | Human approval required |
| `AgentHierarchyEvent` | `agent.hierarchy` | Hierarchy change |
| `UiComponentEvent` | `ui.component` | UI component payload |
| `SkillActivationEvent` | `skill.activation.event` | Skill activated |

A recoverable failure is an `ErrorEvent` with `recoverable() == true`.

### Tool and streaming accessors

- `ToolCallStartEvent`: `toolCallId()`, `toolName()`, `arguments()`.
- `ToolCallEndEvent`: `toolCallId()`, `toolName()`, `result()`, `success()`, `error()`, `durationMs()`.
- `TextDeltaEvent`: `content()` is the chunk, and `isFinal()` marks the last chunk of that message.

## Publish and subscribe on a session

`TnsAIEventPublisher.publish` takes a session id and one of the event records above.

```java
TnsAIEventPublisher publisher = agent.getEventPublisher();
publisher.publish(sessionId, TextDeltaEvent.of("msg-1", "Hello"));
```

`agent.chatWithEvents(message, sessionId)` publishes the turn's events onto that session. To listen on the same bus:

```java
publisher.subscribe(sessionId, event -> log.info("{}", event.eventType()));
```

## Handle events with annotations

`Agent` registers `@EventHandler` methods from the agent and its roles. The `event` attribute is the handler name from the table.

```java
@EventHandler(event = "action.completed")
public void onToolComplete(ToolCallEndEvent event) {
    log.info("Tool {} took {}ms", event.toolName(), event.durationMs());
}
```

To route a session's events to those handlers, subscribe the agent's registry:

```java
EventHandlerRegistry registry = agent.getEventHandlerRegistry();
publisher.subscribe(sessionId, registry.asEventConsumer());
```

## Related

- [Streaming](streaming.md): text deltas as they arrive.
- [Create an agent](create.md): the chat methods.
- [Monitor](../observability/monitor.md): traces and metrics built on these events.
