Annotation Interface AgenorMessageHandler


@Target(METHOD) @Retention(RUNTIME) public @interface AgenorMessageHandler
Marks a method as a message handler that is automatically subscribed to the specified topic when the agent starts.

The annotated method must be public and accept a single Message parameter. The runtime registers the method as a subscriber on the agent's MessageDispatcher using the provided topic.

Handlers that are not finished when they return

Return void when the work is done by the time the method returns. When it is not — an LLM call, a database write, anything whose result arrives later — return the CompletableFuture instead of starting it and returning void. The framework waits on what you return, and that is what puts the work inside the delivery guarantees: the message is acknowledged when your future completes, a failure in the chain reaches the transport rather than vanishing, and the work counts against the mailbox's limit on concurrent handlers.


 // Inside the guarantees: the framework waits for the call.
 @AgenorMessageHandler("research.request")
 public CompletableFuture<Void> onRequest(Message message) {
     return llm.chat(request(message)).thenAccept(this::publishFindings);
 }

 // Outside them: the method returns at once, the message is acknowledged at once,
 // and a later failure in the chain is invisible to the framework.
 @AgenorMessageHandler("research.request")
 public void onRequest(Message message) {
     llm.chat(request(message)).thenAccept(this::publishFindings);
 }
 

Any other return type is rejected when the agent is registered, with the same warning a wrong parameter list gets. CompletableFuture<Void> was rejected too before 0.30.0, which meant an asynchronous handler could not be written at all.

The topic is matched exactly. Both delivery paths resolve a handler by map lookup on Message.topic() — the topic path in the dispatcher and the direct path in BaseAgent — so a value containing * or # matches nothing and is rejected when the agent is registered. For pattern matching, subscribe programmatically with a MessageFilter; TopicFilter in agenor-runtime-ext builds one from a wildcard with TopicFilter.wildcard().

Example:


 @Agent("inventory-agent")
 public class InventoryAgent extends BaseAgent {

     @AgenorMessageHandler("orders.new")
     public void onNewOrder(Message message) {
         var order = message.getContentAs(Order.class);
         reserveStock(order);
     }

     @AgenorMessageHandler(value = "inventory.low-stock", autoSubscribe = false)
     public void onLowStock(Message message) {
         // subscribed manually when needed
     }
 }
 
Since:
0.1.0
See Also:
  • Required Element Summary

    Required Elements
    Modifier and Type
    Required Element
    Description
    The exact topic this handler subscribes to.
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    boolean
    Whether the runtime should subscribe this handler automatically when the agent starts.
  • Element Details

    • value

      String value
      The exact topic this handler subscribes to.

      Compared for equality with the incoming Message.topic(). No wildcard or pattern syntax is supported; see the type documentation for the filter-based alternative.

      Returns:
      the topic, must not be empty and must not contain * or #
    • autoSubscribe

      boolean autoSubscribe
      Whether the runtime should subscribe this handler automatically when the agent starts.

      Set to false to delay subscription; the handler can then be registered manually at an appropriate point in the agent's lifecycle.

      Returns:
      true to subscribe automatically (default), false for manual subscription
      Default:
      true