pub struct HostState { /* private fields */ }Implementations§
Source§impl HostState
impl HostState
pub fn new() -> Self
pub async fn subscribe(&self, client_id: &str, channel: Arc<WsChannel>)
pub async fn unsubscribe(&self, client_id: &str)
pub async fn register_agent( &self, client_id: &str, req: RegisterHostAgentRequest, ) -> Result<HostAgent, String>
pub async fn unregister_agent( &self, caller_client_id: &str, agent_id: &str, ) -> Result<(), String>
pub async fn set_status( &self, caller_client_id: &str, req: SetHostAgentStatusRequest, ) -> Result<HostAgent, String>
Sourcepub async fn create_approval(
&self,
caller_client_id: Option<&str>,
req: CreateHostApprovalRequest,
) -> Result<HostApprovalRequest, String>
pub async fn create_approval( &self, caller_client_id: Option<&str>, req: CreateHostApprovalRequest, ) -> Result<HostApprovalRequest, String>
Create an approval owned by caller_client_id. Pass None
to mark the approval system-level — the high-risk-method
approval gate uses this so the local UI session (a different
session than the one whose dispatch is parking) can resolve
it. Audit 2026-05: prior to this caller arg, create_approval
had no notion of ownership and resolve_approval was open
to any caller, allowing cross-session approval squatting.
Sourcepub async fn resolve_approval(
&self,
caller_client_id: &str,
req: ResolveHostApprovalRequest,
) -> Result<HostApprovalRequest, String>
pub async fn resolve_approval( &self, caller_client_id: &str, req: ResolveHostApprovalRequest, ) -> Result<HostApprovalRequest, String>
Resolve an approval. ACL rules (audit 2026-05, fan-out added 2026-05-15):
- Approval has
client_id: None(system-level, e.g. raised by the high-risk-method gate) → any authed caller may resolve, since the gate’s whole point is that the user acks it via whichever session their UI happens to use. - Approval has
client_id: Some(x)AND caller ISx→ resolve directly, fireapproval.resolved. - Approval has
client_id: Some(x)AND caller is a DIFFERENT session AND owner is still subscribed → fan-out: record anapproval.resolve_requestedevent that the owning agent hooks to callresolve_approvalon its own session. The approval row stays Pending until the owner completes it. Caller gets back the pending approval (status: Pending). This unblocks UIs like CarHost that surface every approval inhost.approvals— including ones agents pushed viahost.request_approval— without breaking the squat- prevention property: only the OWNER ever mutates the row, the non-owning caller just signals intent. - Cross-session AND owner is NOT subscribed → return error identifying the disconnected owner. Caller’s UI knows the resolution can’t land right now and surfaces accordingly.
Sourcepub async fn reap_session_approvals(&self, client_id: &str) -> usize
pub async fn reap_session_approvals(&self, client_id: &str) -> usize
Reap a disconnected session’s pending approvals. Called on WS
close — covers graceful unregister+close, hard crash (TCP
reset), and ping timeout in one place. Only approvals owned
by this session (client_id == Some(client_id)) are touched;
system-level gate approvals (client_id: None) are left for
the user to act on. car-releases#48.
Sourcepub async fn reap_agent_approvals(
&self,
caller_client_id: &str,
agent_id: &str,
) -> usize
pub async fn reap_agent_approvals( &self, caller_client_id: &str, agent_id: &str, ) -> usize
Reap a specific agent’s pending approvals when it unregisters while its session stays open (agent restarts under a new id on the same WS). Scoped to approvals this caller’s session owns so it can’t cancel another session’s — or a system gate’s — work.
Sourcepub async fn request_and_wait_approval(
&self,
req: CreateHostApprovalRequest,
approve_label: &str,
timeout: Duration,
) -> Result<ApprovalOutcome, String>
pub async fn request_and_wait_approval( &self, req: CreateHostApprovalRequest, approve_label: &str, timeout: Duration, ) -> Result<ApprovalOutcome, String>
Create an approval and block until the user resolves it (or
timeout elapses).
Used by the high-risk-method gate in the WS dispatcher to make
the human a load-bearing participant in actions like
automation.run_applescript, messages.send, etc. The
outcome maps as follows:
- resolution string equals
approve_label→ApprovalOutcome::Approved - any other resolution string →
ApprovalOutcome::Denied - timeout fires before resolution →
ApprovalOutcome::TimedOut
Subscribers receive the standard approval.requested event
the moment the approval is created; the local HTML UI and
any other host shell can render approve/deny buttons that
call host.resolve_approval.
On timeout, the approval row is left in Pending on purpose
— the UI still shows it (with a “expired” hint the renderer
can derive from created_at) and the gate path returns
TimedOut so the caller surfaces a clear error.
pub async fn agents(&self) -> Vec<HostAgent>
pub async fn approvals(&self) -> Vec<HostApprovalRequest>
pub async fn events(&self, limit: usize) -> Vec<HostEvent>
pub async fn record_event( &self, kind: impl Into<String>, agent_id: Option<String>, message: impl Into<String>, payload: Value, ) -> HostEvent
Trait Implementations§
Auto Trait Implementations§
impl !Freeze for HostState
impl !RefUnwindSafe for HostState
impl Send for HostState
impl Sync for HostState
impl Unpin for HostState
impl UnsafeUnpin for HostState
impl !UnwindSafe for HostState
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
Source§impl<S> FromSample<S> for S
impl<S> FromSample<S> for S
fn from_sample_(s: S) -> S
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