Skip to main content

Workspace

Struct Workspace 

Source
pub struct Workspace { /* private fields */ }
Expand description

One workspace, resolved: the runtime it borrows, the model, and everything discovered on disk, ready to mint runs from.

Held by reference by every run it mints, so a host keeps one per repository for as long as it wants to send prompts at it. Dropping it does not end the runs already minted — a PreparedRun owns its session — but the MCP connections go with it, and so does the runtime when this held the last Arc.

Send and Sync: the runtime is shared through Arcs and creates sessions from &self, so concurrent minting from one workspace needs no lock of basis’s own.

Implementations§

Source§

impl Workspace

Source

pub async fn open(path: impl Into<PathBuf>) -> Result<Self, RunError>

Opens path with basis’s defaults: a private runtime with the provider auto-detected from the environment, the newest model it offers, and every convention discovered where convention says to look.

builder is the same call with the knobs exposed — including with_runtime, for the host that opens many workspaces on one Runtime.

Source

pub fn builder(path: impl Into<PathBuf>) -> WorkspaceBuilder

Configures a workspace before opening it.

Source

pub fn prepare(&self, spec: impl Into<RunSpec>) -> Result<PreparedRun, RunError>

Mints a run: a fresh conversation against this workspace.

Synchronous, and deliberately so — everything expensive already happened. What this does is spawn a session on the existing runtime and hand back the PreparedRun that drives it.

The spec’s prompt may be empty. Once a session outlives a turn, a conversation with nothing said yet is a real state — it is what ACP’s session/new opens — so the emptiness check belongs where a prompt is actually sent, which is PreparedRun::execute and PreparedRun::send. (The free run keeps its own up-front check, because a one-shot caller that passed nothing wants to hear about it before a session exists.)

Source

pub fn resume( &self, agent_id: &str, spec: impl Into<RunSpec>, ) -> Result<PreparedRun, RunError>

Picks up a conversation a previous process left behind.

agent_id is PreparedRun::agent_id, not the session id: mentra persists agents, and a session is one process’s view of one. Resuming replays the transcript from the store, so the first turn after this already knows everything the last one did.

The workspace has to be the one the conversation belongs to. Nothing here checks that — mentra’s store is keyed by agent, not by path — so resuming an agent under a workspace it never ran in gives it that workspace’s context and tools alongside its own history.

mentra does not persist a model’s context window (Agent::from_loaded always resumes at None — set_model is the only way back), so a resumed session starts with an unknown one. This reapplies this workspace’s own resolved model exactly when the resumed conversation is still on it and this resume is not also changing reasoning — the same model prepare would have minted — which restores mentra’s own compaction threshold as well as PreparedRun::context_window. A reasoning-changing resume leaves the window unknown so it performs one persisted mutation rather than two. A conversation PreparedRun::set_model had already moved elsewhere keeps whatever that call left it at instead: basis has no window for a model it does not resolve, and forcing this one back would silently undo a choice the caller made.

Source

pub fn fingerprint(&self) -> Snapshot

A cheap stand-in for everything in this workspace a run could see.

The utility ADR-0014 kept when watch was deleted, on the type its ledger row promised it to. The semantics are crate::fingerprint’s verbatim: a digest over git ls-files plus HEAD, stat only, and every uncertain answer resolving to changed rather than unchanged.

Fingerprints the workspace as it is now, not as it was when the workspace was opened — that is the whole point, since a caller’s loop asks it repeatedly against one long-lived workspace.

Blocking: it spawns git and stats files. An async caller belongs on a blocking thread — tokio::task::spawn_blocking, or the equivalent.

Source

pub fn path(&self) -> &Path

The path this workspace was opened with, which is what its runs are scoped to.

Source

pub fn root(&self) -> &Path

The same directory as discovery resolved it, symlinks followed. What the run header reports, and what fingerprint reads.

Source

pub fn model(&self) -> &str

The model every run from this workspace uses, resolved once.

Source

pub fn provider(&self) -> &str

Source

pub fn context(&self) -> &WorkspaceContext

The context documents discovered at open, weakest precedence first.

Source

pub fn memories(&self) -> &[Memory]

The memories discovered at open, name-ordered, a workspace memory shadowing a global one of the same name.

Frontmatter only: the index in the system prompt is what a memory costs by default, and the body stays on disk for the model — or a host showing its user what the agent remembers — to read on demand. See crate::memory for the convention.

Source

pub fn skills(&self) -> &[LoadedSkill]

The skills this workspace registered on the runtime, after layering.

Only what this workspace registered. The registry itself is the runtime’s and additive, so on a shared runtime a run may also be able to load_skill what a sibling workspace registered — an accepted consequence of sharing (see WorkspaceBuilder::open).

Source

pub fn templates(&self) -> &[Template]

The prompt templates this workspace defines, after layering, name-ordered. Over ACP these become the client’s commands.

Source

pub fn mcp_servers(&self) -> &[String]

The MCP servers connected at open, by the names that took effect — which is the configured name unless another workspace on the shared runtime already held it, in which case it carries a deterministic suffix. Names only: nothing here echoes a command or a credential.

Source

pub fn declared_tools(&self) -> &[String]

The tools this workspace’s manifests declared, by name, after layering — this workspace’s own first, name-ordered within each manifest.

Names only, for mcp_servers’s reason: nothing here echoes a command or a credential.

Source

pub fn config(&self) -> &Config

What config.json said about this workspace, and which file said it.

The answers here are already in force — the model below is what they resolved to — so this is for the host that reports its own configuration, or that wants to hand the same value to a shared Runtime’s builder rather than read the files twice.

Source

pub fn config_files(&self) -> &[ContextFile]

The config files that took effect, most specific first.

Reported the way .mcp.json’s and .basis/tools.json’s sources are, and for a milder version of their reason: what this file decides — which model, which provider — a run already names in its own header, so the question left is which file said so, and a repository that resolved a model nobody expected should be able to find out in one place.

Source

pub fn declared_tool_files(&self) -> &[ContextFile]

The tool manifests that took effect, most specific first.

A file that says which programs the model may run is the last thing that should apply invisibly, which is why discovery reports its sources the way .mcp.json’s does.

Source

pub fn bind_host_tools( self, tools: Vec<Box<dyn ExecutableTool>>, ) -> Result<Self, RunError>

Binds this reusable generation’s complete checkout-specific host-tool set before its one independent mint.

Consuming makes every failure atomic to the caller: all names are validated against each other and the fresh runtime before the first registration; an unexpected late collision drops the entire generation rather than returning a partially bound workspace. An empty vector is meaningful and marks an explicitly tool-free checkout as bound. A bound tool must finish all work before its call returns; detached work owned only by the tool is outside the tracked reuse guarantee.

Source

pub async fn rebuild_for_reuse(self) -> Result<Self, RunError>

Consumes a clean reusable generation and returns a newly built, prewarmed, unbound replacement.

Rebuild first seals the generation. Any live run, observer guard, detached event forwarder, or prior raw Mentra escape refuses reuse and consumes this entry. Workspace registrations are dropped before Basis demands unique ownership of the old runtime; that runtime is dropped before the provider factory for the replacement is called. Drop alone never invokes this method or the recipe.

This proves the lifecycle Basis owns: attached runs, observers, event forwarders, runtime registrations, and raw-access escapes. It does not claim to scrub Mentra team/background/spawn execution or host tools that detach their own work; strict reusable hosts exclude those names from their exact roster and await every custom-tool effect before returning.

Source

pub fn mentra_runtime(&self) -> &Runtime

The mentra runtime the runs are minted on, for a host that wants mentra’s own surface — the task board, teams, the store — alongside basis’s.

The same bargain as PreparedRun::session: basis does not hide mentra, and reaching past basis’s surface is a supported thing to do rather than a workaround. Renamed from runtime() when ADR-0018 gave basis a Runtime of its own, so the name says whose surface comes back.

On a reusable workspace, calling this method permanently prevents the current generation from being rebuilt. The returned runtime can mint or retain state through handles Basis cannot count, so raw access and safe reuse are deliberately mutually exclusive for that generation.

Trait Implementations§

Source§

impl Debug for Workspace

Hand-written because neither the runtime nor the registration is Debug material, and because the context documents hold whole files — a derived impl would dump them.

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. 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> 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> 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, 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, <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