Skip to main content

everruns_host/session_services/
session_mutator.rs

1//! Backend-neutral mutation contract for session metadata.
2//!
3//! `SessionMutator` is the hosted control-plane service behind the `session`
4//! capability's title and live-capability-reconfigure tools. It moved out of
5//! `everruns-core` with EVE-897: the kernel never mutated a session itself, it
6//! only carried the trait on `ToolContext` so a hosted capability could reach
7//! it. That capability now resolves [`SessionMutatorExt`] from the type-keyed
8//! extension bag instead.
9//!
10//! Mutations acknowledge with the refreshed portable
11//! `everruns_core::ExecutionSession`, never a stored platform record (EVE-882).
12
13use async_trait::async_trait;
14use everruns_capability::CapabilityRef as AgentCapabilityConfig;
15use everruns_core::ExecutionSession;
16use everruns_provider::error::Result;
17use everruns_provider::typed_id::SessionId;
18
19/// Trait for updating mutable session metadata.
20///
21/// Mutations acknowledge with the refreshed portable execution view, never a
22/// stored platform record (EVE-882).
23#[async_trait]
24pub trait SessionMutator: Send + Sync {
25    /// Update a session's human-readable title.
26    async fn update_session_title(
27        &self,
28        session_id: SessionId,
29        title: String,
30    ) -> Result<ExecutionSession>;
31
32    /// Add or replace a session-scoped capability atomically.
33    ///
34    /// Session backends that support live capability reconfiguration override
35    /// this method. The default keeps existing backends source-compatible and
36    /// fails closed instead of pretending the capability was applied.
37    async fn upsert_session_capability(
38        &self,
39        _session_id: SessionId,
40        _capability: AgentCapabilityConfig,
41    ) -> Result<ExecutionSession> {
42        Err(everruns_provider::error::AgentLoopError::store(
43            "session backend does not support live capability reconfiguration",
44        ))
45    }
46
47    /// Remove a session-scoped capability atomically.
48    async fn remove_session_capability(
49        &self,
50        _session_id: SessionId,
51        _capability_id: &str,
52    ) -> Result<ExecutionSession> {
53        Err(everruns_provider::error::AgentLoopError::store(
54            "session backend does not support live capability reconfiguration",
55        ))
56    }
57}
58
59#[async_trait]
60impl<T: SessionMutator + ?Sized> SessionMutator for std::sync::Arc<T> {
61    async fn update_session_title(
62        &self,
63        session_id: SessionId,
64        title: String,
65    ) -> Result<ExecutionSession> {
66        (**self).update_session_title(session_id, title).await
67    }
68
69    async fn upsert_session_capability(
70        &self,
71        session_id: SessionId,
72        capability: AgentCapabilityConfig,
73    ) -> Result<ExecutionSession> {
74        (**self)
75            .upsert_session_capability(session_id, capability)
76            .await
77    }
78
79    async fn remove_session_capability(
80        &self,
81        session_id: SessionId,
82        capability_id: &str,
83    ) -> Result<ExecutionSession> {
84        (**self)
85            .remove_session_capability(session_id, capability_id)
86            .await
87    }
88}
89
90/// Type-keyed wrapper installed on the tool context by hosted presets.
91///
92/// Core carries the generic extension bag but does not name this service.
93#[derive(Clone)]
94pub struct SessionMutatorExt(pub std::sync::Arc<dyn SessionMutator>);