Skip to main content

ExecutionKernel

Struct ExecutionKernel 

Source
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

Source

pub fn new(tenant_context: TenantContext) -> Self

Create a new kernel with a fresh execution

§Arguments
  • tenant_context - REQUIRED tenant context for multi-tenant isolation
§Invariant

TenantContext is REQUIRED for all executions. There is no “system” execution without a tenant - this is enforced at compile time.

Source

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 resume
  • tenant_context - REQUIRED tenant context (must match execution’s tenant)
Source

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);
Source

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).

Source

pub fn inbox(&self) -> Option<&Arc<dyn InboxStore>>

Get the inbox store (if set)

Source

pub fn with_state_store(self, state_store: Arc<dyn StateStore>) -> Self

Set the state store for snapshot persistence (best-effort cache)

Source

pub fn state_store(&self) -> Option<&Arc<dyn StateStore>>

Get state store (if configured)

Source

pub fn with_signal_bus(self, signal_bus: Arc<dyn SignalBus>) -> Self

Set the signal bus for optional execution lifecycle signaling

Source

pub fn signal_bus(&self) -> Option<&Arc<dyn SignalBus>>

Get signal bus (if configured)

Source

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.

Source

pub fn artifact_store(&self) -> Option<&Arc<dyn ArtifactStore>>

Get the artifact store (if set)

Source

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.

Source

pub fn enforcement(&self) -> &Arc<EnforcementMiddleware>

Get the enforcement middleware

Source

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.

Source

pub fn long_running_policy(&self) -> &LongRunningExecutionPolicy

Get the long-running execution policy

Source

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

Source

pub fn spawn_mode(&self) -> Option<&SpawnMode>

Get the spawn mode (if set)

Source

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.

Source

pub fn parent_execution_id(&self) -> Option<&ExecutionId>

Get the parent execution ID (if set)

Source

pub fn usage_snapshot(&self) -> Option<UsageSnapshot>

Get current usage snapshot for this execution

Source

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.

Source

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.

Source

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.

Source

pub async fn record_step_completed(&self)

Record step completion with the enforcement middleware

Source

pub async fn record_token_usage(&self, input_tokens: u32, output_tokens: u32)

Record token usage with the enforcement middleware

Source

pub async fn record_cost(&self, cost_usd: f64)

Record cost with the enforcement middleware

Source

pub async fn record_discovered_step(&self)

Record a discovered step with the enforcement middleware

Source

pub async fn push_discovery_depth(&self)

Push discovery depth (entering a sub-agent execution)

Source

pub async fn pop_discovery_depth(&self)

Pop discovery depth (exiting a sub-agent execution)

Source

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:

  1. Stores the artifact in the artifact store
  2. Emits an ArtifactCreated event for audit trail
  3. Returns the artifact ID
§Arguments
  • step_id - The step that produced this artifact
  • name - Name of the artifact
  • artifact_type - Type of artifact
  • content - Raw content bytes
§Returns

The generated ArtifactId, or None if no artifact store is configured

Source

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)

Source

pub async fn store_json_artifact( &self, step_id: &StepId, name: impl Into<String>, value: &Value, ) -> Option<ArtifactId>

Store a JSON artifact (convenience method)

Source

pub fn tenant_context(&self) -> &TenantContext

Get the tenant context

Source

pub fn execution_id(&self) -> &ExecutionId

Get the execution ID

Source

pub fn state(&self) -> ExecutionState

Get the current execution state

Source

pub fn emitter(&self) -> &EventEmitter

Get the event emitter

Source

pub fn execution(&self) -> &Execution

Get the execution reference

Source

pub fn is_cancelled(&self) -> bool

Check if cancelled

Uses CancellationToken for proper async cancellation support.

Source

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.

Source

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.

Source

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 */ }
}
Source

pub fn dispatch(&mut self, action: ExecutionAction) -> Result<(), ReducerError>

Dispatch an action to the reducer

This is the ONLY way to change execution state.

Source

pub fn start(&mut self) -> Result<(), ReducerError>

Start execution

Source

pub fn begin_step( &mut self, step_type: StepType, name: impl Into<String>, parent_step_id: Option<StepId>, ) -> Result<StepId, ReducerError>

Begin a step

Source

pub fn complete_step( &mut self, step_id: StepId, output: Option<String>, duration_ms: u64, ) -> Result<(), ReducerError>

Complete a step

Source

pub fn fail_step( &mut self, step_id: StepId, error: ExecutionError, ) -> Result<(), ReducerError>

Fail a step with a structured error (feat-02)

Source

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)

Source

pub fn pause(&mut self, reason: impl Into<String>) -> Result<(), ReducerError>

Pause execution

Source

pub fn resume(&mut self) -> Result<(), ReducerError>

Resume execution

Source

pub fn wait_for(&mut self, reason: WaitReason) -> Result<(), ReducerError>

Enter waiting state

Source

pub fn input_received(&mut self) -> Result<(), ReducerError>

Signal that external input was received

Source

pub fn complete(&mut self, output: Option<String>) -> Result<(), ReducerError>

Complete execution

Source

pub fn fail(&mut self, error: ExecutionError) -> Result<(), ReducerError>

Fail execution with a structured error (feat-02)

Source

pub fn fail_with_message( &mut self, message: impl Into<String>, ) -> Result<(), ReducerError>

Fail execution with a simple message (creates a KernelInternal error)

Source

pub fn cancel_execution( &mut self, reason: impl Into<String>, ) -> Result<(), ReducerError>

Cancel execution

Source

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.

Source

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.).

Source

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).

Source

pub fn has_protected_emitter(&self) -> bool

Check if protected emitter is configured

Source

pub fn protected_emitter(&self) -> Option<&ProtectedEventEmitter>

Get the protected emitter (if configured)

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more