pub struct ExecutionKernel { /* private fields */ }Expand description
ExecutionKernel - the core execution engine
This is THE place where execution happens. Runner wires things up, but kernel does the work.
§Invariant: TenantContext is REQUIRED
Every execution MUST have a TenantContext. This ensures:
- Multi-tenant isolation
- Resource limit enforcement
- Billing attribution
- Audit compliance
Implementations§
Source§impl ExecutionKernel
impl ExecutionKernel
Sourcepub fn new(tenant_context: TenantContext) -> Self
pub fn new(tenant_context: TenantContext) -> Self
Sourcepub fn with_execution(
execution: Execution,
tenant_context: TenantContext,
) -> Self
pub fn with_execution( execution: Execution, tenant_context: TenantContext, ) -> Self
Create a kernel with an existing execution (for replay)
§Arguments
execution- Existing execution state to resumetenant_context- REQUIRED tenant context (must match execution’s tenant)
Sourcepub fn with_protected_emitter(self, emitter: ProtectedEventEmitter) -> Self
pub fn with_protected_emitter(self, emitter: ProtectedEventEmitter) -> Self
Set the protected event emitter for content protection
When set, events with potentially sensitive content (step outputs, tool results, etc.) are emitted through this protected emitter which applies protection processors before streaming.
§Usage
use enact_core::streaming::{ProtectedEventEmitter, PiiProtectionProcessor};
let protected_emitter = ProtectedEventEmitter::new()
.with_processor(Arc::new(PiiProtectionProcessor::new()));
let kernel = ExecutionKernel::new(tenant_context)
.with_protected_emitter(protected_emitter);Sourcepub fn with_inbox(self, inbox: Arc<dyn InboxStore>) -> Self
pub fn with_inbox(self, inbox: Arc<dyn InboxStore>) -> Self
Set the inbox store for mid-execution guidance
When set, the kernel will check the inbox after every step (INV-INBOX-001) and process messages in priority order (INV-INBOX-002).
Sourcepub fn inbox(&self) -> Option<&Arc<dyn InboxStore>>
pub fn inbox(&self) -> Option<&Arc<dyn InboxStore>>
Get the inbox store (if set)
Sourcepub fn with_state_store(self, state_store: Arc<dyn StateStore>) -> Self
pub fn with_state_store(self, state_store: Arc<dyn StateStore>) -> Self
Set the state store for snapshot persistence (best-effort cache)
Sourcepub fn state_store(&self) -> Option<&Arc<dyn StateStore>>
pub fn state_store(&self) -> Option<&Arc<dyn StateStore>>
Get state store (if configured)
Sourcepub fn with_signal_bus(self, signal_bus: Arc<dyn SignalBus>) -> Self
pub fn with_signal_bus(self, signal_bus: Arc<dyn SignalBus>) -> Self
Set the signal bus for optional execution lifecycle signaling
Sourcepub fn signal_bus(&self) -> Option<&Arc<dyn SignalBus>>
pub fn signal_bus(&self) -> Option<&Arc<dyn SignalBus>>
Get signal bus (if configured)
Sourcepub fn with_artifact_store(self, store: Arc<dyn ArtifactStore>) -> Self
pub fn with_artifact_store(self, store: Arc<dyn ArtifactStore>) -> Self
Set the artifact store for storing execution artifacts
When set, the kernel can store artifacts produced by steps. Artifacts are emitted as events for audit trail.
Sourcepub fn artifact_store(&self) -> Option<&Arc<dyn ArtifactStore>>
pub fn artifact_store(&self) -> Option<&Arc<dyn ArtifactStore>>
Get the artifact store (if set)
Sourcepub fn with_enforcement(self, enforcement: Arc<EnforcementMiddleware>) -> Self
pub fn with_enforcement(self, enforcement: Arc<EnforcementMiddleware>) -> Self
Set the enforcement middleware for resource limits
When set, the kernel uses this enforcement middleware to track usage and check limits before each step execution.
Sourcepub fn enforcement(&self) -> &Arc<EnforcementMiddleware>
pub fn enforcement(&self) -> &Arc<EnforcementMiddleware>
Get the enforcement middleware
Sourcepub fn with_long_running_policy(
self,
policy: LongRunningExecutionPolicy,
) -> Self
pub fn with_long_running_policy( self, policy: LongRunningExecutionPolicy, ) -> Self
Set the long-running execution policy
Controls discovery depth, discovered step limits, cost thresholds, and idle timeout for long-running agentic executions.
Sourcepub fn long_running_policy(&self) -> &LongRunningExecutionPolicy
pub fn long_running_policy(&self) -> &LongRunningExecutionPolicy
Get the long-running execution policy
Sourcepub fn with_spawn_mode(self, spawn_mode: SpawnMode) -> Self
pub fn with_spawn_mode(self, spawn_mode: SpawnMode) -> Self
Set the spawn mode for this execution
Controls inbox message routing:
- Inline: shares parent’s inbox (same ExecutionId)
- Child { inherit_inbox: true }: checks both parent and own inbox
- Child { inherit_inbox: false }: isolated inbox
@see docs/TECHNICAL/32-SPAWN-MODE.md
Sourcepub fn spawn_mode(&self) -> Option<&SpawnMode>
pub fn spawn_mode(&self) -> Option<&SpawnMode>
Get the spawn mode (if set)
Sourcepub fn with_parent_execution_id(self, parent_id: ExecutionId) -> Self
pub fn with_parent_execution_id(self, parent_id: ExecutionId) -> Self
Set the parent execution ID for child executions
Used for inbox inheritance when spawn_mode is Child with inherit_inbox=true. Must be set when spawning child executions that need to inherit parent’s inbox.
Sourcepub fn parent_execution_id(&self) -> Option<&ExecutionId>
pub fn parent_execution_id(&self) -> Option<&ExecutionId>
Get the parent execution ID (if set)
Sourcepub fn usage_snapshot(&self) -> Option<UsageSnapshot>
pub fn usage_snapshot(&self) -> Option<UsageSnapshot>
Get current usage snapshot for this execution
Sourcepub async fn register_for_enforcement(&mut self) -> Arc<ExecutionUsage>
pub async fn register_for_enforcement(&mut self) -> Arc<ExecutionUsage>
Register this execution with the enforcement middleware
Call this at the start of execution to begin tracking usage. Returns the usage tracker for this execution.
Sourcepub async fn unregister_from_enforcement(&self)
pub async fn unregister_from_enforcement(&self)
Unregister this execution from the enforcement middleware
Call this at the end of execution to stop tracking usage.
Sourcepub async fn check_limits_before_step(&self) -> Result<(), ExecutionError>
pub async fn check_limits_before_step(&self) -> Result<(), ExecutionError>
Check all resource limits before executing a step
This checks:
- Basic limits (steps, tokens, wall time)
- Long-running limits (discovery depth, discovered steps, cost, idle)
Returns an error if any limit is exceeded.
Sourcepub async fn record_step_completed(&self)
pub async fn record_step_completed(&self)
Record step completion with the enforcement middleware
Sourcepub async fn record_token_usage(&self, input_tokens: u32, output_tokens: u32)
pub async fn record_token_usage(&self, input_tokens: u32, output_tokens: u32)
Record token usage with the enforcement middleware
Sourcepub async fn record_cost(&self, cost_usd: f64)
pub async fn record_cost(&self, cost_usd: f64)
Record cost with the enforcement middleware
Sourcepub async fn record_discovered_step(&self)
pub async fn record_discovered_step(&self)
Record a discovered step with the enforcement middleware
Sourcepub async fn push_discovery_depth(&self)
pub async fn push_discovery_depth(&self)
Push discovery depth (entering a sub-agent execution)
Sourcepub async fn pop_discovery_depth(&self)
pub async fn pop_discovery_depth(&self)
Pop discovery depth (exiting a sub-agent execution)
Sourcepub async fn store_artifact(
&self,
step_id: &StepId,
name: impl Into<String>,
artifact_type: ArtifactType,
content: Vec<u8>,
) -> Option<ArtifactId>
pub async fn store_artifact( &self, step_id: &StepId, name: impl Into<String>, artifact_type: ArtifactType, content: Vec<u8>, ) -> Option<ArtifactId>
Store an artifact produced by a step
This method:
- Stores the artifact in the artifact store
- Emits an ArtifactCreated event for audit trail
- Returns the artifact ID
§Arguments
step_id- The step that produced this artifactname- Name of the artifactartifact_type- Type of artifactcontent- Raw content bytes
§Returns
The generated ArtifactId, or None if no artifact store is configured
Sourcepub async fn store_text_artifact(
&self,
step_id: &StepId,
name: impl Into<String>,
content: impl Into<String>,
) -> Option<ArtifactId>
pub async fn store_text_artifact( &self, step_id: &StepId, name: impl Into<String>, content: impl Into<String>, ) -> Option<ArtifactId>
Store a text artifact (convenience method)
Sourcepub async fn store_json_artifact(
&self,
step_id: &StepId,
name: impl Into<String>,
value: &Value,
) -> Option<ArtifactId>
pub async fn store_json_artifact( &self, step_id: &StepId, name: impl Into<String>, value: &Value, ) -> Option<ArtifactId>
Store a JSON artifact (convenience method)
Sourcepub fn tenant_context(&self) -> &TenantContext
pub fn tenant_context(&self) -> &TenantContext
Get the tenant context
Sourcepub fn execution_id(&self) -> &ExecutionId
pub fn execution_id(&self) -> &ExecutionId
Get the execution ID
Sourcepub fn state(&self) -> ExecutionState
pub fn state(&self) -> ExecutionState
Get the current execution state
Sourcepub fn emitter(&self) -> &EventEmitter
pub fn emitter(&self) -> &EventEmitter
Get the event emitter
Sourcepub fn is_cancelled(&self) -> bool
pub fn is_cancelled(&self) -> bool
Check if cancelled
Uses CancellationToken for proper async cancellation support.
Sourcepub fn cancel(&self, _reason: impl Into<String>)
pub fn cancel(&self, _reason: impl Into<String>)
Cancel the execution
This triggers cooperative cancellation of all async operations. The actual state transition happens through dispatch.
Sourcepub fn child_cancellation_token(&self) -> CancellationToken
pub fn child_cancellation_token(&self) -> CancellationToken
Get a child cancellation token
Child tokens are cancelled when the parent is cancelled, but cancelling a child doesn’t affect the parent.
Sourcepub fn cancellation_token(&self) -> &CancellationToken
pub fn cancellation_token(&self) -> &CancellationToken
Get the cancellation token for use in async operations
Use this with tokio::select! to make async operations cancellable:
tokio::select! {
_ = token.cancelled() => { /* handle cancellation */ }
result = some_async_operation() => { /* handle result */ }
}Sourcepub fn dispatch(&mut self, action: ExecutionAction) -> Result<(), ReducerError>
pub fn dispatch(&mut self, action: ExecutionAction) -> Result<(), ReducerError>
Dispatch an action to the reducer
This is the ONLY way to change execution state.
Sourcepub fn start(&mut self) -> Result<(), ReducerError>
pub fn start(&mut self) -> Result<(), ReducerError>
Start execution
Sourcepub fn begin_step(
&mut self,
step_type: StepType,
name: impl Into<String>,
parent_step_id: Option<StepId>,
) -> Result<StepId, ReducerError>
pub fn begin_step( &mut self, step_type: StepType, name: impl Into<String>, parent_step_id: Option<StepId>, ) -> Result<StepId, ReducerError>
Begin a step
Sourcepub fn complete_step(
&mut self,
step_id: StepId,
output: Option<String>,
duration_ms: u64,
) -> Result<(), ReducerError>
pub fn complete_step( &mut self, step_id: StepId, output: Option<String>, duration_ms: u64, ) -> Result<(), ReducerError>
Complete a step
Sourcepub fn fail_step(
&mut self,
step_id: StepId,
error: ExecutionError,
) -> Result<(), ReducerError>
pub fn fail_step( &mut self, step_id: StepId, error: ExecutionError, ) -> Result<(), ReducerError>
Fail a step with a structured error (feat-02)
Sourcepub fn fail_step_with_message(
&mut self,
step_id: StepId,
message: impl Into<String>,
) -> Result<(), ReducerError>
pub fn fail_step_with_message( &mut self, step_id: StepId, message: impl Into<String>, ) -> Result<(), ReducerError>
Fail a step with a simple message (creates a KernelInternal error)
Sourcepub fn resume(&mut self) -> Result<(), ReducerError>
pub fn resume(&mut self) -> Result<(), ReducerError>
Resume execution
Sourcepub fn wait_for(&mut self, reason: WaitReason) -> Result<(), ReducerError>
pub fn wait_for(&mut self, reason: WaitReason) -> Result<(), ReducerError>
Enter waiting state
Sourcepub fn input_received(&mut self) -> Result<(), ReducerError>
pub fn input_received(&mut self) -> Result<(), ReducerError>
Signal that external input was received
Sourcepub fn complete(&mut self, output: Option<String>) -> Result<(), ReducerError>
pub fn complete(&mut self, output: Option<String>) -> Result<(), ReducerError>
Complete execution
Sourcepub fn fail(&mut self, error: ExecutionError) -> Result<(), ReducerError>
pub fn fail(&mut self, error: ExecutionError) -> Result<(), ReducerError>
Fail execution with a structured error (feat-02)
Sourcepub fn fail_with_message(
&mut self,
message: impl Into<String>,
) -> Result<(), ReducerError>
pub fn fail_with_message( &mut self, message: impl Into<String>, ) -> Result<(), ReducerError>
Fail execution with a simple message (creates a KernelInternal error)
Sourcepub fn cancel_execution(
&mut self,
reason: impl Into<String>,
) -> Result<(), ReducerError>
pub fn cancel_execution( &mut self, reason: impl Into<String>, ) -> Result<(), ReducerError>
Cancel execution
Sourcepub async fn execute_graph(
&mut self,
graph: &CompiledGraph,
input: &str,
) -> Result<NodeState>
pub async fn execute_graph( &mut self, graph: &CompiledGraph, input: &str, ) -> Result<NodeState>
Execute a compiled graph
§Invariants
- INV-INBOX-001: Inbox is checked after every step
- INV-INBOX-002: Control messages are processed first (via priority_order)
- INV-INBOX-003: Inbox events are emitted for audit trail
§Async Cancellation
Uses tokio::select! with CancellationToken for cooperative cancellation. Node execution can be interrupted cleanly if cancellation is requested.
Sourcepub async fn emit_protected(&self, event: StreamEvent) -> Result<()>
pub async fn emit_protected(&self, event: StreamEvent) -> Result<()>
Emit an event with protection processing
If a protected emitter is configured, the event passes through the protection pipeline before being emitted. Otherwise, falls back to the regular emitter.
Use this for events that may contain sensitive content (step outputs, tool results, etc.).
Sourcepub fn emit_unprotected(&self, event: StreamEvent)
pub fn emit_unprotected(&self, event: StreamEvent)
Emit an event without protection (control events, etc.)
Use for events that are guaranteed safe (control signals, execution lifecycle events without content).
Sourcepub fn has_protected_emitter(&self) -> bool
pub fn has_protected_emitter(&self) -> bool
Check if protected emitter is configured
Sourcepub fn protected_emitter(&self) -> Option<&ProtectedEventEmitter>
pub fn protected_emitter(&self) -> Option<&ProtectedEventEmitter>
Get the protected emitter (if configured)