Skip to content

feat(embabel): Phase 1 + Annotation Extensions — AgentPlatform Injection & Metadata Foundation #312

@jwijgerd

Description

@jwijgerd

Phase 1 + Annotation Extensions: AgentPlatform Injection & Metadata Foundation

Replaces: #305 (please close that issue)
Incorporates changes from: PR #303

Tracking issue: #311
Module: main/agentic, main/api
Can start: Immediately
Blocks: Phase 2, Phase 3, Phase 4, Phase 5

📋 Plan references:


Overview

This issue combines two foundational pieces that are independent but closely related:

  1. Phase 1 — Inject the Embabel AgentPlatform into the KafkaAgenticAggregateRuntime so that the runtime can create and run AgentProcess instances during command processing.

  2. Annotation Extensions — Extend @AgenticAggregateInfo with three new properties (agentHandledCommands, agentHandledEvents, agentProducedErrors) and create the agentic handler adapter classes that plug into the existing handler mechanism.


Tasks

Phase 1: AgentPlatform Injection

1.1 Add AgentPlatform to KafkaAgenticAggregateRuntime

  • Inject AgentPlatform (Spring-managed bean from embabel-agent-starter) into the constructor
  • Store as a final field
  • Expose via getAgentPlatform() method

1.2 Wire AgentPlatform through AgenticAggregateRuntimeFactory

  • Resolve AgentPlatform bean from ApplicationContext
  • Pass it to KafkaAgenticAggregateRuntime constructor
  • Fail fast if AgentPlatform is not available — agentic aggregates cannot start without it

1.3 Update AgenticAggregateRuntime Interface

  • Add AgentPlatform getAgentPlatform() method to the interface
  • Allows AgenticAggregatePartition to access the platform

1.4 Expose Memories on AgentProcess Blackboard — Moved to Adapter

This is now handled inside the agentic handler adapters' apply() method. The adapter already has access to the state (passed as a parameter), so the partition does not need to set up the blackboard separately.

Annotation Extensions

Extend @AgenticAggregateInfo

Add three new properties:

  • agentHandledCommandsClass<? extends Command>[] — commands handled by the AI agent (no @CommandHandler method needed)
  • agentHandledEventsClass<? extends DomainEvent>[] — external events handled by the AI agent (no @EventHandler method needed)
  • agentProducedErrorsClass<? extends ErrorEvent>[] — error events the AI agent may produce

Registration Flow for Agent Properties

In AgenticAggregateBeanFactoryPostProcessor / AgenticAggregateRuntimeFactory:

  • For agentHandledCommands[]: Validate, create CommandType, create AgenticCommandHandlerFunctionAdapter, register via runtimeBuilder.addCommandHandler()
  • For agentHandledEvents[]: Validate, create DomainEventType(external=true), create AgenticEventHandlerFunctionAdapter, register via runtimeBuilder.addExternalEventHandler()
  • For agentProducedErrors[]: Validate, create DomainEventType(error=true), register JSON schema

Create AgenticCommandHandlerFunctionAdapter

  • Implements CommandHandlerFunction<S, C, E> (NO AgenticProcessHandler — deferred to agenttasks plan)
  • apply(command, state): Sets up Blackboard → creates AgentProcess via agentPlatform.createAgentProcess(processCommandGoal, blackboard) → runs tick() loop → collects DomainEvents via AgentProcessResultTranslator → returns Stream
  • Concrete goal: Uses ProcessCommandGoal resolved at construction time
  • isCreate() always returns false — agent-handled commands cannot create state
  • Plugs into existing KafkaAggregateRuntime.handleCommand() flow unchanged

Create AgenticEventHandlerFunctionAdapter

  • Implements EventHandlerFunction<S, InputEvent, E> (NO AgenticProcessHandler — deferred)
  • apply(event, state): Sets up Blackboard → creates AgentProcess via agentPlatform.createAgentProcess(reactToExternalEventGoal, blackboard) → runs tick() loop → collects DomainEvents → returns Stream
  • Concrete goal: Uses ReactToExternalEventGoal resolved at construction time
  • isCreate() always returns false
  • Plugs into existing KafkaAggregateRuntime.handleEvent() flow unchanged

Key Design Decisions

  • No AgentTask / AgenticProcessHandler: Incremental tick support is deferred to a separate plan. Adapters run tick-to-completion.
  • Adapter-based approach: No changes needed to KafkaAggregateRuntime internals — the adapters plug into existing CommandHandlerFunction/EventHandlerFunction contracts
  • Concrete goal selection: Each adapter is hardcoded to its specific goal (ProcessCommandGoal / ReactToExternalEventGoal), resolved at construction time
  • Agent-handled commands cannot create state: isCreate() always false. Every AgenticAggregate must have a deterministic @CommandHandler(create = true) method
  • Error events: Explicitly declared via agentProducedErrors. Registered as DomainEventType(error=true) for schema validation and service discovery
  • Runtime error tolerance: Accept all ErrorEvent instances at runtime, even if undeclared. Log warning, do not reject.
  • AgentPlatform is mandatory: Fatal error if not available in ApplicationContext
  • Blackboard setup in adapter: Memories, state, aggregate service records, and conditions are set up inside the adapter's apply() method (not by the partition)

Acceptance Criteria

  • @AgenticAggregateInfo has agentHandledCommands(), agentHandledEvents(), agentProducedErrors() properties
  • AgentPlatform is injected into KafkaAgenticAggregateRuntime and exposed via getAgentPlatform()
  • AgenticAggregateRuntimeFactory resolves AgentPlatform and fails fast if absent
  • AgenticCommandHandlerFunctionAdapter implements CommandHandlerFunction with concrete ProcessCommandGoal
  • AgenticEventHandlerFunctionAdapter implements EventHandlerFunction with concrete ReactToExternalEventGoal
  • Both adapters return isCreate() = false
  • No AgentTask record or AgenticProcessHandler interface (deferred)
  • Registration flow processes all three annotation properties correctly
  • agentHandledCommands create CommandType entries with adapter
  • agentHandledEvents create external DomainEventType entries with adapter
  • agentProducedErrors create DomainEventType(error=true) entries with JSON schemas
  • Blackboard setup (memories, state, conditions, aggregate service records) done inside adapter apply()
  • All existing tests still pass
  • Code follows Java 25 conventions

Metadata

Metadata

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions