Class ActionExecutor

java.lang.Object
com.tnsai.actions.ActionExecutor

@ThreadSafe public class ActionExecutor extends Object
Main router for action execution in the TnsAI framework.

ActionExecutor is the central dispatcher that routes action requests to appropriate executors based on ActionType. It handles parameter conversion, approval checking, and error wrapping.

Action Types and Executors

ActionTypeExecutorDescription
LOCAL(Reflection)Direct method invocation on Role
WEB_SERVICEWebServiceExecutorHTTP REST API calls
LLMLLMRoleExecutorLLM reasoning (tool calling deferred to function-shape POJOs via AgentBuilder.toolPojos)
MCP_TOOL(tnsai-mcp module)MCP server tool execution (optional)

Execution Flow

 execute(action, role, params, context)
     │
     ├── 1. Validate inputs (null checks)
     │
     ├── 2. Check approval (@ApprovalRequired)
     │       └── Throw ApprovalRequiredException if token missing
     │
     ├── 3. Route by ActionType
     │       ├── LOCAL → invokeDirectly() via reflection
     │       └── Others → TypedActionExecutor.execute()
     │
     └── 4. Return result or wrap exception
 

Usage Examples

Basic Execution


 ActionExecutor executor = new ActionExecutor();
 // Required before any role dispatch, which is refused before the action body without them.
 // AgentBuilder does this for the agents it builds.
 executor.setPrincipal(principal);
 executor.setDefaultScope(AuthorityScope.unrestricted(Duration.ofHours(1)));

 // Get action from role
 Role role = new MyRole();
 ActionMetadata action = role.getAction("processData");

 // Execute with parameters
 Map<String, Object> params = Map.of("input", "data.csv");
 Object result = executor.execute(action, role, params);
 

With Approval Token


 // For actions annotated with @ApprovalRequired
 Map<String, Object> params = Map.of(
     "targetServer", "production",
     "_approval_token", approvalService.getToken()
 );
 executor.execute(deployAction, role, params);
 

Custom Executor Registration


 // Register a custom executor for a specific action type
 executor.registerExecutor(ActionType.WEB_SERVICE, new CustomWebServiceExecutor());
 

Parameter Conversion

The executor automatically converts parameters using TypeConverter:

  • String → int/long/double/boolean
  • Number → int/long/double/float
  • Map → POJO (using Jackson ObjectMapper)

Error Handling

All exceptions are wrapped in ActionExecutionException with:

  • PARAMETER - Invalid parameter values
  • INVOCATION - Reflection invocation errors
  • NETWORK - HTTP/API call failures
  • UNKNOWN - Unexpected errors

Thread Safety

ActionExecutor is thread-safe. Multiple threads can call execute concurrently. Each executor implementation must also be thread-safe.

See Also:
  • Constructor Details

    • ActionExecutor

      public ActionExecutor(ToolMethodDispatcher toolDispatcher)
      Creates an ActionExecutor with a ToolMethodDispatcher for external tool dispatch.

      Pre-0.7.0 this constructor took a List<Tool> of legacy Tool instances. The Tool interface and its single-string execute method were retired with the function-shape POJO migration (RFC #188 v0.7.0 closeout); the dispatcher now resolves both @Tool-annotated POJO methods (StaticToolMethod) and runtime-defined dynamic tools (DynamicToolMethod) through the same path.

      Parameters:
      toolDispatcher - dispatcher seeded by AgentBuilder.buildToolMethodDispatcher(). Must not be null; use the no-arg constructor for an agent with no external tools.
    • ActionExecutor

      public ActionExecutor()
      Creates an ActionExecutor with no external tools — the dispatcher is seeded with an empty ToolMethodRegistry.
  • Method Details

    • configureExecution

      public void configureExecution(AccessSubject agent)
      Bind the actual executing agent once at trusted host startup.
    • configureExecution

      public void configureExecution(AccessSubject agent, Function<Object,String> targetSystem)
      Optional target-system lookup supports the existing AuthorityScope system bounds.
    • authorizeAction

      public AccessRequest authorizeAction(ActionMetadata action, Object target, Map<String,Object> parameters)
      Side-effect-free authorization preflight; does not consume approval or execute hooks.
    • authorizeExternalTool

      public void authorizeExternalTool(String name, Map<String,Object> parameters)
    • authorizeMcpAlias

      public void authorizeMcpAlias(ActionMetadata action, Role role, String alias, Map<String,Object> parameters)
      Discovered endpoint aliases need their own explicit registration in secured dispatch.
    • getMcpToolExecutor

      public McpToolExecutor getMcpToolExecutor()
      Gets the McpToolExecutor if registered.
      Returns:
      the McpToolExecutor or null if not registered
    • setLiabilitySink

      public void setLiabilitySink(LiabilitySink sink)
      Configure the sink the executor publishes per-dispatch AgentLiabilityRecord entries to (TNS-298). Optional; no records are retained unless a sink is set.
      Throws:
      NullPointerException - if sink is null
    • setPrincipal

      public void setPrincipal(AgentPrincipal principal)
      Configure the AgentPrincipal this executor attributes its actions to (TNS-298). Required before execute runs.
      Throws:
      NullPointerException - if principal is null
    • setDefaultScope

      public void setDefaultScope(AuthorityScope scope)
      Configure the AuthorityScope recorded on every dispatch (TNS-298). Required before execute runs.

      Use AuthorityScope.unrestricted(java.time.Duration) when the consumer deliberately chose not to narrow — that's still an explicit decision recorded in the audit trail, not a silent default.

      Throws:
      NullPointerException - if scope is null
    • registerExecutor

      public void registerExecutor(ActionType type, TypedActionExecutor executor)
      Registers a custom executor for a specific action type.
    • execute

      public Object execute(ActionMetadata action, Role role, Map<String,Object> parameters, Map<String,Object> context)
      Executes an action with the given parameters and context.
      Parameters:
      action - The action metadata
      role - The role instance that contains the method
      parameters - The parameters for the action
      context - Additional context (e.g., agent reference)
      Returns:
      The execution result
      Throws:
      ApprovalRequiredException - if action requires approval but no token provided
    • execute

      public Object execute(ActionMetadata action, Role role, Map<String,Object> parameters)
      Executes an action without additional context.
    • execute

      public Object execute(ActionMetadata action, Object target, Map<String,Object> parameters, Map<String,Object> context)
      Executes an action on a generic Object target.

      This overload supports targets that are not TnsAI Role instances, such as SCOP framework roles (ai.scop.core.Role). When the target is a TnsAI Role, it delegates to the standard execute(ActionMetadata, Role, Map, Map) method.

      For non-Role targets, the following pipeline steps apply:

      • @BeforeAction hooks (discovered via reflection on target class)
      • Method- or class-level
        invalid reference
        InputGuardrail
        enforcement
      • ActionType-based routing (LOCAL via reflection, others via typed executors)
      • @AfterAction hooks
      • Parameter validation and conversion

      The following steps are skipped for non-Role targets:

      • Security enforcement (access control, parameter encryption, audit)
      • ActionContract validation (pre/post conditions, invariants)
      • Automatic invariant checking (@InvariantCheck)
      Parameters:
      action - The action metadata
      target - The target object that contains the method (can be any Object)
      parameters - The parameters for the action
      context - Additional context
      Returns:
      The execution result
      Throws:
      ActionExecutionException - if execution fails
    • getToolDispatcher

      public ToolMethodDispatcher getToolDispatcher()
      Returns the ToolMethodDispatcher backing external tool dispatch. Use to obtain the registry for catalog enumeration (e.g.
      invalid reference
      AgentToolExecutor#getToolsForLLM()
      reads the JSON-Schema fragments through this).
      Returns:
      the dispatcher (never null)
    • executeExternalTool

      public Object executeExternalTool(String toolName, Map<String,Object> args)
      Executes an external tool by name through the ToolMethodDispatcher.

      Pre-0.7.0 this took a single string input and called

      invalid reference
      Tool.execute(input)
      . With the Tool interface gone, dispatch is JSON-Schema-aware end-to-end: the LLM emits a typed argument map; the dispatcher coerces it to the tool's parameter types (for StaticToolMethod) or forwards it verbatim to the registered handler (for DynamicToolMethod).
      Parameters:
      toolName - the name of the tool to execute
      args - the LLM's argument map (Jackson-friendly)
      Returns:
      the tool's response (caller serialises back to the LLM)
      Throws:
      IllegalArgumentException - if the tool is not registered
      ToolTimeoutException - when the tool exceeds its configured deadline
      ActionExecutionException - if an input guardrail rejects the call or the tool body throws
      IdempotencyException - for any idempotency conflict, propagated unwrapped so the caller can read getConflict() and decide
    • findExternalTool

      public Optional<ToolMethod> findExternalTool(String toolName)
      Returns the ToolMethod registered under the given name, or empty if none exists.
    • hasExternalTool

      public boolean hasExternalTool(String toolName)
      Checks whether the dispatcher knows a tool with the given name.