pub struct OperatorSpawnerFactory { /* private fields */ }Expand description
Factory for AgentKind::Operator. Looks up the Arc<dyn Operator>
pre-registered under spec.operator_ref and wraps it in an
OperatorSpawner. Also resolves AgentDef.profile.worker_binding into
a WorkerBinding at compile time and fails loud (CompileError::InvalidSpec)
when the resolved operator’s Operator::requires_worker_binding is true
and no binding was declared.
Spec shape:
{ "operator_ref": "main_ai" } // Operator id pre-registered with the factory§The only axis that reaches an Operator
This factory (OperatorSpawnerFactory → OperatorSpawner) is the
AgentSpec axis: a kind = Operator AgentDef names its seat
through spec.operator_ref, and at compile() time an
Arc<dyn Operator> for that seat is placed in routes[agent_name].
Because the agent.md loader (agent_md_loader) defaults kind to
Operator, agents that flow in through external agent.md files land
here.
There was a second axis until recently: OperatorDelegateMiddleware,
opted into with spawner_hints.layers = ["operator_delegate"], which
ignored ctx.agent and handed every spawn in the session to one
backend named at launch. When both were effective it sat at the outer
end of the stack and bypassed inner.spawn entirely, leaving this
factory’s routes entry inert — so the two axes needed an exclusivity
story, and an author needed to know which one a given Blueprint was
actually running on.
That axis was removed (it resolved its destination from the launch
record rather than the Run’s seat, so a handover could not move it,
and it had no per-agent spawner with which to render an agent’s
system_prompt). Declaring its key is now a
CompileError::RemovedSpawnerHint. One axis remains, so there is no
exclusivity rule left to get wrong: if a dispatch reaches an
Operator, it reached it through the agent’s declared seat.
§Who answers spec.operator_ref — resolver first, registry second
spec.operator_ref names a Blueprint-declared seat
(Blueprint.operators[]), and a seat’s holder is per-Run, mutable
state. Two ways to answer it:
- A
OperatorSlotResolverinstalled viaSelf::set_slot_resolver— the host hands back an indirection that resolves the seat’s current holder on every dispatch (mlua-swarm-server’sAssigneeRouter). What this factory bakes intoroutes[agent_name]is then “the seat”, not “the session that held it when the Blueprint compiled” — model §4.3 A10. This is the wiringmse serveuses. - This factory’s own
id → Arc<dyn Operator>map (no resolver installed) — the direct binding used by hosts with no Run store to resolve holders against:mse bp doctor’s lint stubs, in-process embeddings, tests.
The two never mix within one build: an installed resolver that cannot serve a seat fails the compile rather than falling through to the map, because falling through is exactly how a dispatch ends up at a backend the caller never named.
Interior mutability is provided by Arc<RwLock>s. Even after the
factory has been stored as Arc<dyn SpawnerFactory> in
SpawnerRegistry, a caller holding an Arc clone can still add
Operator backends dynamically via register_operator(&self, id, op) or
install the resolver. build() performs a read() lookup each time.
Implementations§
Source§impl OperatorSpawnerFactory
impl OperatorSpawnerFactory
Sourcepub fn set_slot_resolver(
&self,
resolver: Arc<dyn OperatorSlotResolver>,
) -> &Self
pub fn set_slot_resolver( &self, resolver: Arc<dyn OperatorSlotResolver>, ) -> &Self
Install the OperatorSlotResolver every kind = Operator agent’s
spec.operator_ref is answered through from now on (see the struct
doc). Installing replaces any previous resolver.
Takes &self on purpose: the host builds its factory before it has
a RunStore to resolve holders against (the router builder resolves
the store), and the same Arc is already inside a SpawnerRegistry
by then.
Sourcepub fn resolve_operator(
&self,
slot: &str,
agent: &str,
) -> Result<Arc<dyn Operator>, CompileError>
pub fn resolve_operator( &self, slot: &str, agent: &str, ) -> Result<Arc<dyn Operator>, CompileError>
The Arc<dyn Operator> a kind = Operator agent declaring
operator_ref = slot dispatches through — the single lookup
SpawnerFactory::build performs, exposed so a host can assert
what its wiring resolves to without standing up a compile.
agent only shapes the error message (CompileError::InvalidSpec
is keyed by agent name).
Sourcepub fn register_operator(
&self,
id: impl Into<String>,
op: Arc<dyn Operator>,
) -> &Self
pub fn register_operator( &self, id: impl Into<String>, op: Arc<dyn Operator>, ) -> &Self
Register an Operator backend dynamically through &self.
Overwrites are allowed — later wins. Callers can still reach this
after the factory has been stored as Arc<dyn SpawnerFactory> in
SpawnerRegistry, as long as they hold an Arc clone; interior
mutability is provided by the inner RwLock.
Sourcepub fn unregister_operator(&self, id: &str) -> &Self
pub fn unregister_operator(&self, id: &str) -> &Self
Dynamically unregister an id (used to clean up when a WebSocket disconnects, for example). A missing id is a no-op.
Trait Implementations§
Source§impl Default for OperatorSpawnerFactory
impl Default for OperatorSpawnerFactory
Source§impl SpawnerFactory for OperatorSpawnerFactory
impl SpawnerFactory for OperatorSpawnerFactory
Source§fn build(
&self,
agent_def: &AgentDef,
_hint: Option<&Value>,
) -> Result<Arc<dyn SpawnerAdapter>, CompileError>
fn build( &self, agent_def: &AgentDef, _hint: Option<&Value>, ) -> Result<Arc<dyn SpawnerAdapter>, CompileError>
No build hint is read here: an Operator agent’s whole input is its
declared seat (spec.operator_ref) plus its profile. The hint slot
used to carry a launch-scoped session pin, which was the compile
baking a destination — see the struct doc.
Source§impl SpawnerFactoryKind for OperatorSpawnerFactory
impl SpawnerFactoryKind for OperatorSpawnerFactory
Source§const KIND: AgentKind = AgentKind::Operator
const KIND: AgentKind = AgentKind::Operator
AgentKind this factory handles — used as the HashMap key
by SpawnerRegistry::register.Source§type Worker = OperatorWorker
type Worker = OperatorWorker
AgentKind — this
binds the type chain all the way from AgentKind down to Worker.
Every factory declares it so the AgentKind → Worker mapping is
explicit across all four layers. It is the source of truth for
preserving the concrete type right up until SpawnerAdapter::spawn
erases it into Box<dyn Worker>.Auto Trait Implementations§
impl Freeze for OperatorSpawnerFactory
impl RefUnwindSafe for OperatorSpawnerFactory
impl Send for OperatorSpawnerFactory
impl Sync for OperatorSpawnerFactory
impl Unpin for OperatorSpawnerFactory
impl UnsafeUnpin for OperatorSpawnerFactory
impl UnwindSafe for OperatorSpawnerFactory
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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