CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-api

Fluent DSL and Kotlin DSL for building autonomous agents with planning capabilities on the JVM, featuring annotation-based and programmatic configuration for agentic flows with Spring Boot integration

Overview
Eval results
Files

events.mddocs/

Event System

The event system provides comprehensive observability into agent execution. Events are emitted for all significant actions including planning, action execution, LLM calls, tool invocations, and process lifecycle changes.

Capabilities

AgenticEvent Interface

Base marker interface for all agentic events.

interface AgenticEvent

AgenticEventListener

Interface for observing agent events.

interface AgenticEventListener {
    /** Called when any agentic event occurs */
    fun onEvent(event: AgenticEvent)
}

class MulticastAgenticEventListener(
    private val listeners: List<AgenticEventListener>
) : AgenticEventListener {
    override fun onEvent(event: AgenticEvent) {
        listeners.forEach { it.onEvent(event) }
    }
}

Basic Event Listener Example:

class ExecutionLogger : AgenticEventListener {
    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is AgentProcessCreationEvent ->
                logger.info("Agent process created")

            is ActionExecutionStartEvent ->
                logger.info("Action started: ${event.action.name}")

            is ActionExecutionResultEvent ->
                logger.info("Action result: ${event.result}")

            is AgentProcessCompletedEvent ->
                logger.info("Agent completed: ${event.result}")

            is AgentProcessFailedEvent ->
                logger.error("Agent failed", event.cause)
        }
    }
}

// Register listener
val invocation = AgentInvocation.create<Result>(agentPlatform)
    .withAgentName("processor")
    .withEventListener(ExecutionLogger())
    .execute()

AgentProcessEvent

Base interface for process-level events.

sealed interface AgentProcessEvent : AgenticEvent, InProcess {
    val processId: String
    val timestamp: Instant
}

Process Lifecycle Events

Events tracking agent process lifecycle.

/** Agent process created */
data class AgentProcessCreationEvent(
    override val processId: String,
    val agent: Agent
) : AgentProcessEvent

/** Process ready to formulate plan */
data class AgentProcessReadyToPlanEvent(
    override val processId: String
) : AgentProcessEvent

/** Replanning requested */
data class ReplanRequestedEvent(
    override val processId: String,
    val reason: String
) : AgentProcessEvent

/** Plan formulated and ready to execute */
data class AgentProcessPlanFormulatedEvent(
    override val processId: String,
    val plan: Plan
) : AgentProcessEvent

/** Agent process completed successfully */
data class AgentProcessCompletedEvent(
    override val processId: String,
    val result: Any?
) : AgentProcessEvent

/** Agent process failed */
data class AgentProcessFailedEvent(
    override val processId: String,
    val cause: Throwable
) : AgentProcessEvent

/** Process waiting for external input */
data class AgentProcessWaitingEvent(
    override val processId: String,
    val waitingFor: String
) : AgentProcessEvent

/** Process paused */
data class AgentProcessPausedEvent(
    override val processId: String
) : AgentProcessEvent

/** Process stuck and cannot proceed */
data class AgentProcessStuckEvent(
    override val processId: String,
    val stuckReason: String
) : AgentProcessEvent

/** Process killed/terminated */
data class ProcessKilledEvent(
    override val processId: String
) : AgentProcessEvent

Lifecycle Event Example:

class LifecycleMonitor : AgenticEventListener {
    private var startTime: Instant? = null
    private var endTime: Instant? = null

    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is AgentProcessCreationEvent -> {
                startTime = Instant.now()
                logger.info("Process ${event.processId} started for agent ${event.agent.name}")
            }

            is AgentProcessCompletedEvent -> {
                endTime = Instant.now()
                val duration = Duration.between(startTime, endTime)
                logger.info("Process ${event.processId} completed in ${duration.toMillis()}ms")
            }

            is AgentProcessFailedEvent -> {
                logger.error("Process ${event.processId} failed: ${event.cause.message}")
            }

            is AgentProcessStuckEvent -> {
                logger.warn("Process ${event.processId} stuck: ${event.stuckReason}")
            }
        }
    }
}

Action Execution Events

Events for action execution.

/** Action execution started */
data class ActionExecutionStartEvent(
    override val processId: String,
    val action: Action
) : AgentProcessEvent

/** Action execution completed */
data class ActionExecutionResultEvent(
    override val processId: String,
    val action: Action,
    val result: Any?
) : AgentProcessEvent

Action Event Example:

class ActionTracker : AgenticEventListener {
    private val actionTimes = mutableMapOf<String, Long>()

    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is ActionExecutionStartEvent -> {
                actionTimes[event.action.name] = System.currentTimeMillis()
                logger.info("Started: ${event.action.name}")
            }

            is ActionExecutionResultEvent -> {
                val startTime = actionTimes[event.action.name]
                if (startTime != null) {
                    val duration = System.currentTimeMillis() - startTime
                    logger.info("Completed ${event.action.name} in ${duration}ms")
                    logger.info("Result: ${event.result}")
                }
            }
        }
    }
}

LLM Interaction Events

Events for LLM requests and responses.

/** LLM request sent */
data class LlmRequestEvent<O>(
    override val processId: String,
    val messages: List<Message>,
    val tools: List<Tool>,
    val options: LlmOptions
) : AgentProcessEvent

/** LLM response received */
data class LlmResponseEvent<O>(
    override val processId: String,
    val result: O,
    val tokens: TokenUsage
) : AgentProcessEvent

LLM Event Example:

class LlmUsageTracker : AgenticEventListener {
    private var totalPromptTokens = 0
    private var totalCompletionTokens = 0
    private var requestCount = 0

    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is LlmRequestEvent<*> -> {
                requestCount++
                logger.info("LLM request #$requestCount")
                logger.info("Messages: ${event.messages.size}")
                logger.info("Tools: ${event.tools.size}")
            }

            is LlmResponseEvent<*> -> {
                totalPromptTokens += event.tokens.promptTokens
                totalCompletionTokens += event.tokens.completionTokens
                logger.info("Total tokens: ${event.tokens.totalTokens}")
            }
        }
    }

    fun getTotalCost(): TokenStats {
        return TokenStats(
            requests = requestCount,
            promptTokens = totalPromptTokens,
            completionTokens = totalCompletionTokens,
            totalTokens = totalPromptTokens + totalCompletionTokens
        )
    }
}

data class TokenStats(
    val requests: Int,
    val promptTokens: Int,
    val completionTokens: Int,
    val totalTokens: Int
)

Tool Call Events

Events for tool invocations.

/** Tool call requested */
data class ToolCallRequestEvent(
    override val processId: String,
    val toolName: String,
    val input: String,
    val requestId: String
) : AgentProcessEvent

/** Tool call response received */
data class ToolCallResponseEvent(
    override val processId: String,
    val toolName: String,
    val result: Tool.Result,
    val responseId: String
) : AgentProcessEvent

Tool Event Example:

class ToolMonitor : AgenticEventListener {
    private val toolUsage = mutableMapOf<String, Int>()

    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is ToolCallRequestEvent -> {
                toolUsage[event.toolName] = toolUsage.getOrDefault(event.toolName, 0) + 1
                logger.info("Tool called: ${event.toolName}")
                logger.info("Request ID: ${event.requestId}")
            }

            is ToolCallResponseEvent -> {
                when (val result = event.result) {
                    is Tool.Result.Text ->
                        logger.info("Tool ${event.toolName} returned: ${result.content}")

                    is Tool.Result.Error ->
                        logger.error("Tool ${event.toolName} failed: ${result.message}")

                    is Tool.Result.WithArtifact ->
                        logger.info("Tool ${event.toolName} returned artifact")
                }
            }
        }
    }

    fun getMostUsedTool(): String? {
        return toolUsage.maxByOrNull { it.value }?.key
    }
}

State and Goal Events

Events for state transitions and goal achievements.

/** State transition occurred */
data class StateTransitionEvent(
    override val processId: String,
    val stateDescription: String,
    val fromState: Any?,
    val toState: Any
) : AgentProcessEvent

/** Goal achieved */
data class GoalAchievedEvent(
    override val processId: String,
    val goal: Goal
) : AgentProcessEvent

State Event Example:

class StateTracker : AgenticEventListener {
    private val stateHistory = mutableListOf<String>()
    private val achievedGoals = mutableListOf<String>()

    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is StateTransitionEvent -> {
                stateHistory.add(event.stateDescription)
                logger.info("State transition: ${event.stateDescription}")
            }

            is GoalAchievedEvent -> {
                achievedGoals.add(event.goal.name)
                logger.info("Goal achieved: ${event.goal.name}")
            }
        }
    }

    fun getProgress(): Progress {
        return Progress(
            states = stateHistory,
            goals = achievedGoals
        )
    }
}

Blackboard Events

Events for blackboard object operations.

interface ObjectBindingEvent : AgentProcessEvent

/** Object added to blackboard */
data class ObjectAddedEvent(
    override val processId: String,
    val obj: Any
) : ObjectBindingEvent

/** Object bound to specific name */
data class ObjectBoundEvent(
    override val processId: String,
    val name: String,
    val obj: Any
) : ObjectBindingEvent

Blackboard Event Example:

class BlackboardMonitor : AgenticEventListener {
    private val boundObjects = mutableMapOf<String, Any>()

    override fun onEvent(event: AgenticEvent) {
        when (event) {
            is ObjectAddedEvent -> {
                logger.info("Object added: ${event.obj::class.simpleName}")
            }

            is ObjectBoundEvent -> {
                boundObjects[event.name] = event.obj
                logger.info("Object bound: ${event.name} = ${event.obj::class.simpleName}")
            }
        }
    }
}

Progress Events

Events for progress updates.

/** Progress update */
data class ProgressUpdateEvent(
    override val processId: String,
    val message: String
) : AgentProcessEvent

Progress Event Example:

class ProgressReporter : AgenticEventListener {
    override fun onEvent(event: AgenticEvent) {
        if (event is ProgressUpdateEvent) {
            println("Progress: ${event.message}")
        }
    }
}

Platform Events

Platform-level events for ranking and choices.

sealed interface AgentPlatformEvent : AgenticEvent

/** Ranking choice requested */
data class RankingChoiceRequestEvent<T>(
    val options: List<T>,
    val criteria: RankingCriteria
) : AgentPlatformEvent

/** Ranking choice made */
data class RankingChoiceMadeEvent<T>(
    val chosen: T,
    val options: List<T>
) : AgentPlatformEvent

/** Ranking choice could not be made */
data class RankingChoiceCouldNotBeMadeEvent<T>(
    val options: List<T>,
    val reason: String
) : AgentPlatformEvent

Composite Event Listener

Combine multiple listeners.

fun createCompositeListener(
    vararg listeners: AgenticEventListener
): AgenticEventListener {
    return MulticastAgenticEventListener(listeners.toList())
}

// Usage
val compositeListener = createCompositeListener(
    LifecycleMonitor(),
    LlmUsageTracker(),
    ToolMonitor(),
    ProgressReporter()
)

AgentInvocation.create<Result>(agentPlatform)
    .withAgentName("processor")
    .withEventListener(compositeListener)
    .execute()

Types

interface InProcess {
    val processId: String
}

interface Plan {
    val steps: List<Action>
    val cost: Double
}

interface TokenUsage {
    val promptTokens: Int
    val completionTokens: Int
    val totalTokens: Int
}

interface RankingCriteria {
    val description: String
}

Install with Tessl CLI

npx tessl i tessl/maven-com-embabel-agent--embabel-agent-api@0.3.0

docs

actions-goals.md

agent-definition.md

builtin-tools.md

chat.md

conditions.md

events.md

human-in-the-loop.md

index.md

invocation.md

io-binding.md

llm-interaction.md

models.md

planning-workflows.md

platform-management.md

runtime-context.md

state-management.md

streaming.md

subagents.md

tools.md

type-system.md

typed-operations.md

validation.md

tile.json