Text Chat
Start a text-based conversation with a Stellar agent. No microphone or audio setup is needed — the SDK handles the WebSocket connection and streams agent responses as text.
import { createStellarClient } from "@stellar-ai/agent-sdk";
const client = createStellarClient();
const chat = await client.startChatConversation({
auth: {
strategy: "publicToken",
token: "<your-public-access-token>",
},
agentId: "<your-agent-id>",
visitorId: getOrCreateVisitorId(), // unique ID for this visitor
});
chat.sendMessage("Hello, I need help with my order.");
The SDK automatically creates a conversation on the platform before connecting the WebSocket. Each client supports one active chat conversation at a time.
Options
| Option | Type | Required | Description |
|---|---|---|---|
auth | AuthConfig | Yes | Authentication configuration |
agentId | string | Yes | The agent to connect to |
visitorId | string | Yes | Unique identifier for this visitor |
conversationId | string | No | Resume a previous conversation |
entryPoint | "AI" | "HUMAN" | No | Route to AI (default) or human queue |
variables | Variables | No | Initial context for the agent |
metadata | Record<string, string> | No | Visitor context (page URL, etc.) |
Sending and receiving messages
Send messages with chat.sendMessage(). Agent responses arrive as streaming events:
chat
.on("messageDelta", ({ messageId, delta }) => {
// Streaming text from the agent, arriving in chunks
process.stdout.write(delta);
})
.on("messageDone", ({ messageId, content }) => {
console.log("\nAgent:", content);
});
chat.sendMessage("What's the status of order ORD-12345?");
messageDelta events deliver text incrementally as the agent generates it. messageDone fires once with the complete message when the agent finishes. Both events include a messageId so you can associate deltas with their final message.
Resuming a chat
Pass a conversationId to reconnect to a previous session. The SDK checks if the conversation is still active — if it is, it reconnects and delivers the message history. If the conversation has ended, it creates a new one instead.
// First session — save the conversation ID
chat.on("started", ({ conversationId }) => {
localStorage.setItem("chatId", conversationId);
});
// Later — resume the conversation
const chat = await client.startChatConversation({
auth: { strategy: "publicToken", token: "<your-token>" },
agentId: "<your-agent-id>",
visitorId: getVisitorId(),
conversationId: localStorage.getItem("chatId"),
});
chat.on("started", ({ conversationId, resumed, messages }) => {
if (resumed) {
// Render previous messages
messages.forEach((msg) => renderMessage(msg.role, msg.content));
}
// Save the (possibly new) conversation ID
localStorage.setItem("chatId", conversationId);
});
The started event includes:
conversationId— the active conversation ID (may differ from what you passed if the old one expired)resumed—trueif reconnected to the existing conversation,falseif a new one was createdmessages— previous message history (only present whenresumedistrue)
Each message in the history has the shape:
interface ChatMessage {
id: string;
role: "user" | "assistant" | "human" | "system";
content: string;
}
Events
| Event | Payload | Description |
|---|---|---|
stateChanged | { state } | Connection state changed (connecting, connected, disconnected, error) |
error | { error, code } | An error occurred |
started | { clientId, conversationId, resumed, messages? } | Chat session ready |
messageDelta | { messageId, delta } | Streaming text chunk from the agent |
messageDone | { messageId, content } | Complete message from the agent |
handover | {} | Agent initiated handover to a human |
agentJoined | {} | A human agent joined the chat |
queued | {} | Conversation placed in a queue |
supervisorRequest | { question } | Agent asked a human supervisor a question |
supervisorResolved | {} | Supervisor question resolved |
actionExecuted | { actionName, functionCallItemId, requestBody?, responseBody?, httpStatusCode?, httpStatusText?, error? } | Agent executed a tool or action |
guardrailTriggered | { name, executor, description, message, triggerReason } | A guardrail was triggered |
topicSet | { topic } | The conversation topic was detected or set |
stateTransition | { fromState?, fromStateName?, toState, toStateName, reason?, success, errorMessage? } | The agent moved between dialogue states |
compactionStarted | {} | The server started compacting a long conversation's context |
compactionCompleted | {} | The server completed context compaction |
ended | {} | Conversation ended by the server |
Human handover events
The handover, agentJoined, and queued events support human-in-the-loop workflows. When the agent determines a human should take over, it emits handover. If the conversation is placed in a queue while waiting, you'll receive queued. Once a human agent picks up, agentJoined fires.
chat
.on("handover", () => {
showStatus("Connecting you to a human agent...");
})
.on("queued", () => {
showStatus("You're in the queue. We'll be with you shortly.");
})
.on("agentJoined", () => {
showStatus("A human agent has joined the conversation.");
});
Supervisor events
The supervisorRequest and supervisorResolved events handle scenarios where the AI agent consults a human supervisor mid-conversation without fully handing over. The agent pauses to ask a question, and resumes after receiving an answer.
chat
.on("supervisorRequest", ({ question }) => {
showStatus("The agent is consulting a supervisor...");
})
.on("supervisorResolved", () => {
showStatus("The agent is back.");
});
Action executed events
The actionExecuted event is the main way to handle Studio Client action executions in your app. When the agent chooses a client_action, the SDK emits this event with the Studio action name and the arguments the agent supplied. Your app can listen for it and map it to local behavior like navigation, opening native UI, or updating client-side state.
The same event also fires for other action types. In those cases, requestBody, responseBody, and the optional HTTP fields let you inspect what happened after the action ran. In chat, the payload also includes functionCallItemId, which you can use to correlate the event with the underlying function call.
chat.on("actionExecuted", ({ actionName, requestBody, error }) => {
const args = requestBody ? JSON.parse(requestBody) : {};
if (actionName === "navigate_to_screen") {
navigateToScreen(args.screen_name);
}
if (error) {
showStatus("The agent tried an action, but it failed.");
}
});
State, topic, and guardrail events
These events surface what the agent is doing under the hood, so you can build live observability into your chat experience.
topicSetfires when the agent detects or sets the conversation topic. Use it to label or route the conversation.stateTransitionfires when the agent moves between dialogue states. The payload includessuccess, anderrorMessagewhen a transition fails.guardrailTriggeredfires when a guardrail activates, with the guardrail'sname,description,message, andtriggerReason.
chat
.on("topicSet", ({ topic }) => {
showTopic(topic);
})
.on("stateTransition", ({ toStateName, success }) => {
if (success) showStatus(`Now in: ${toStateName}`);
})
.on("guardrailTriggered", ({ name, message }) => {
console.warn(`Guardrail "${name}" triggered: ${message}`);
});
Server-ended conversations
The ended event fires when the server terminates the conversation (for example, due to inactivity or an agent-initiated close). Clean up your UI when you receive this event.
chat.on("ended", () => {
showStatus("This conversation has ended.");
disableChatInput();
});
Methods
sendMessage
Send a text message to the agent:
chat.sendMessage("What's the status of order ORD-12345?");
end
End the chat session and close the connection:
await chat.end();
state
The current connection state: connecting, connected, disconnected, or error.
on, off, and once
on(event, handler)— subscribe to an event. Returns the instance for chaining.off(event, handler)— unsubscribe a handler.once(event, handler)— subscribe for a single occurrence, then auto-unsubscribe.
Next steps
- Voice conversations — real-time audio conversations with agents
- TypeScript SDK — installation, authentication, and shared configuration