pub struct LaunchEnvelope {Show 15 fields
pub id: SessionId,
pub operator_id: String,
pub role: Role,
pub attached_at: u64,
pub last_seen: u64,
pub attached: bool,
pub owned_task_ids: Vec<StepId>,
pub token_fp: String,
pub operator_kind: Option<OperatorKind>,
pub runtime_agent_kinds: HashMap<String, OperatorKind>,
pub bp_agent_kinds: HashMap<String, OperatorKind>,
pub bp_global_kind: Option<OperatorKind>,
pub bridge_id: Option<String>,
pub hook_id: Option<String>,
pub operator_backend_id: Option<String>,
}Expand description
Everything one launch bakes for its own dispatches: identity, role,
heartbeat bookkeeping, owned tasks, and the OperatorKind cascade
inputs plus registry IDs used to rebuild OperatorInfo on dispatch
(see Engine::resolve_operator_info).
§Why this is an envelope and not a session
It was called OperatorSession, and the name said the wrong thing on
both halves. It is not the Operator session — that is
WSOperatorSession, one per live WS connection, which outlives any
number of launches and is reachable by sid. This is minted by
Engine::attach* once per launch, keyed by a SessionId nobody
dispatches to, and read only to rebuild the launch’s own Ctx. Two
launches driven by the same operator have two of these; a handover
changes neither.
So the name now says what it holds: the launch-time envelope its
dispatches are opened from. The SessionId in Self::id stays — it
is the engine’s own token/session bookkeeping key, and renaming that
axis is a different change from renaming this type.
Fields§
§id: SessionIdUnique session identifier (distinct from the token nonce).
operator_id: StringCaller-supplied name identifying the Operator (not necessarily unique across sessions).
role: RoleRole the session’s token was minted with.
attached_at: u64Unix timestamp (seconds) when the session was attached.
last_seen: u64Unix timestamp (seconds) of the last heartbeat/attach touch.
attached: boolWhether the session is currently considered live. Flipped to
false by detach or by start_detach_loop on a heartbeat miss.
owned_task_ids: Vec<StepId>Task IDs started by this session (via start_task while this
session’s token was current).
token_fp: StringFingerprint (CapToken::fingerprint, SHA-256 of the nonce) of the
CapToken this session was attached with; used to look sessions up
by token in with_state closures. Holds the fingerprint rather
than the nonce so the session table carries no secret material
(issue #14).
operator_kind: Option<OperatorKind>The Operator’s kind, plus IDs of
the SeniorBridge / SpawnHook registered on the engine’s
BridgeRegistry. Persisted (all String; no Arc<dyn ...>). At
dispatch_attempt time the engine looks these up in the registry
and builds an OperatorInfo to inject into Ctx.
§4-tier OperatorKind cascade — “Runtime Global” tier
This field is the literal value passed to Engine::attach_with_ids’s
kind parameter, and is fed to crate::core::ctx::collapse_operator_kind
as the runtime_global tier verbatim: Some(_) is always an
explicit Runtime Global request that outranks both BP tiers — even
Some(OperatorKind::Automate) — and None means “not requested”,
letting the BP-level tiers (bp_agent_kinds / bp_global_kind) take
over. #[serde(default)] keeps existing persisted sessions (from
before this field existed / was Option) deserializing as None.
See crate::core::ctx::collapse_operator_kind for the full cascade +
rationale.
runtime_agent_kinds: HashMap<String, OperatorKind>“Runtime Agent-level” tier (highest priority) of the OperatorKind
cascade — per-agent override supplied at task-launch time via
TaskLaunchInput.operator_kind_overrides / TaskApplicationInput .operator_kind_overrides. Keyed by AgentDef.name.
bp_agent_kinds: HashMap<String, OperatorKind>“BP Agent-level” tier of the OperatorKind cascade — baked at
TaskLaunchService::launch time from Blueprint.operators[].kind,
resolved per-agent via AgentDef.spec.operator_ref. Keyed by
AgentDef.name (not OperatorDef.name).
bp_global_kind: Option<OperatorKind>“BP Global” tier of the OperatorKind cascade — baked at
TaskLaunchService::launch time from Blueprint.default_operator_kind.
bridge_id: Option<String>ID of the Arc<dyn SeniorBridge> registered on the engine’s
BridgeRegistry, if any; resolved back into OperatorInfo.senior_bridge.
hook_id: Option<String>ID of the Arc<dyn SpawnHook> registered on the engine’s
BridgeRegistry, if any; resolved back into OperatorInfo.spawn_hook.
operator_backend_id: Option<String>ID of the Arc<dyn Operator> registered on the OperatorRegistry.
Nothing resolves this at dispatch any more. It was
OperatorDelegateMiddleware’s input — the middleware looked the id
up per spawn and delegated the whole spawn to operator.execute —
and that layer was removed precisely because reading a launch-time
id meant the delegate axis could not follow a seat handover. What
the field still does is (a) travel in the persisted session blob,
whose shape old records are deserialized against, and (b) name the
key Engine::list_operator_ids validates a launch’s operator_sid
against. A dispatch reaches its Operator through the agent’s
declared seat and the Run’s current holder, not through here.
Its one source is the launch’s operator_sid
(TaskLaunchInput::operator_sid), which is where the three former
spellings of this value were folded together. The name stays
registry-shaped because that is what the field is at this layer —
a key into Engine.operators, sibling to Self::bridge_id and
Self::hook_id — and because the key space it indexes is a
superset of the WS sids: an embedder can register_operator under
any name and launch against it.
Trait Implementations§
Source§impl Clone for LaunchEnvelope
impl Clone for LaunchEnvelope
Source§fn clone(&self) -> LaunchEnvelope
fn clone(&self) -> LaunchEnvelope
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for LaunchEnvelope
impl Debug for LaunchEnvelope
Source§impl<'de> Deserialize<'de> for LaunchEnvelope
impl<'de> Deserialize<'de> for LaunchEnvelope
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Auto Trait Implementations§
impl Freeze for LaunchEnvelope
impl RefUnwindSafe for LaunchEnvelope
impl Send for LaunchEnvelope
impl Sync for LaunchEnvelope
impl Unpin for LaunchEnvelope
impl UnsafeUnpin for LaunchEnvelope
impl UnwindSafe for LaunchEnvelope
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> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
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