use std::path::{Path, PathBuf};
use std::sync::Arc;
use mentra::{
ModelInfo, RuntimePolicy, Session,
agent::AgentConfig,
runtime::{SessionOptions, SessionResumeOptions},
session::PermissionRuleScope,
tool::ToolAudience,
};
use crate::{error::RunError, shell::ShellAccess};
use super::{Runtime, builder::execution::workspace_policy};
#[derive(Debug, Clone)]
pub(crate) struct SessionScope {
pub(crate) identifier: String,
pub(crate) policy: RuntimePolicy,
}
impl SessionScope {
pub(crate) fn audience(&self) -> ToolAudience {
ToolAudience::new(self.identifier.clone())
}
}
impl Runtime {
pub(crate) fn session_policy(
&self,
workspace: &Path,
shell: ShellAccess,
memory_roots: &[PathBuf],
) -> RuntimePolicy {
self.policy_shaping
.apply_to(workspace_policy(workspace, shell, memory_roots))
}
pub(crate) fn mint(
&self,
name: impl Into<String>,
model: ModelInfo,
config: AgentConfig,
scope: &SessionScope,
) -> Result<Session, RunError> {
let options = SessionOptions {
config,
policy: Some(scope.policy.clone()),
tool_audience: Some(scope.audience()),
project_id: None,
runtime_identifier: Some(Arc::from(scope.identifier.as_str())),
};
Ok(self
.mentra
.create_session_with_options(name, model, options)?)
}
pub(crate) fn resume_minted(
&self,
agent_id: &str,
workspace: &Path,
scope: &SessionScope,
) -> Result<Session, RunError> {
let session = self.mentra.resume_session_with_options(
agent_id,
SessionResumeOptions {
project_id: None,
policy: Some(scope.policy.clone()),
tool_audience: Some(scope.audience()),
},
)?;
let based_in = session.config().workspace.base_dir.clone();
if crate::store::runtime_identifier(&based_in) != scope.identifier {
return Err(RunError::WorkspaceMismatch {
agent_id: agent_id.to_owned(),
workspace: workspace.to_path_buf(),
agent_workspace: based_in,
});
}
session
.permission_handle()
.clear_scope(PermissionRuleScope::Session)
.map_err(|error| RunError::SessionRulesNotCleared {
agent_id: agent_id.to_owned(),
error,
})?;
Ok(session)
}
}