Core Module Overview
The kotlin-core module provides the fundamental building blocks of the AG-UI protocol implementation. It contains all protocol types, event definitions, message structures, and JSON serialization logic needed for AG-UI communication.
Installation
dependencies {
implementation("com.agui:kotlin-core:0.2.1")
}Note: The core module is automatically included when using kotlin-client, so explicit installation is only needed for advanced use cases.
Core Components
Events
Complete set of AG-UI protocol events with automatic serialization.
- All 24 protocol event types
- Streaming event support (TEXT_MESSAGE_CHUNK, TOOL_CALL_CHUNK)
- Thinking/reasoning events for agent internal processes
- State management events
- Error handling events
Types
Protocol message types and data structures.
- Message hierarchy with role-based discrimination
- Tool definitions and execution types
- State management types
- Request/response structures
Features
JSON Serialization
Built on kotlinx.serialization with AG-UI protocol compliance:
- Polymorphic serialization with
@JsonClassDiscriminator("role") - Automatic event type discrimination
- Proper null handling
- Platform-agnostic JSON processing
Protocol Compliance
Full implementation of the AG-UI protocol specification:
- All message types (System, User, Assistant, Tool)
- Complete event lifecycle support
- State synchronization primitives
- Tool call/result handling
- Agent thinking/reasoning workflows
Type Safety
Compile-time guarantees for protocol correctness:
- Sealed class hierarchies prevent invalid states
- Exhaustive when expressions for event handling
- Null safety throughout the API
- Immutable data structures
Usage Examples
Basic Event Handling
// Handle protocol events
fun processEvent(event: BaseEvent) {
when (event) {
is TextMessageStartEvent -> {
println("Agent started message: ${event.messageId}")
}
is TextMessageContentEvent -> {
print(event.delta) // Stream content
}
is TextMessageEndEvent -> {
println("\nMessage complete")
}
is ThinkingStartEvent -> {
println("Agent is thinking...")
}
is ThinkingTextMessageContentEvent -> {
println("Thinking: ${event.delta}")
}
is ThinkingEndEvent -> {
println("Agent finished thinking")
}
is ToolCallStartEvent -> {
println("Tool call: ${event.toolCallName}")
}
// Handle all other event types...
}
}Message Creation
// Create different message types
val systemMessage = SystemMessage(
id = "sys-1",
content = "You are a helpful assistant"
)
val userMessage = UserMessage(
id = "user-1",
content = "What's the weather like?"
)
val assistantMessage = AssistantMessage(
id = "asst-1",
content = "I'll help you check the weather.",
toolCalls = listOf(
ToolCall(
id = "tool-1",
function = FunctionCall(
name = "get_weather",
arguments = """{"location":"New York"}"""
)
)
)
)
val toolMessage = ToolMessage(
id = "tool-1",
content = "Weather in New York: 72°F, sunny"
)JSON Serialization
// Serialize events to JSON
val event = TextMessageContentEvent(
messageId = "msg-1",
delta = "Hello world"
)
val jsonString = AgUiJson.encodeToString<BaseEvent>(event)
println(jsonString) // {"eventType":"TEXT_MESSAGE_CONTENT",...}
// Deserialize from JSON
val decoded = AgUiJson.decodeFromString<BaseEvent>(jsonString)
println(decoded is TextMessageContentEvent) // trueState Management
// Work with agent state
val initialState = buildJsonObject {
put("conversation", buildJsonObject {
put("topic", "weather")
put("location", "New York")
})
put("userPreferences", buildJsonObject {
put("units", "fahrenheit")
put("language", "en")
})
}
// State is included in requests
val input = RunAgentInput(
threadId = "thread-1",
runId = "run-1",
messages = listOf(userMessage),
state = initialState
)Event Types
Message Events
TextMessageStartEvent- Agent begins text responseTextMessageContentEvent- Streaming text contentTextMessageEndEvent- Agent completes text responseTextMessageChunkEvent- Chunk-based text streaming
Thinking Events
ThinkingStartEvent- Agent begins internal reasoningThinkingTextMessageStartEvent- Agent starts thinking textThinkingTextMessageContentEvent- Streaming thinking contentThinkingTextMessageEndEvent- Agent completes thinking textThinkingEndEvent- Agent finishes reasoning process
Tool Events
ToolCallStartEvent- Tool execution beginsToolCallArgsEvent- Streaming tool argumentsToolCallEndEvent- Tool call completeToolCallChunkEvent- Chunk-based tool streamingToolResultEvent- Tool execution result
State Events
StateSnapshotEvent- Complete state snapshotStateDeltaEvent- Incremental state change
Lifecycle Events
RunStartedEvent- Agent run beginsRunFinishedEvent- Agent run completes
Error Events
ErrorEvent- Protocol or execution errors
Message Types
Core Message Hierarchy
sealed class Message {
abstract val id: String
abstract val content: String?
abstract val role: String
}
// System messages define agent behavior
@Serializable
@SerialName("system")
data class SystemMessage(
override val id: String,
override val content: String,
override val role: String = "system"
) : Message()
// User messages from the human
@Serializable
@SerialName("user")
data class UserMessage(
override val id: String,
override val content: String,
override val role: String = "user"
) : Message()
// Assistant responses from the agent
@Serializable
@SerialName("assistant")
data class AssistantMessage(
override val id: String,
override val content: String?,
val toolCalls: List<ToolCall>? = null,
override val role: String = "assistant"
) : Message()
// Tool execution results
@Serializable
@SerialName("tool")
data class ToolMessage(
override val id: String,
override val content: String,
override val role: String = "tool"
) : Message()Serialization Configuration
The core module provides AgUiJson for protocol-compliant serialization:
object AgUiJson {
val instance = Json {
ignoreUnknownKeys = true
isLenient = true
encodeDefaults = false
classDiscriminator = "role" // For message types
}
}
// Use AgUiJson for all protocol serialization
val json = AgUiJson.encodeToString(message)
val message = AgUiJson.decodeFromString<Message>(json)Platform Support
The core module is fully multiplatform:
- Android: Full compatibility with Android API 26+
- iOS: Native iOS support with Swift interop
- JVM: Server-side and desktop application support
- Shared Code: Common business logic across all platforms
Best Practices
Event Processing
// Use exhaustive when expressions for event handling
fun handleEvent(event: BaseEvent): EventResult {
return when (event) {
is TextMessageStartEvent -> EventResult.StartMessage(event.messageId)
is TextMessageContentEvent -> EventResult.AppendContent(event.delta)
is TextMessageEndEvent -> EventResult.CompleteMessage
is ThinkingStartEvent -> EventResult.StartThinking
is ThinkingTextMessageContentEvent -> EventResult.ThinkingContent(event.delta)
is ThinkingEndEvent -> EventResult.CompleteThinking
is ToolCallStartEvent -> EventResult.StartTool(event.toolCallName)
is ToolCallArgsEvent -> EventResult.AppendArgs(event.delta)
is ToolCallEndEvent -> EventResult.CompleteTool
is ToolResultEvent -> EventResult.ToolResult(event.content)
is StateSnapshotEvent -> EventResult.UpdateState(event.snapshot)
is StateDeltaEvent -> EventResult.PatchState(event.delta)
is RunStartedEvent -> EventResult.StartRun
is RunFinishedEvent -> EventResult.CompleteRun
is ErrorEvent -> EventResult.Error(event.error)
// Compiler ensures all cases are covered
}
}Message Processing
// Process messages - core module provides data access only
fun processMessage(message: Message) {
when (message) {
is SystemMessage -> {
println("System prompt: ${message.content}")
}
is UserMessage -> {
println("User input: ${message.content}")
}
is AssistantMessage -> {
// Handle text content
message.content?.let { content ->
println("Assistant response: $content")
}
// Access tool call data (execution handled by framework)
message.toolCalls?.forEach { toolCall ->
println("Tool requested: ${toolCall.name}")
println("Arguments: ${toolCall.args}")
println("Call ID: ${toolCall.id}")
}
}
is ToolMessage -> {
println("Tool result: ${message.content}")
}
}
}JSON Handling
// Always use AgUiJson for protocol serialization
// Correct
val json = AgUiJson.encodeToString<BaseEvent>(event)
val event = AgUiJson.decodeFromString<BaseEvent>(json)
// Avoid using default Json configuration for protocol types
// val json = Json.encodeToString(event) // May not be protocol compliant