Skip to main content

EngineHost

Struct EngineHost 

Source
pub struct EngineHost { /* private fields */ }

Implementations§

Source§

impl EngineHost

Source

pub async fn boot(opts: HostOpts) -> Result<Arc<EngineHost>, RuntimeError>

Every step of the old setup::boot() that does NOT mention a session: profile, logging, HTTP client, registry, config, skills, MCP, extension manager. Touches NO process static: the OpenAI routing static is written by Self::install for the winning host only, and the global broker is installed in Self::foreground_runtime via apply_config, exactly where the old boot did it relative to session resolution.

Source

pub fn spawn_background_mcp_discovery(&self) -> Option<JoinHandle<usize>>

McpDiscovery::Background: start the deferred MCP discovery in a background task (at most once per host; None when nothing is pending — Inline boot, every server cached, or already spawned). The daemon calls this after it is accepting. On completion ONLY the newly discovered servers’ dormant tools are added to the shared registry, as one atomic try_register_batch (a rejected batch is logged and fails closed). Like late extension registration this mutates the shared catalog while sessions may be live: with progressive disclosure on the tools are dormant (searchable, activatable); with it off they are core tools that every live session sends from its NEXT request — one prompt-cache rewrite per live session, once. The task resolves to the number of tools registered.

Source

pub fn install(host: Arc<EngineHost>) -> Result<(), Arc<EngineHost>>

Idempotent, atomic install of the process host. Exactly one host ever wins the OnceLock; the winner’s extension manager becomes the OpenAI routing static in the same step. A different host arriving later (or losing a race) is returned as Err(rejected) and never replaces the installed one — nor touches any static.

Source

pub async fn boot_and_install( opts: HostOpts, ) -> Result<Arc<EngineHost>, RuntimeError>

The process host: the installed one, else boot + install. Under a race the loser is discarded and the installed host returned, so a caller never ends up on a host that current() does not agree with.

Source

pub fn current() -> Option<Arc<EngineHost>>

Source

pub fn flush_logs(&self)

Flush the non-blocking log writer: drops the appender guard, which drains the channel and joins the writer thread (≤ 1 s). Idempotent. Lines logged AFTER this call are dropped, so it belongs at the very last exit point (main return, panic hook), not at session shutdown.

Source

pub fn flush_installed_logs()

Self::flush_logs on the installed host, if any.

Source

pub fn parts(&self) -> &HostParts

Source

pub fn config(&self) -> Guard<Arc<SynapsConfig>>

Source

pub fn ext_manager(&self) -> &Arc<RwLock<ExtensionManager>> ⓘ

Source

pub fn command_registry(&self) -> &Arc<CommandRegistry> ⓘ

Source

pub fn keybind_registry(&self) -> &Arc<RwLock<KeybindRegistry>> ⓘ

Source

pub fn mcp_server_count(&self) -> usize

Source

pub async fn foreground_runtime(&self) -> Result<Runtime, RuntimeError>

The foreground runtime for an interactive session: shares client, tools, hook_bus, credential source, token cache, MCP/extension lease managers. Everything else is fresh (exactly Runtime::new()’s values). apply_config runs here — the ONLY place the broker is (re)installed. disabled_tools is NOT re-applied: boot() did it once on the fresh registry, before skills/MCP registered, exactly where the old boot did.

Source

pub async fn foreground_runtime_for( &self, cwd: Option<PathBuf>, ) -> Result<Runtime, RuntimeError>

foreground_runtime() for a session whose cwd is not the process cwd (daemon-hosted sessions). The cwd is set BEFORE apply_config so the memory binding (apply_memory_backend_config, one-shot) scopes to the session’s project, not the daemon’s — A-engine-merge §4. None = process cwd, byte-identical to foreground_runtime().

Source

pub async fn worker_runtime(&self) -> Result<Runtime, RuntimeError>

A worker (subagent) runtime. Shares credential source and token cache (so NO broker re-install and NO cache eviction) and takes a CLONE of the cached worker registry template. Does NOT share: the HTTP client (fresh — see below), hook_bus (fresh, empty — hooks do not fire for subagent tool calls), session_manager (fresh + reaper), mcp_runtime/extension_runtime (None, as Runtime::new()).

Own client: workers block_on a throwaway current_thread runtime and hyper parks each connection’s I/O driver on the runtime that opened it. A pooled connection born on a worker would be handed to the foreground (LIFO checkout) and die mid-stream when that worker’s runtime dropped. A per-worker pool dies with its runtime, as before.

Source

pub async fn worker_registry(&self) -> ToolRegistry

Cached-or-rebuilt worker registry: without_subagent_with_extensions is a pure function of the shared registry’s tool set, which is a pure function of its catalog generation, so caching by generation returns exactly what a rebuild would.

Source

pub fn reload_config(&self)

Re-read config from disk into the host’s snapshot. Phase 1 callers keep load_config() where they use it today.

Source

pub async fn create_session( self: &Arc<EngineHost>, cfg: SessionConfig, ) -> Result<SessionHandle, RuntimeError>

Create a session on this host: builds the SessionActor (the one Runtime + ConversationState), spawns its task, registers the handle. The task removes itself from the map when it ends.

Attach-if-live: when cfg.continue_session resolves to a session that is already running on this host (--continue X twice, Attach::Create twice, --continue latest while latest is live), the existing handle is returned and NO second actor is built. Two actors on one id would share the session file, and the second’s spawn_session_background would unlink the first’s per-session UDS and overwrite its registry entry. cfg’s other fields (cwd, model override, …) are ignored in that case — the live session wins.

Source

pub fn adopt_session(&self, handle: SessionHandle)

Register a handle built elsewhere (daemon test factories). The caller owns the task; remove_session when it ends.

Source

pub fn session_handles(&self) -> Vec<SessionHandle>

Every registered handle (B4: THE session map; DaemonState delegates here). Dead handles are dropped defensively: the actor task drops cmd_rx when run() returns and remove_session runs after — a listing in that window sees is_alive() == false. That is an ordering window, not an invariant violation, so it is logged at debug, never asserted.

Source

pub fn attach(&self, id: &SessionId) -> Option<SessionHandle>

Handle for a live session, if any.

Source

pub fn sessions(&self) -> Vec<SessionMeta>

Metadata of every live session, with the live cells filled (lifecycle, clients, input_owner, awaiting_input, journal_id).

Source

pub fn remove_session(&self, id: &SessionId) -> Option<SessionHandle>

Drop the host’s handle for a session (the actor keeps running until its command queue closes or it receives End).

Source

pub fn note_extensions_loading(&self)

Loader seam (C2): a discovery pass for this host’s manager has been dispatched. Called synchronously by spawn_discover_and_load before its task runs, so extensions_ready() cannot slip through the gap.

Source

pub fn mark_extensions_ready(&self)

Loader seam (C2): discovery finished — every extensions_ready() waiter proceeds. Idempotent.

Source

pub fn extensions_loading_guard( self: &Arc<EngineHost>, ) -> ExtensionsLoadingGuard

Loader seam (C2): note_extensions_loading() now, and mark_extensions_ready() when the guard drops — however the loader task ends (return, cancel, panic). Hold it inside the task so a panicking walk cannot strand extensions_ready() waiters.

Source

pub async fn extensions_ready(&self)

Resolve once extension discovery on this host is known-finished, so a session’s on_session_start lands on subscribed extensions. Resolves IMMEDIATELY when no loader was dispatched (hosts that never load extensions, --no-extensions, tests) or discovery already completed on the manager (in-process discover_and_load() callers such as synaps chat); otherwise awaits mark_extensions_ready().

The watch sender is a field of the host, so “sender dropped” is NOT a wake-up path while the host lives. Liveness comes from ExtensionsLoadingGuard (the loader task marks ready on any exit, including panic); callers that must not trust the loader at all (SessionActor::create) additionally bound the await with budgets::EXTENSIONS_READY_TIMEOUT.

Source

pub async fn broadcast_extension_notification( &self, ext_id: &str, method: &str, params: Value, ) -> usize

C3: push one extension notification frame to every live session (non-blocking; a session whose command queue is full drops it with a warning — widget upserts are idempotent last-writer-wins). Frames carry no session id today, hence broadcast. Returns how many sessions accepted it.

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> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> DowncastSync for T
where T: Any + Send + Sync,

Source§

fn into_any_arc(self: Arc<T>) -> Arc<dyn Any + Send + Sync> ⓘ

Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.
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> 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> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ThreadSafetyMarker for T
where T: Send,

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