# Event System

The event system provides observability into an agent run. Events implement the sealed `TnsAIEvent` hierarchy in `com.tnsai.events`. You cannot add a custom implementation: only the permitted records below exist.

`@EventEmitter` is **scaffold**. The annotation is declared, but nothing in main reads `EventEmitter.class`. There is no `EventEmitterEvent` type. The catalog already marks this **Scaffold**; this page matches that. Wiring is [TAN-3640](https://linear.app/tansuasici-workspace-1/issue/TAN-3640), not a docs workaround.

## Subscribing to Events

Pass a `Consumer<TnsAIEvent>` to `chatWithEvents`. The callback receives every event the run 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

`TnsAIEvent` permits these records. Names in the left column are the types you match on; the right column is the `EventHandlerRegistry` name used by `@EventHandler`.

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

There is no `ActionStartEvent`, `ActionEndEvent`, `AgentStateChangedEvent`, `MessageEvent`, `WarningEvent`, or `EventEmitterEvent`. Recoverable failures are `ErrorEvent` with `recoverable() == true`.

### Tool Events

`ToolCallStartEvent.toolName()` / `arguments()` and `ToolCallEndEvent.result()` / `durationMs()` / `success()` are the accessors for tool tracing.

### Streaming

`TextDeltaEvent.content()` is the chunk. `isFinal()` marks the last chunk of that message.

## Publishing Events

`TnsAIEventPublisher.publish` takes a **session id** and a **permitted** `TnsAIEvent`. It does not accept a one-arg custom payload.

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

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

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

Until TAN-3640 ships, there is no annotation that auto-emits a named event after a method returns. Use `publish` with a sealed type, or wait for the dispatcher.

## Annotation-Based Handlers

`@EventHandler` **is** wired. `EventHandlerProcessor` extracts methods; `Agent` registers them from the agent and its roles. The required attribute is the **event name string**, not a class token.

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

`EventHandlerRegistry.register(Class, Consumer)` does not exist. To attach a registry to a publisher:

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