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>,
    pub tool_handles: Arc<ToolHandleRegistry>,
    /* 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).

§tool_handles: Arc<ToolHandleRegistry>

Detached tool invocations (C2): a ToolCall with a streaming/long_running invocation mode is registered here and its handle returned as the action’s output while the DAG proceeds. Chunks are drained via Runtime::tool_poll, cancelled via Runtime::tool_cancel, and fanned out to Runtime::subscribe_tool_events subscribers.

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 register_tool_deny_in_session( &self, session_id: &str, name: &str, tool: &str, check: PolicyCheck, description: &str, ) -> Result<(), String>

Self::register_policy_in_session for a check that forbids tool outright, so PolicyEngine::blanket_denied_tools can read it back. Separate method rather than an extra parameter: the existing signature is public and crosses the bindings, and this is purely additive.

Source

pub async fn unregister_policy( &self, name: &str, session_id: Option<&str>, ) -> Result<usize, String>

Remove a policy by name. session_id targets a session’s policy set; None targets the global one.

Returns how many policies were dropped, or Err if session_id names a session that doesn’t exist. Session-scoped policies could always be dropped wholesale by Self::close_session, but a global policy had no removal path at all — once registered it lived until the process exited, so a mistyped or over-broad rule could only be cleared by restarting the daemon (Parslee-ai/car#623).

Source

pub async fn list_policies( &self, session_id: Option<&str>, ) -> Result<Vec<(String, String)>, String>

Registered policies as (name, description). session_id lists a session’s set; None lists the global one. Without this a client could register a policy but never ask what was in force, so a rejection could not be explained beyond its single message (Parslee-ai/car#623).

Source

pub async fn load_project_policies( &self, car_dir: impl AsRef<Path>, ) -> Result<usize, PolicyLoadError>

Load declarative deny rules from a project’s .car/policies/ directory and register them on the global policy engine (EPIC A / task A2).

car_dir is a .car directory; this looks for car_dir/policies/*.toml. The caller chooses the directory and there is no walk-up here — this joins the path it is given, once. The two production callers pick differently: the daemon passes $HOME/.car, and the assistant passes its working directory’s .car (--dir, else cwd). So a rule file at a repository root does not govern a car do run started from a subdirectory. Say which directory you mean at the call site rather than assuming discovery.

A missing directory is not an error (returns 0). A malformed rule file is an error — a dropped security rule must surface loudly. Returns the number of rules registered.

These rules are additive on top of any code-registered policies, and every one of them is a prohibition — though allow_tool_param states its prohibition as an allowlist, denying everything about its tool that it does not name. Once A9 makes policy violations blocking at admission, a matching action refuses the proposal.

Source

pub async fn install_information_flow_gate( &self, car_dir: impl AsRef<Path>, ) -> Result<(), FlowLoadError>

Load information-flow tool labels from a project’s .car directory and register the information-flow admission gate (EPIC A / A3+A4).

Reads car_dir/tool-labels.json (merged over built-in defaults) and registers an crate::flow::InformationFlowGate so every admitted proposal is checked for data exfiltration (blocked) and forbidden tool orderings (escalated to approval). A missing labels file is fine — the built-in defaults still mark the network tools as sinks. A malformed file is a loud error.

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_message_sink(self, sink: Arc<dyn MessageSink>) -> Self

Attach an outbound message sink, making messaging.send a real tool on this runtime.

Registering the schema here — and ONLY here — is deliberate. A tool the runtime cannot execute must not be advertised to the model: without a sink the dispatch arm can only return an error, and a model that keeps seeing “message the human” in its tool list will keep trying to use it. So sink and schema arrive together, and neither exists alone.

The entry is AskUser with side effects: reaching a human is irreversible, so the conservative default is the right one, and hosts that have their own consent model can re-register with a different permission. category = "messaging" groups it for the capability surfaces that filter by category.

Follows Self::with_inference’s non-async registration pattern (try_write during construction, while the locks are uncontended) — making the builders async would break every Runtime::new().with_…() chain in the workspace for no gain.

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 trajectory_store(&self) -> Option<&Arc<TrajectoryStore>>

The attached trajectory store, if any.

Writing traces was always the point of the store; reading them back is what makes them a feedback signal rather than an audit log.

Source

pub fn tool_feedback(&self, window_days: u32) -> Option<ToolFeedback>

Per-tool success rates observed over the last window_days, or None when no trajectory store is attached.

Dispatch-conditional (see ToolFeedback::dispatched_from_trajectories) — rejected and skipped actions are excluded, because the tool never ran and a consumer that models rejection separately would otherwise count it twice.

The window exists because a success rate is a claim about how a tool behaves now. An API that was broken for a week six months ago and has been fixed since should not still be dragging its own rate down, and an unbounded history makes the rate progressively less responsive to exactly the recent change an operator is trying to see. 30 days is the suggested default at the call sites.

Reads from disk on every call — parsing is bounded by the window (day files outside it are skipped by filename, never opened), and the callers are interactive rather than hot-path. If that stops being true, cache here rather than at each call site.

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_substrate(self, substrate: Arc<dyn Substrate>) -> Self

Bind the execution substrate the side-effecting built-in tools (read_file/write_file/edit_file/list_dir/find_files/ grep_files) act within (builder). Defaults to crate::substrate::LocalSubstrate; bind e.g. crate::substrate::McpSubstrate to make those tools hit a VM. calculate stays pure and ignores the substrate.

Source

pub async fn set_substrate(&self, substrate: Arc<dyn Substrate>)

Set the execution substrate at runtime.

Source

pub async fn substrate(&self) -> Arc<dyn Substrate>

Clone the currently bound substrate.

Source

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

Source

pub fn with_shared_event_log(self, log: Arc<TokioMutex<EventLog>>) -> Self

Bind an event log the caller already holds a handle to.

Self::with_event_log takes ownership, which leaves no way for anything outside the runtime to read what was recorded. That was fine while the log was observability-only, but a model-callable events surface has to run inside a ToolExecutor — and the executor is constructed before the runtime that would own it, so the two can only meet through a handle created first and shared into both (Parslee-ai/car#815).

Source

pub fn event_log_handle(&self) -> Arc<TokioMutex<EventLog>>

The runtime’s event log handle, for callers that need to read the record the runtime is writing.

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 set_transaction_check_mode(&self, mode: TransactionCheckMode)

Set the pre-execution transactional conflict-check mode (survey §4.3/§5.2.4). Off (default) preserves prior behavior; Warn records conflicts as telemetry; Strict rejects a conflicting proposal before executing it.

Source

pub async fn register_admission_gate(&self, gate: Arc<dyn AdmissionGate>)

Register a proposal-admission gate (EPIC A / task A1).

Gates run during admission, before any action executes, in the order they were registered. Each is a verified pre-execution safety check — information-flow (A4), concurrency (A5), blocking-policy (A9) — that can block a proposal or escalate it to human approval. Registering no gates leaves behavior unchanged.

Source

pub async fn clear_admission_gates(&self)

Remove all registered admission gates (primarily for tests and reconfiguration). After this, proposal admission reverts to the transactional pre-check only.

Source

pub async fn admission_gate_count(&self) -> usize

The number of currently-registered admission gates.

Source

pub async fn admission_gate_names(&self) -> Vec<String>

The name() of every registered admission gate, in registration order.

Prefer this over Self::admission_gate_count when asserting that a particular gate is installed: a bare count couples the assertion to every other gate the runtime happens to register, so adding one breaks unrelated tests that only cared about their own.

Source

pub async fn install_intent_gate(&self, config: IntentGateConfig)

Register the VIGIL intent gate (arXiv 2601.05755 — the live verify-before-commit call-site). Installs crate::intent_gate::IntentGate as an admission gate: a forbidden capability or a tool-stream-influenced out-of-intent action hard-rejects the proposal; untainted drift escalates to the durable approval flow (A7) by content-bound fingerprint. Replans are covered automatically (gates re-run on every replanned proposal).

This is also where the runtime’s crate::taint::TaintLedger is installed. From here on, every successful action records whether its result was tainted and which state keys it wrote, and the gate marks any incoming action that READS a tainted key as tool-stream- influenced. That closes the two holes the static untrusted_tools list leaves open: a trusted tool laundering attacker-controlled content out of state, and a replanned proposal that carries no dependency edge back to the poisoned action. Installing no intent gate installs no ledger, so nothing changes for a runtime that doesn’t configure VIGIL.

Calling this again REPLACES the installed gate; it does not add a second one. Reinstalling is a real operation — an operator editing .car/intent.json and re-running Runtime::install_intent_gate_from_project lands here — and each call builds a fresh ledger that becomes the only one the executor writes to. Appending would leave the previous gate registered while holding an orphaned ledger, and because gate aggregation is fail-closed that frozen gate’s verdict would still win: every key tainted before the reload would stay tainted forever, no trusted overwrite could clear it, and legitimate out-of-intent work would hard-refuse with no approval path. So the runtime holds exactly one intent gate, and it is always the one bound to the live ledger. The replacement keeps the previous gate’s position in the admission order, so a reload never silently reorders the other gates.

Source

pub async fn taint_ledger(&self) -> Option<Arc<TaintLedger>>

The runtime’s taint provenance ledger, when an intent gate is installed (see Runtime::install_intent_gate). None otherwise — the ledger is strictly opt-in, alongside VIGIL.

Source

pub async fn install_intent_gate_from_project( &self, car_dir: impl AsRef<Path>, ) -> Result<bool, IntentLoadError>

Load .car/intent.json from car_dir and install the intent gate when present. Absent file → Ok(false) (opt-in, ungated); malformed file → loud error, never a silently-ungated session.

Source

pub async fn install_skill_ceiling_gate(&self) -> bool

Register the skill deployment-tier ceiling gate (EPIC A / A8).

Requires a memgine to be attached (skills live there). When a proposal names its driving skill in context["skill"], the gate caps the proposal’s actions at that skill’s persisted deployment_tier, escalating an over-ceiling action to the durable approval flow (A7). Returns false if no memgine is attached.

Source

pub async fn tool_poll(&self, handle_id: &str) -> Option<ToolPollResult>

Drain buffered chunks + status for a detached tool invocation (C2). None for an unknown or fully-consumed handle. See crate::tool_handles::ToolHandleRegistry::poll for the consume-on-terminal contract.

Source

pub async fn tool_cancel(&self, handle_id: &str) -> bool

Request cancellation of a detached tool invocation (C2): fires the handle’s cancel token (dropping the executor’s chunk receiver) and seals its status as cancelled unless already terminal. Returns false for an unknown handle.

Source

pub fn subscribe_tool_events(&self) -> Receiver<ToolStreamEvent>

Subscribe to the live car_ir::ToolStreamEvent fanout for all detached tool invocations on this runtime (C2). The WS layer forwards these as tools.stream.event notifications.

Source

pub async fn enable_event_log_hash_chaining(&self)

Enable tamper-evident hash chaining on the event log (EPIC A / A9). Every event appended from now on is linked to its predecessor by a content hash, so an after-the-fact edit to a chained event — or an interior deletion/reordering — is detectable via Runtime::verify_event_log_chain. Truncation at either end of the log (dropping a prefix or suffix wholesale) is NOT detectable — the chain has no anchored head hash and no trusted tail witness; that is out of scope until the chain head is anchored. Opt-in: existing logs stay byte-identical until enabled.

Source

pub async fn verify_event_log_chain(&self) -> Result<usize, usize>

Verify the event log’s tamper-evidence chain (EPIC A / A9). Returns Ok(n) for n verified chained events, or Err(index) naming the first event whose hash/linkage doesn’t match — the point of an interior edit, deletion, or reordering. Head/tail truncation is not detectable (no anchored head hash; the first chained event’s prev_hash is taken on trust) — see EventLog::verify_chain.

Source

pub async fn set_approval_ledger_path( &self, path: impl Into<PathBuf>, ) -> Result<()>

Install a durable HITL approval ledger backed by a JSONL journal (EPIC A / A7). Loads any existing decisions so approvals survive a restart, then resolves future admission-gate NeedsApproval verdicts against it. The canonical daemon path is ~/.car/approvals.jsonl.

Source

pub async fn set_approval_ledger_in_memory(&self)

Use an in-memory approval ledger (no persistence) — primarily for tests and ephemeral runtimes.

Source

pub async fn approve_admission( &self, fingerprint: &str, reviewer: &str, reason: &str, ) -> Result<(), String>

Record a human approval for an admission fingerprint (EPIC A / A7). A subsequently re-submitted proposal whose escalation matches this fingerprint is admitted without asking again.

Source

pub async fn reject_admission( &self, fingerprint: &str, reviewer: &str, reason: &str, ) -> Result<(), String>

Record a human rejection for an admission fingerprint (EPIC A / A7). A proposal whose escalation matches a rejected fingerprint is blocked outright.

Source

pub async fn set_idempotency_cache_path( &self, path: impl Into<PathBuf>, ) -> Result<usize>

Back the idempotency cache with a durable JSONL journal (EPIC A / C3).

Loads any existing entries into the in-memory cache, then persists future idempotent results (and rollback invalidations, as tombstones) to the journal. After a crash-restart, re-submitting a completed idempotent action returns the cached result instead of re-executing it — preventing duplicate external side effects. The canonical daemon path is ~/.car/idempotency.jsonl. Returns the number of live entries loaded.

Source

pub async fn admission_decision( &self, fingerprint: &str, ) -> Option<ApprovalDecision>

Look up the current decision for an admission fingerprint, if any.

Source

pub async fn verify_tool_receipts( &self, claims: &[ToolClaim], proposal_id: Option<&str>, ) -> ReceiptReport

Verify a model’s tool-use claims against the runtime’s own execution receipts (EPIC A / A6 — arXiv 2603.10060). The runtime owns tool execution and logs it, so it holds unforgeable ground truth: this projects car_eventlog::tool_receipts::ToolReceipts from the event log and cross-checks the supplied claims, catching a fabricated tool reference, a misstated result count, or a false “found nothing”.

proposal_id scopes the cross-check window to a single proposal’s events (pass the proposal whose response the claims came from) — a claim is never judged against another run’s receipts. The check is retention-coherent (review A6): when the log has trimmed events and the window can’t be proven complete (unscoped, or the proposal’s ProposalReceived marker — which precedes every receipt of that proposal — was itself evicted), a claim without a receipt comes back in ReceiptReport::ungroundable (“window evicted”) instead of being mis-flagged fabricated_tool_reference.

Returns the car_eventlog::tool_receipts::ReceiptReport; when it is not grounded, a ToolReceiptHallucination event is emitted so the caller’s verdict→action loop (reject/flag the response) is auditable. Deterministic, zero-inference. Claims arrive structured — CAR’s thesis is that intent is structured IR, so a caller extracts claims from the model’s tool_calls / IR rather than regexing prose.

Source

pub async fn set_harness_config(&self, cfg: HarnessConfig)

Install a harness operating config — the live end of the Evolution Agent loop (survey §3.5). After the meta-agent’s HarnessConfig::apply produces a governed, regression-gated config, hand it here to take effect: max_retries/retry_backoff_ms drive the per-action retry loop, and planning_max_replans is mapped onto the replan budget. Every HarnessConfig knob is consumed here — none is aspirational.

Source

pub async fn harness_config(&self) -> Option<HarnessConfig>

The current harness operating config, if one has been installed.

Source

pub async fn update_harness_config<R>( &self, f: impl FnOnce(&mut HarnessConfig) -> R, ) -> R

Atomically read-modify-write the harness operating config under ONE write lock (installing the default first when none is set). The get→mutate→set alternative is a lost-update race when two requests mutate concurrently — the daemon’s evolution.run harness-apply path uses this instead (kernel review S3). Keeps the same replan-budget propagation as Self::set_harness_config.

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 unregister_tool(&self, name: &str) -> bool

Remove a tool from both the canonical registry and the legacy tools schema map, so the model no longer sees it and the validator no longer accepts it. Used when a remote MCP connector tool is disabled or its connector is removed. Returns true if the tool was present in either store.

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_session_and_stable_replan_id( &self, proposal: &ActionProposal, session_id: &str, ) -> ProposalResult

Execute an active-run proposal while requiring every accepted replan to retain the authenticated proposal id already claimed by the server.

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_with_stable_replan_id( &self, proposal: &ActionProposal, ) -> ProposalResult

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 execute_scoped_with_stable_replan_id( &self, proposal: &ActionProposal, scope: &RuntimeScope, ) -> ProposalResult

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.

Source§

impl Runtime

Source

pub async fn gather_goal_inputs(&self, gather: &GoalGather) -> GoalInputs

Project a GoalInputs from this runtime’s ground truth plus the caller-supplied checks. Reads receipts from the event log (Runtime::verify_tool_receipts), the live state snapshot, and — when a proposal is supplied — transactional consistency. Never fabricates a signal: a check whose input wasn’t requested is left None, so the pure evaluator fails it closed.

Source

pub async fn record_goal_evaluated( &self, goal: &str, condition: &GoalCondition, iteration: u32, met: bool, grounded: bool, reason: &str, model: &str, )

Append a durable goal-verifier result to the runtime event log.

This is the audit counterpart to the live GoalEvaluated stream event: hosts can miss a websocket frame, but the session journal still records why the verifier continued or accepted the run. model/model_tier attribute the verdict to the model that produced the turn, so an ungrounded completion is queryable as “fails on local, passes on cloud” — the same provenance record_turn_completed stamps on the default path.

Source

pub async fn record_turn_completed( &self, decision: &str, stop_reason: Option<&str>, was_truncated: bool, turns: u32, model: &str, )

Append a durable completion-decision record for an assistant/coder turn loop. The default (goal-less) loop declares success the instant the model emits no tool calls, with no truncation or outcome check — so a truncated or turn-capped run is indistinguishable from a real finish at the terminal. Recording decision/stop_reason/was_truncated/turns makes that decision a first-class, queryable event: the false-success and never-finished signals live here on the ungrounded default path, which emits no GoalEvaluated.

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

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

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: Sized + 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: Sized + 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> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

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 = !

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

fn try_from(value: U) -> Result<T, !>

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