Skip to main content

Handling Events

Your agent can now chat, remember conversations, serve multiple users, and use tools. But there's a problem: you can't see what it's doing.

When a user sends a message, your UI is blind. It doesn't know if the agent is thinking, streaming text, or calling tools. Users just see... nothing.

The Problem

Without events, your UI looks frozen:

const agent = new DextoAgent({
llm: { provider: 'openai', model: 'gpt-4o-mini', apiKey: process.env.OPENAI_API_KEY }
});
await agent.start();

const session = await agent.createSession();

console.log('Sending message...');
const response = await agent.generate('Explain quantum computing', session.id);
// 10 seconds of silence...

console.log('Done:', response.content);

Users see "Sending message..." then wait 10 seconds with no feedback. Not great.

The Solution: Events

Listen to what the agent is doing:

const agent = new DextoAgent({
llm: { provider: 'openai', model: 'gpt-4o-mini', apiKey: process.env.OPENAI_API_KEY }
});

// Listen BEFORE starting
agent.agentEventBus.on('llm:thinking', () => {
console.log('Agent is thinking...');
});

agent.agentEventBus.on('llm:chunk', ({ content }) => {
process.stdout.write(content); // Stream text as it arrives
});

agent.agentEventBus.on('llm:response', () => {
console.log('\n✓ Complete');
});

await agent.start();
const session = await agent.createSession();
await agent.generate('Explain quantum computing', session.id);

Now users see:

  1. "Agent is thinking..." (immediate feedback)
  2. Text streaming word-by-word (real-time progress)
  3. "Complete" (clear ending)

Much better!

Core Events

Thinking

agent.agentEventBus.on('llm:thinking', ({ sessionId }) => {
showLoadingSpinner(sessionId);
});

Fires when the agent starts processing. Show a loading indicator.

Streaming Text

agent.agentEventBus.on('llm:chunk', ({ sessionId, content }) => {
appendText(sessionId, content);
});

Fires for each chunk of text. Build up the response in your UI.

Response Complete

agent.agentEventBus.on('llm:response', ({ sessionId, content, usage }) => {
hideLoadingSpinner(sessionId);
console.log(`Tokens used: ${usage?.totalTokens}`);
});

Fires when done. Hide loading, show final message.

Showing Tool Usage

When your agent uses tools, show what it's doing:

agent.agentEventBus.on('llm:tool-call', ({ sessionId, toolName, args }) => {
showToolBanner(sessionId, `Using ${toolName}...`);
});

agent.agentEventBus.on('llm:tool-result', ({ sessionId, toolName, success }) => {
if (success) {
hideToolBanner(sessionId);
} else {
showError(sessionId, `Failed to use ${toolName}`);
}
});

This gives users confidence—they see the agent working, not just waiting.

Complete Example

Here's a simple chat UI with event handling:

import { DextoAgent } from '@dexto/core';

const agent = new DextoAgent({
llm: { provider: 'openai', model: 'gpt-4o-mini', apiKey: process.env.OPENAI_API_KEY }
});

// Track UI state
const uiState = new Map<string, {
status: 'idle' | 'thinking' | 'streaming';
currentMessage: string;
}>();

agent.agentEventBus.on('llm:thinking', ({ sessionId }) => {
uiState.set(sessionId, { status: 'thinking', currentMessage: '' });
updateUI(sessionId);
});

agent.agentEventBus.on('llm:chunk', ({ sessionId, content }) => {
const state = uiState.get(sessionId)!;
state.status = 'streaming';
state.currentMessage += content;
updateUI(sessionId);
});

agent.agentEventBus.on('llm:response', ({ sessionId }) => {
const state = uiState.get(sessionId)!;
state.status = 'idle';
updateUI(sessionId);
});

function updateUI(sessionId: string) {
const state = uiState.get(sessionId)!;

if (state.status === 'thinking') {
console.log(`[${sessionId}] 🤔 Thinking...`);
} else if (state.status === 'streaming') {
console.log(`[${sessionId}] ✍️ ${state.currentMessage}`);
} else {
console.log(`[${sessionId}] ✓ Done`);
}
}

await agent.start();

All Available Events

LLM Events:

  • llm:thinking - Started processing
  • llm:chunk - Text chunk arrived
  • llm:tool-call - Calling a tool
  • llm:tool-result - Tool finished
  • llm:response - Response complete

MCP Events:

  • mcp:server-connected - Tool server connected
  • mcp:server-disconnected - Tool server disconnected

Session Events:

  • session:created - New session created
  • session:deleted - Session deleted

See the Events API Reference for complete details.

What's Next?

You've mastered the core SDK capabilities—creating agents, managing sessions, serving users, adding tools, and handling events. But you've been configuring everything inline with JavaScript objects.

Production applications need more: reusable configs, environment management, and programmatic agent orchestration. The next tutorials cover these production patterns.

Continue to: Loading Agent Configs