Agent and Action Termination
Embabel provides mechanisms to terminate agents and actions early, either gracefully (at the next checkpoint) or immediately. This is useful for scenarios like user cancellation, timeout handling, resource limits, or ctitical error handling.
Choosing Between Signal and Exception
Section titled “Choosing Between Signal and Exception”| Mechanism | When to Use | Behavior | | --- | --- | --- | | Graceful (Signal) | “Let me finish my work, then stop” - side effects need to complete | Terminates at next checkpoint; current operation completes normally | | Immediate (Exception) | “Stop now, nothing left to do” - no further processing needed | Terminates immediately; nothing executes after the exception being thrown |
Agent Termination
Section titled “Agent Termination”Agent termination stops the entire agent process. The process status becomes TERMINATED.
Graceful Agent Termination (Signal)
Section titled “Graceful Agent Termination (Signal)”Use terminateAgent() when the current operation should complete before stopping.
The agent terminates before the next checkpoint tick.
// Signal: "Let me finish my work, then stop the agent" @LlmTool(description = "Save all pending work and shutdown") fun saveAndShutdown(ctx: ProcessContext): String { repository.saveAll(pendingRecords) // side effect completes ctx.terminateAgent("All work saved, shutting down") return "Saved ${pendingRecords.size} records" // tool finishes normally }Immediate Agent Termination (Exception)
Section titled “Immediate Agent Termination (Exception)”Use TerminateAgentException when the agent must stop immediately.
No further tool calls or actions execute.
// Exception: "Stop now, nothing left to do" @LlmTool(description = "Validate critical prerequisites") fun validatePrerequisites(): String { if (!authService.hasRequiredPermissions()) { throw TerminateAgentException("Missing required permissions") // nothing after this runs } return "Prerequisites validated" }@LlmTool(description = "Validate critical prerequisites")public String validatePrerequisites() {if (!authService.hasRequiredPermissions()) {throw new TerminateAgentException("Missing required permissions");// nothing after this runs}return "Prerequisites validated";}Action Termination
Section titled “Action Termination”Action termination stops only the current action. The agent continues with the next planned action. This is useful for skipping problematic steps while allowing the overall goal to proceed.
Graceful Action Termination (Signal)
Section titled “Graceful Action Termination (Signal)”Use terminateAction() when the current tool call should complete before stopping the action.
The action terminates between tool calls.
// Signal: "Let me finish my work, then stop" @LlmTool(description = "Save and shutdown") fun saveAndStop(ctx: ProcessContext): String { customerRepository.save(record) // side effect completes ctx.terminateAction("Save complete, no more work needed") return "Saved" // tool finishes normally }import com.embabel.agent.api.annotation.AchievesGoal;import com.embabel.agent.api.annotation.Action;import com.embabel.agent.api.annotation.Agent;import com.embabel.agent.api.annotation.support.AgentMetadataReader;import com.embabel.agent.api.common.ActionContext;import com.embabel.agent.api.dsl.Frog;import com.embabel.agent.core.AgentProcess;import com.embabel.agent.core.AgentProcessStatusCode;import com.embabel.agent.core.ProcessOptions;import com.embabel.agent.core.support.InMemoryBlackboard;import com.embabel.agent.core.support.SimpleAgentProcess;import com.embabel.agent.domain.io.UserInput;import com.embabel.agent.spi.support.DefaultPlannerFactory;import org.junit.jupiter.api.Test;
import java.time.Instant;
import static com.embabel.agent.api.termination.Termination.terminateAction;import static com.embabel.agent.test.integration.IntegrationTestUtils.dummyPlatformServices;import static org.assertj.core.api.Assertions.assertThat;
/** * Java test demonstrating the use of {@code terminateAction(processContext, reason)} * extension function from Java using static import. * * <p>This test verifies that the static import syntax works correctly from Java * and that graceful ACTION termination signal is cleared after action completes. */class TerminateActionJavaTest {
/** * Test agent that explicitly calls terminateAction using Java syntax. */ @Agent(description = "Java agent with graceful action termination") static class JavaActionTerminatingAgent {
@Action public String firstAction(UserInput input, ActionContext context) { context.set("firstActionRan", true);
// This is the key test: calling terminateAction from Java using static import terminateAction(context.getProcessContext(), "Graceful action termination from Java");
return "first-" + input.getContent(); }
@Action public Frog secondAction(String input, ActionContext context) { context.set("secondActionRan", true); return new Frog(input); }
@Action @AchievesGoal(description = "Turn input into frog") public Frog frogGoal(Frog frog) { return frog; } }Immediate Action Termination (Exception)
Section titled “Immediate Action Termination (Exception)”Use TerminateActionException when the action must stop immediately.
Remaining tool calls in the current batch are skipped.
// Exception: "Stop now, nothing left to do" @LlmTool(description = "Check service health") fun checkHealth(): String { if (!mcpClient.isConnected("required_service")) { throw TerminateActionException("Service unavailable") // nothing after this runs } return "Healthy" }@LlmTool(description = "Check service health")public String checkHealth() {if (!mcpClient.isConnected("required_service")) {throw new TerminateActionException("Service unavailable");// nothing after this runs}return "Healthy";}Catching Both Exception Types
Section titled “Catching Both Exception Types”Both TerminateAgentException and TerminateActionException extend TerminationException,
allowing you to catch them together:
try { tool.execute() } catch (e: TerminationException) { logger.info("Terminated: ${e.reason}") // Handle both agent and action termination }Summary
Section titled “Summary”| Scope | Mechanism | Method/Exception | Use Case |
| --- | --- | --- | --- |
| Agent | Graceful | processContext.terminateAgent(reason) | “Finish current work, then stop agent” |
| Agent | Immediate | throw TerminateAgentException(reason) | “Stop now - critical error, no recovery” |
| Action | Graceful | processContext.terminateAction(reason) | “Finish current tool, then stop action” |
| Action | Immediate | throw TerminateActionException(reason) | “Stop now - try different approach” |