#Event Package

    The event package defines the event system used throughout the agent lifecycle for tracking and responding to state changes.

    #Core Types

    #Event

    The main event structure with metadata.

    ///|
    pub struct Event {
    id : @uuid.Uuid
    created : @clock.Timestamp
    desc : EventDesc
    } derive(Eq, Show)

    #EventDesc

    Enumeration of all possible event types in the agent lifecycle.

    ///|
    pub(all) enum EventDesc {
    ModelLoaded(name~ : String)
    PreConversation
    PostConversation
    SystemPromptSet(String?)
    MessageUnqueued(id~ : @uuid.Uuid)
    MessageQueued(id~ : @uuid.Uuid)
    ToolAdded(@tool.ToolDesc)
    PreToolCall(@ai.ToolCall)
    PostToolCall(@ai.ToolCall, result~ : Result[Json, Json], rendered~ : String)
    TokenCounted(Int)
    ContextPruned(origin_token_count~ : Int, pruned_token_count~ : Int)
    AssistantMessage(
    usage~ : @ai.Usage?,
    tool_calls~ : Array[@ai.ToolCall],
    String
    )
    UserMessage(String)
    Cancelled
    Failed(Json)
    Pruned(id~ : @uuid.Uuid)
    }

    #Event Categories

    #Lifecycle Events

    EventDescription
    ModelLoadedAI model has been loaded
    PreConversationConversation is starting
    PostConversationConversation has ended
    CancelledAgent was cancelled
    FailedAn error occurred

    #Message Events

    EventDescription
    SystemPromptSetSystem prompt was set/cleared
    MessageQueuedMessage added to pending queue
    MessageUnqueuedMessage moved to processing
    UserMessageUser or tool message content
    AssistantMessageResponse from AI model

    #Tool Events

    EventDescription
    ToolAddedTool registered with agent
    PreToolCallBefore tool execution
    PostToolCallAfter tool execution (with result)

    #Context Management Events

    EventDescription
    TokenCountedToken count calculated
    ContextPrunedContext trimmed for budget
    PrunedSpecific event pruned from history

    #Key APIs

    #Event Creation

    pub fn Event::new(
    id~ : @uuid.Uuid,
    created? : @clock.Timestamp,
    desc : EventDesc,
    ) -> Event

    #Event Classification

    // Is this an incoming/external event?
    pub fn Event::is_incoming(self : Event) -> Bool

    // Does this event end the conversation?
    pub fn Event::is_stopping(self : Event) -> Bool

    // Does this event start the conversation?
    pub fn Event::is_starting(self : Event) -> Bool

    // Is this a cancellation event?
    pub fn Event::is_cancellation(self : Event) -> Bool

    #EventTarget

    A type for emitting events to listeners.

    type EventTarget

    pub fn EventTarget::new(
    uuid? : @uuid.Generator,
    clock? : &@clock.Clock
    ) -> Self raise

    pub fn EventTarget::emit(Self, EventDesc, id? : @uuid.Uuid) -> Unit
    pub fn EventTarget::add_listener(Self, async (Event) -> Unit) -> Unit
    pub async fn EventTarget::start(Self) -> Unit
    pub async fn EventTarget::flush(Self) -> Unit

    #ExternalEventQueue

    A queue for receiving external events from the environment.

    type ExternalEventQueue

    pub fn ExternalEventQueue::new() -> Self raise
    pub fn ExternalEventQueue::poll(Self) -> Array[Event]
    pub fn ExternalEventQueue::send(Self, EventDesc) -> Unit

    #Typical Event Flow

    ModelLoaded │ ▼ SystemPromptSet │ ▼ MessageQueued ──► MessageUnqueued ──► UserMessage │ ▼ PreConversation │ ▼ TokenCounted │ ▼ ContextPruned (if needed) │ ▼ AssistantMessage │ ├──► PreToolCall ──► PostToolCall ──► UserMessage (tool result) │ │ │ └──► (loop back for more tool calls) │ ▼ PostConversation

    #Usage Example

    // Create event target
    let target = @event.EventTarget::new()

    // Add listener
    target.add_listener(fn(event) {
    match event.desc {
    AssistantMessage(content, ..) => println("AI: \{content}")
    PostToolCall(tc, result~, ..) => println("Tool \{tc.name}: \{result}")
    _ => ()
    }
    })

    // Start processing events
    target.start()

    // Emit events
    target.emit(UserMessage("Hello!"))
    target.emit(AssistantMessage(usage=None, tool_calls=[], "Hi there!"))

    #Dependencies

    • ai: Message and tool call types
    • tool: Tool descriptor types
    • uuid: Unique identifiers
    • clock: Timestamps

    Event

    Event type that occurs during agent conversation lifecycle.

    A typical conversation lifecycle looks like this:

    // add system and user messages
    UserMessage (system)
    UserMessage (user)
    // calling agent.start()
    PreConversation
    while true {
    // poll external events
    ExternalEventReceived (if any)
    // count tokens before request
    TokenCounted
    // prune context if necessary
    ContextPruned
    // receive assistant response
    AssistantMessage
    // executing tool call
    PreToolCall
    PostToolCall
    UserMessage (tool)
    // continue to iterate if there are more messages
    }
    // conversation ended
    PostConversation
    impl ToJson for Event
    impl FromJson for Event

    Event::is_cancellation

    fn Event::is_cancellation(self : Event) -> Bool

    Indicates whether the event represents a cancellation.

    Event::is_incoming

    fn Event::is_incoming(self : Event) -> Bool

    External events that can be sent to the agent from outside sources. These events are collected via polling and can influence the conversation.

    Event::is_starting

    fn Event::is_starting(self : Event) -> Bool

    Indicates whether the event signifies the start of a conversation.

    Event::is_stopping

    fn Event::is_stopping(self : Event) -> Bool

    Indicates whether the event signifies the end of a conversation.

    Event::new

    Creates a new Event with the specified parameters.

    Parameters:

    • id : The unique identifier for the event.
    • created : The timestamp when the event was created. Defaults to the current time if not provided.
    • desc : The event description that specifies the type and content of the event.

    Returns a new Event instance.

    EventDesc

    pub(all) enum EventDesc {
    ModelLoaded(name~ : String)
    PreConversation
    PostConversation
    SystemPromptSet(String?)
    MessageUnqueued(id~ :
    Uuid
    )
    MessageQueued(id~ :
    Uuid
    )
    ToolAdded(
    ToolDesc
    )
    PreToolCall(
    ToolCall
    )
    PostToolCall(
    ToolCall
    , result~ : Result[Json, Json], rendered~ : String)
    TokenCounted(Int)
    ContextPruned(origin_token_count~ : Int, pruned_token_count~ : Int)
    AssistantMessageDelta(String)
    AssistantMessage(usage~ :
    Usage
    ?, tool_calls~ : Array[
    ToolCall
    ], String)
    UserMessage(String)
    Cancelled
    Failed(Json)
    Pruned(id~ :
    Uuid
    )
    } derive(Eq)

    EventTarget

    type EventTarget

    The central event dispatcher for agent lifecycle events.

    EventTarget implements an observer pattern where multiple listeners can subscribe to receive events. Events are queued and processed asynchronously, ensuring that event emission is always non-blocking.

    Architecture

    emit() ──▶ [Queue] ──▶ start() ──▶ [Listener 1] ──▶ [Listener 2] ──▶ [Listener N]

    Threading Model

    • emit() is synchronous and non-blocking (enqueues event)
    • start() runs an async event loop that dispatches to listeners
    • Listeners are invoked sequentially for each event

    EventTarget::add_listener

    fn EventTarget::add_listener(self : EventTarget, f : async (Event) -> Unit) -> Unit

    Registers an async listener function to receive events.

    Listeners are called sequentially for each event in the order they were registered. Each listener receives every event emitted after registration.

    Parameters

    • f: An async function that takes an Event and returns Unit.

    Example

    emitter.add_listener(async fn(event) {
    match event {
    PostToolCall(call, result~, rendered~) => {
    // Log tool call results
    println("Tool \(call.name) completed")
    }
    AssistantMessage(usage~, message~) => {
    // Track token usage
    if usage is Some(u) {
    total_tokens += u.total_tokens
    }
    }
    _ => ()
    }
    })

    EventTarget::emit

    fn EventTarget::emit(self : EventTarget, desc : EventDesc, id? :
    Uuid
    ) -> Unit

    Emits an event to be processed by all registered listeners.

    This method enqueues the event for asynchronous processing. The event will be dispatched to all listeners when start() processes it from the queue.

    Parameters

    • event: The Event to emit.

    Behavior

    • Non-blocking: Returns immediately after enqueuing.
    • Order-preserving: Events are processed in FIFO order.

    Panics

    Aborts if the queue is full (should not happen with unbounded queue).

    Example

    emitter.emit(PreConversation)
    emitter.emit(TokenCounted(1500))
    emitter.emit(PostConversation)

    EventTarget::flush

    async fn EventTarget::flush(self : EventTarget) -> Unit

    Immediately processes all pending events in the queue.

    Unlike start(), this method does not wait for new events. It processes all currently queued events and returns, making it useful for ensuring all events are handled before a checkpoint.

    Behavior

    • Non-blocking on empty: Returns immediately if queue is empty.
    • Draining: Processes all pending events.
    • Does not terminate: Does not affect the start() loop.

    Example

    // Ensure all events are processed before saving state
    emitter.flush()
    save_checkpoint()
    CR: listener is suspendable, should we collcect all events and then call listeners outside of the loop to avoid racing? assuming listerns can take a long time

    EventTarget::new

    Creates a new EventTarget with an empty listener list.

    The event target uses an unbounded queue to ensure emit() never blocks. Remember to call start() in a background task to begin processing events.

    Returns

    A new EventTarget instance ready to receive listeners and events.

    Example

    let emitter = EventTarget::new()
    emitter.add_listener(async fn(event) { println(event) })
    // Start in background
    spawn(() => emitter.start())

    EventTarget::start

    async fn EventTarget::start(self : EventTarget) -> Unit

    Starts the event processing loop.

    This async function runs continuously, waiting for events from the queue and dispatching them to all registered listeners. It blocks until a None sentinel is received (via close()).

    Behavior

    • Blocking: Waits for events when queue is empty.
    • Sequential dispatch: Listeners are called one at a time per event.
    • Terminates: Exits when close() sends the termination signal.

    Usage

    Should typically be spawned as a background task:

    @async.with_task_group((group) => {
    group.spawn_bg(() => { emitter.start() }, no_wait=true)
    // ... rest of the application
    })

    ExternalEventQueue

    type ExternalEventQueue

    A queue for receiving external events from the environment.

    The ExternalEventQueue provides a thread-safe, non-blocking mechanism for external sources (IDE, user input, environment) to communicate with the agent. The environment pushes events via send(), and the agent polls via poll().

    Example

    let queue = ExternalEventQueue::new()

    // From external source (e.g., IDE integration)
    queue.send(Diagnostics(diagnostics))

    // From agent (polling during conversation)
    let events = queue.poll() // Returns all pending events

    ExternalEventQueue::new

    Creates a new ExternalEventQueue with an unbounded async queue.

    The queue is unbounded to prevent blocking when external sources send events, ensuring that event producers never need to wait.

    Returns

    A new ExternalEventQueue instance ready to receive events.

    ExternalEventQueue::poll

    Polls and retrieves all pending external events from the queue.

    This method is called by the agent to check for and process any external events that have been sent since the last poll. It drains the queue, returning all accumulated events.

    Returns

    An Array[ExternalEvent] containing all pending events. Returns an empty array if no events are pending.

    Behavior

    • Non-blocking: Returns immediately with available events.
    • Draining: Removes all events from the queue.
    • Order-preserving: Events are returned in FIFO order.
    • Incoming-only: Only returns events intended for the consumption of the agent.

    Example

    // During conversation loop
    let external_events = queue.poll()
    for event in external_events {
    match event {
    Cancelled => return // Stop conversation
    Diagnostics(d) => process_diagnostics(d)
    UserMessage(msg) => handle_message(msg)
    }
    }

    ExternalEventQueue::send

    fn ExternalEventQueue::send(self : ExternalEventQueue, desc : EventDesc) -> Unit

    Sends an external event to the agent.

    This method is designed to be called from external contexts (environment, IDE integration, user input handlers) to communicate with the agent.

    Parameters

    • event: The ExternalEvent to send to the agent.

    Behavior

    • Non-blocking: Returns immediately without waiting.
    • Thread-safe: Can be safely called from any context.
    • Fire-and-forget: Silently ignores failures (queue full scenario).

    Example

    // Send IDE diagnostics
    queue.send(Diagnostics(diagnostics))

    // Request cancellation
    queue.send(Cancelled)

    // Send user message
    queue.send(UserMessage("Stop and explain"))