Annotation Interface Capability


@Documented @Retention(RUNTIME) @Target(TYPE) public @interface Capability
Marks an interface as a reusable capability contract whose methods are dispatched by the framework (LLM / MCP / WEB_SERVICE) rather than implemented by the developer.

A capability interface carries one or more @ActionSpec-annotated methods that describe what the capability does. A Role gains the capability by implementsing the interface; the framework's ActionDiscovery picks up the capability methods during role initialisation and routes LLM tool calls to them via ActionExecutor.

Why this exists

Before capabilities, every dispatched method required a return null body:


 @ActionSpec(type = ActionType.LLM, ...)
 public String summarize(String text) {
     return null;  // body unused — LLMRoleExecutor handles dispatch
 }
 
The body is dead code — ActionExecutor.processWithActionResult skips the invocation when there is no ActionResult parameter. Yet the method declaration forces the author to write a misleading body that:
  • reads as "always returns null" to static analysis,
  • silently returns null if ever invoked outside the framework path (should be a loud failure), and
  • duplicates the same specification across every role that needs the capability.

With a capability interface the method body is the interface's default — Actions.dispatchedByFramework() — so every implementing role inherits the same contract with zero body boilerplate:


 @Capability
 public interface Summarizer {
     @ActionSpec(type = ActionType.LLM,
                 description = "Summarise the text in one short paragraph",
                 llmSystemPrompt = "You are concise.",
                 llmTemperature = 0.2f)
     default String summarize(String text) {
         throw Actions.dispatchedByFramework();
     }
 }

 public class EditorRole extends Role implements Summarizer, Translator {
     // no capability method bodies — inherited from the interfaces
 }
 

Constraints

  • Every method declared on a @Capability interface must carry an @ActionSpec annotation. This is validated at action-discovery time; a violation throws an IllegalStateException.
  • Capability methods are pure dispatch — they must not declare an ActionResult parameter. Post-processing (ActionResult-based) still lives on the concrete role class, not the interface.
  • The default body must throw, not return. Returning null from a default would silently re-introduce the problem this annotation exists to solve.
Since:
0.4.0
See Also: