CopilotKit

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

Learn more about 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

Learn more about Types →

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) // true

State 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 response
  • TextMessageContentEvent - Streaming text content
  • TextMessageEndEvent - Agent completes text response
  • TextMessageChunkEvent - Chunk-based text streaming

Thinking Events

  • ThinkingStartEvent - Agent begins internal reasoning
  • ThinkingTextMessageStartEvent - Agent starts thinking text
  • ThinkingTextMessageContentEvent - Streaming thinking content
  • ThinkingTextMessageEndEvent - Agent completes thinking text
  • ThinkingEndEvent - Agent finishes reasoning process

Tool Events

  • ToolCallStartEvent - Tool execution begins
  • ToolCallArgsEvent - Streaming tool arguments
  • ToolCallEndEvent - Tool call complete
  • ToolCallChunkEvent - Chunk-based tool streaming
  • ToolResultEvent - Tool execution result

State Events

  • StateSnapshotEvent - Complete state snapshot
  • StateDeltaEvent - Incremental state change

Lifecycle Events

  • RunStartedEvent - Agent run begins
  • RunFinishedEvent - 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