Skip to content
tnsaijava agent framework

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.

EventHandler nameWhen
RunStartEventagent.startedRun begins
RunEndEventagent.stoppedRun finishes
ToolCallStartEventaction.startedTool invocation begins
ToolCallEndEventaction.completedTool invocation completes
TextDeltaEventllm.responseStreaming text chunk
ThoughtEventllm.thoughtReasoning / thought
ErrorEventerror.occurredFailure during the run
StatusEventstatus.updatedProgress update
BDIUpdateEventbdi.updatedBDI state change
ArtifactEventartifact.createdArtifact created
ApprovalRequestEventapproval.requestedHuman approval required
AgentHierarchyEventagent.hierarchyHierarchy change
UiComponentEventui.componentUI component payload
SkillActivationEventskill.activation.eventSkill 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());