Skip to main content

Runtime

Struct Runtime 

Source
pub struct Runtime {
    pub state: Arc<StateStore>,
    pub tools: Arc<RwLock<HashMap<String, ToolSchema>>>,
    pub policies: Arc<RwLock<PolicyEngine>>,
    pub session_policies: Arc<RwLock<HashMap<String, Arc<RwLock<PolicyEngine>>>>>,
    pub log: Arc<Mutex<EventLog>>,
    pub rate_limiter: Arc<RateLimiter>,
    pub result_cache: Arc<ResultCache>,
    pub registry: Arc<ToolRegistry>,
    /* private fields */
}
Expand description

Common Agent Runtime — deterministic execution layer.

Lock ordering discipline (never hold multiple simultaneously, never hold sync locks across .await):

  1. capabilities (RwLock, read-only during execution)
  2. tools (RwLock, read-only during execution)
  3. policies (RwLock, read-only during execution)
  4. session_policies (RwLock to find the per-session engine, then read inner Arc)
  5. cost_budget (RwLock, read-only during execution)
  6. log (TokioMutex, acquired/released per event)
  7. tool_executor (TokioMutex, clone Arc and drop before await)
  8. idempotency_cache (TokioMutex, acquired/released per check)

StateStore uses parking_lot::Mutex (sync) — NEVER hold across .await points.

Fields§

§state: Arc<StateStore>§tools: Arc<RwLock<HashMap<String, ToolSchema>>>§policies: Arc<RwLock<PolicyEngine>>§session_policies: Arc<RwLock<HashMap<String, Arc<RwLock<PolicyEngine>>>>>

Per-session policy registries. Hosts that multiplex multiple concurrent agent sessions over a single Runtime (IDE-style frontends with per-project rules, multi-tenant servers) call Runtime::open_session to mint an id, then Runtime::register_policy_in_session to attach session-scoped rules. Validation under that session walks the global registry AND the session’s — both must pass. Sessions can deny what global allows; sessions cannot allow what global denies. Closing a session drops its registry and any closures it holds. See docs/proposals/per-session-policy-scoping.md.

§log: Arc<Mutex<EventLog>>§rate_limiter: Arc<RateLimiter>§result_cache: Arc<ResultCache>§registry: Arc<ToolRegistry>

Canonical tool registry (optional — new code should use this).

Implementations§

Source§

impl Runtime

Source

pub fn new() -> Self

Source

pub fn with_shared( state: Arc<StateStore>, log: Arc<TokioMutex<EventLog>>, policies: Arc<TokioRwLock<PolicyEngine>>, ) -> Self

Create a runtime with shared state, event log, and policies. Each runtime gets its own tool set, executor, and idempotency cache.

Source

pub async fn open_session(&self) -> String

Mint a new session id and pre-register an empty policy engine under it. Hosts call this once per concurrent agent context (an IDE project window, a multi-tenant client, etc.) and pair it with Self::close_session when the context ends.

Returns the opaque id to pass to subsequent Self::register_policy_in_session / Self::execute_with_session calls. Ids are UUIDs so collisions across concurrent calls don’t matter.

Source

pub async fn close_session(&self, session_id: &str) -> bool

Drop the session and every policy scoped to it. Returns true if a session by that id existed; false if it didn’t (already closed, never opened, etc.). Idempotent in effect — closing a missing session is a no-op the caller is free to ignore.

Source

pub async fn register_policy_in_session( &self, session_id: &str, name: &str, check: PolicyCheck, description: &str, ) -> Result<(), String>

Register a policy under a specific session id. The policy applies only when a proposal is executed under that session; proposals executed without a session (the default) only see global policies.

Returns Err(...) if the session is unknown — callers either forgot to call Self::open_session or are using a stale/closed id.

Source

pub async fn session_exists(&self, session_id: &str) -> bool

True if a session with this id is currently open. Mostly for tests and FFI surface validation — production code should trust the id it just opened.

Source

pub fn with_inference(self, engine: Arc<InferenceEngine>) -> Self

Attach a local inference engine. Registers infer, embed, classify as built-in tools with real implementations.

Source

pub fn with_learning( self, memgine: Arc<TokioMutex<MemgineEngine>>, auto_distill: bool, ) -> Self

Attach a memgine for automatic skill learning after execution. When auto_distill is true, execution traces are automatically distilled into skills and domains are evolved when underperforming.

Source

pub fn with_memgine(self, memgine: Arc<TokioMutex<MemgineEngine>>) -> Self

Attach a memgine with auto-distillation enabled (recommended default).

Source

pub fn with_trajectory_store(self, store: Arc<TrajectoryStore>) -> Self

Attach a trajectory store for persisting execution traces.

Source

pub fn with_executor(self, executor: Arc<dyn ToolExecutor>) -> Self

Source

pub async fn set_executor(&self, executor: Arc<dyn ToolExecutor>)

Set a tool executor for the next execute() call. Used by NAPI bindings where executor varies per call.

Source

pub fn with_event_log(self, log: EventLog) -> Self

Source

pub fn with_replan( self, callback: Arc<dyn ReplanCallback>, config: ReplanConfig, ) -> Self

Attach a replan callback for failure recovery (builder).

Source

pub async fn set_replan_callback(&self, callback: Arc<dyn ReplanCallback>)

Set a replan callback at runtime.

Source

pub async fn set_replan_config(&self, config: ReplanConfig)

Set replan configuration at runtime.

Source

pub async fn register_tool(&self, name: &str)

Register a tool with just a name (backward compatible).

Source

pub async fn register_tool_schema(&self, schema: ToolSchema)

Register a tool with full schema.

Source

pub async fn register_tool_entry(&self, entry: ToolEntry)

Register a tool via the canonical registry. This is the preferred way to register tools — it updates both the registry and the legacy tools HashMap for backward compatibility.

Source

pub async fn register_agent_basics(&self)

Register CAR’s built-in agent utility stdlib.

This is an opt-in convenience layer for common local-file and text tools. Existing runtimes remain unchanged until this is called.

Source

pub async fn tool_schemas(&self) -> Vec<ToolSchema>

Get all registered tool schemas (for model prompt generation).

Source

pub async fn set_cost_budget(&self, budget: CostBudget)

Set a cost budget that limits proposal execution.

Source

pub async fn set_capabilities(&self, caps: CapabilitySet)

Set per-agent capability permissions that restrict tools, state keys, and action count.

Source

pub async fn set_rate_limit( &self, tool: &str, max_calls: u32, interval_secs: f64, )

Set a per-tool rate limit (token bucket).

max_calls tokens are available per interval_secs window. When the bucket is empty, dispatch() applies backpressure by waiting until a token refills.

Source

pub async fn enable_tool_cache(&self, tool: &str, ttl_secs: u64)

Enable cross-proposal result caching for a tool with a TTL in seconds.

Source

pub async fn execute(&self, proposal: &ActionProposal) -> ProposalResult

Execute a proposal with automatic replanning on failure.

If a ReplanCallback is registered and max_replans > 0, the runtime will catch abort failures, roll back state, ask the model for an alternative proposal via the callback, and re-execute. This transforms “execute-and-hope” into “execute-and-recover.”

If no callback is registered or max_replans == 0, behaves identically to a single execute_inner() call (zero overhead, fully backward compatible).

Source

pub async fn execute_with_session( &self, proposal: &ActionProposal, session_id: &str, ) -> ProposalResult

Execute a proposal scoped to a specific session id.

Validation walks the global policy registry plus the session’s own registry — both must pass for an action to run. Session policies can deny what global allows; they cannot allow what global denies (validation is conjunctive).

Returns the same ProposalResult shape as Self::execute. Errors with an action-level rejection if the session id is unknown — callers should check via Self::session_exists or trust an id they minted via Self::open_session.

Source

pub async fn execute_with_session_and_cancel( &self, proposal: &ActionProposal, session_id: &str, cancel: &CancellationToken, ) -> ProposalResult

Combined session-scoped + cancellable execute. The session id is passed verbatim to the per-action policy check; the cancel token behaves identically to Self::execute_with_cancel.

Source

pub async fn execute_with_cancel( &self, proposal: &ActionProposal, cancel: &CancellationToken, ) -> ProposalResult

Execute a proposal with cooperative cancellation.

The runtime checks token.is_cancelled() at each DAG level boundary. When set, every action that hadn’t yet started runs is reported as Skipped with error = "canceled: ..." so callers can distinguish “user pulled the plug” from “earlier abort cascaded.” Actions already in flight continue to completion — tool calls dispatched to user-provided executors can’t be safely interrupted from the engine.

The CAR A2A bridge uses this so tasks/cancel produces a ProposalResult with clean partial state rather than relying on JoinHandle::abort to interrupt mid-await (which leaves no record of which actions actually ran).

FFI exposure: this method is intentionally not surfaced through the NAPI / PyO3 / car-server-core JSON-RPC bindings. Those consumers (Node, Python, WebSocket) don’t currently expose long-running async-task surfaces that need cancellation; the bridge is the lone consumer. When a binding gains a long-running task surface, the path is clear: add a per-binding token registry keyed by some caller-provided id, expose cancelExecution(id) / cancel_execution(id) / proposal.cancel { id }, and have the runtime call execute_with_cancel with the matching token. Skipping that today avoids speculative API surface that bloats bindings without a consumer.

Source

pub async fn execute_scoped( &self, proposal: &ActionProposal, scope: &RuntimeScope, ) -> ProposalResult

Execute a proposal with an attached [RuntimeScope] (Parslee-ai/car#187 phase 3).

Same contract as Self::execute plus a per-execution identity surface — typically built by the car-a2a dispatcher from the verified Identity and cooperative a2a_caller metadata on the inbound ActionProposal. The scope is recorded on the event log so downstream audit / log analysis can see which caller / tenant issued each action.

What this enforces today: scope is captured + logged. Memgine queries and state-store ops still hit global namespaces — those follow-ups are tracked under #187. Tool / policy code that needs per-tenant behaviour right now should keep reading proposal.context["a2a_caller_verified"] directly (the phase 1 / 2 surface).

Source

pub async fn execute_scoped_with_cancel( &self, proposal: &ActionProposal, scope: &RuntimeScope, cancel: &CancellationToken, ) -> ProposalResult

Combined scoped + cancellable execute. Mirrors the shape of Self::execute_with_session_and_cancel for symmetry — both add a side-channel (session id / scope) on top of the cancellable form.

Source

pub async fn plan_and_execute( &self, candidates: &[ActionProposal], planner_config: Option<PlannerConfig>, feedback: Option<&ToolFeedback>, ) -> ProposalResult

Score N candidate proposals, execute the best valid one, fall back to next-best on failure. Combines car-planner scoring with engine execution.

Returns the result from whichever proposal was executed (best or fallback). If all candidates fail verification, returns an error result for the first.

Source

pub async fn save_checkpoint(&self) -> Checkpoint

Save a checkpoint of the current runtime state.

Source

pub async fn save_checkpoint_to_file(&self, path: &str) -> Result<(), String>

Save checkpoint to a JSON file.

Source

pub async fn load_checkpoint_from_file( &self, path: &str, ) -> Result<Checkpoint, String>

Load a checkpoint from a JSON file and restore state.

Source

pub async fn restore_checkpoint(&self, checkpoint: &Checkpoint)

Restore runtime state from a checkpoint.

Source

pub async fn register_subprocess_tool(&self, name: &str, tool: SubprocessTool)

Register a subprocess tool and set up the subprocess executor. If no executor exists, creates a new SubprocessToolExecutor. If one already exists, creates a new SubprocessToolExecutor with the existing executor as fallback.

Trait Implementations§

Source§

impl Default for Runtime

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
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
Source§

impl<T> ErasedDestructor for T
where T: 'static,