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, not a docs workaround.
Subscribing to Events
Pass a Consumer<TnsAIEvent> to chatWithEvents. The callback receives every event the run publishes.
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.
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:
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.
@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:
EventHandlerRegistry registry = agent.getEventHandlerRegistry();
publisher.subscribe(sessionId, registry.asEventConsumer());Capabilities
Capabilities are reusable, body-less action contracts. A @Capability interface carries one or more @ActionSpec-annotated methods that describe what the capability does; the framework dispatches the call at runtime. A role gains the capability by implements-ing the interface — without writing any method bodies.
Actions vs Tools
TnsAI has three overlapping extension points. They are not interchangeable.