use std::{path::Path, sync::Arc, time::Duration};
use crate::{
domain::{
errors::{AgentError, AgentResult, ErrorCode},
pi_rpc::{PiCommandInfo, PiRpcCommand, PiRpcResponse},
},
infrastructure::pi_rpc_probe::PiRpcProbe,
operational::pi_rpc_manager::PiRpcManager,
};
#[derive(Clone)]
pub(crate) struct PiSkillRuntime {
probe: PiRpcProbe,
manager: Arc<PiRpcManager>,
}
impl PiSkillRuntime {
pub(crate) fn new(
command: Vec<String>,
request_timeout: Duration,
manager: Arc<PiRpcManager>,
) -> Self {
Self {
probe: PiRpcProbe::new(command, request_timeout),
manager,
}
}
pub(crate) async fn get_commands(&self, workspace: &Path) -> AgentResult<Vec<PiCommandInfo>> {
match self.manager.get_commands(workspace).await {
Ok(Some(commands)) => Ok(commands),
Ok(None) => self.fresh_commands(workspace).await,
Err(error) => Err(skill_runtime_error("active Pi session", error)),
}
}
pub(crate) async fn fresh_commands(&self, workspace: &Path) -> AgentResult<Vec<PiCommandInfo>> {
if !self.probe.is_configured() {
return Err(AgentError::new(
ErrorCode::SkillFilesystemFailed,
"Pi RPC command is not configured for skill discovery",
));
}
let workspace = workspace
.canonicalize()
.map_err(|_| AgentError::new(ErrorCode::SkillScopeDenied, "skill workspace denied"))?;
let response = self
.probe
.request(
&workspace,
PiRpcCommand::GetCommands {
id: "__regy:skills:probe".into(),
},
)
.await
.map_err(|error| skill_runtime_error("ephemeral Pi probe", error))?;
match response {
PiRpcResponse::GetCommands { data, .. } => Ok(data.commands),
PiRpcResponse::Failure { error, .. } => Err(AgentError::new(
ErrorCode::SkillFilesystemFailed,
format!("Pi get_commands failed: {error}"),
)),
response => Err(AgentError::new(
ErrorCode::SkillInvalid,
format!(
"Pi returned {} while waiting for get_commands",
response.command()
),
)),
}
}
}
fn skill_runtime_error(context: &str, error: AgentError) -> AgentError {
let code = if error.message().contains("invalid Pi RPC JSON record") {
ErrorCode::SkillInvalid
} else {
ErrorCode::SkillFilesystemFailed
};
AgentError::new(
code,
format!("{context} skill discovery failed: {}", error.message()),
)
}