agent-runtime-kernel 0.1.1

Internal model, context, and Tool execution Kernel for Agent Runtime
Documentation
use std::sync::Arc;

use async_trait::async_trait;
use runtime_extension_api::{
    CommandOutcome, ExtensionError, InteractionFacade, SubagentFacade, WorkspaceEntry,
    WorkspaceFacade,
};
use runtime_ports::{
    CommandRequest, InteractionRequest, InteractionSession, OperationContext, PortFailure,
    SubagentRequest, SubagentSession, WorkspaceSession,
};

pub(crate) struct PortWorkspaceFacade {
    operation: OperationContext,
    session: Arc<dyn WorkspaceSession>,
}

impl PortWorkspaceFacade {
    pub(crate) fn new(operation: OperationContext, session: Arc<dyn WorkspaceSession>) -> Self {
        Self { operation, session }
    }
}

#[async_trait]
impl WorkspaceFacade for PortWorkspaceFacade {
    async fn describe(&self) -> Result<String, ExtensionError> {
        self.session
            .describe(&self.operation)
            .await
            .map_err(map_error)
    }

    async fn read_text(&self, path: &str) -> Result<String, ExtensionError> {
        let bytes = self
            .session
            .read(&self.operation, path)
            .await
            .map_err(map_error)?;
        String::from_utf8(bytes)
            .map_err(|_| ExtensionError::Failed("workspace file is not UTF-8".into()))
    }

    async fn write_text(&self, path: &str, content: &str) -> Result<(), ExtensionError> {
        self.session
            .write(&self.operation, path, content.as_bytes())
            .await
            .map_err(map_error)
    }

    async fn list(&self, path: &str) -> Result<Vec<WorkspaceEntry>, ExtensionError> {
        self.session
            .list(&self.operation, path)
            .await
            .map(|entries| {
                entries
                    .into_iter()
                    .map(|entry| WorkspaceEntry {
                        path: entry.path,
                        is_dir: entry.is_dir,
                        size: entry.size,
                    })
                    .collect()
            })
            .map_err(map_error)
    }

    async fn search(
        &self,
        path: &str,
        query: &str,
        limit: usize,
    ) -> Result<Vec<String>, ExtensionError> {
        self.session
            .search(&self.operation, path, query, limit)
            .await
            .map_err(map_error)
    }

    async fn execute(
        &self,
        command: &str,
        cwd: Option<&str>,
    ) -> Result<CommandOutcome, ExtensionError> {
        self.session
            .execute(
                &self.operation,
                CommandRequest {
                    command: command.to_string(),
                    cwd: cwd.map(ToOwned::to_owned),
                },
            )
            .await
            .map(|output| CommandOutcome {
                exit_code: output.exit_code,
                stdout: output.stdout,
                stderr: output.stderr,
                truncated: output.truncated,
            })
            .map_err(map_error)
    }
}

pub(crate) struct PortInteractionFacade {
    operation: OperationContext,
    request_id: String,
    session: Arc<dyn InteractionSession>,
}

impl PortInteractionFacade {
    pub(crate) fn new(
        operation: OperationContext,
        request_id: String,
        session: Arc<dyn InteractionSession>,
    ) -> Self {
        Self {
            operation,
            request_id,
            session,
        }
    }
}

#[async_trait]
impl InteractionFacade for PortInteractionFacade {
    async fn request_text(&self, prompt: &str) -> Result<String, ExtensionError> {
        let response = self
            .session
            .request(
                &self.operation,
                InteractionRequest {
                    request_id: self.request_id.clone(),
                    prompt: prompt.to_string(),
                },
            )
            .await
            .map_err(map_error)?;
        Ok(response.text)
    }
}

pub(crate) struct PortSubagentFacade {
    operation: OperationContext,
    request_id: String,
    session: Arc<dyn SubagentSession>,
}

impl PortSubagentFacade {
    pub(crate) fn new(
        operation: OperationContext,
        request_id: String,
        session: Arc<dyn SubagentSession>,
    ) -> Self {
        Self {
            operation,
            request_id,
            session,
        }
    }
}

#[async_trait]
impl SubagentFacade for PortSubagentFacade {
    async fn execute(
        &self,
        prompt: &str,
        max_model_turns: usize,
    ) -> Result<String, ExtensionError> {
        self.session
            .execute(
                &self.operation,
                SubagentRequest {
                    request_id: self.request_id.clone(),
                    prompt: prompt.to_string(),
                    max_model_turns,
                },
            )
            .await
            .map(|outcome| outcome.answer)
            .map_err(map_error)
    }
}

fn map_error(error: PortFailure) -> ExtensionError {
    use runtime_ports::PortFailureKind;
    match error.kind {
        PortFailureKind::Invalid => ExtensionError::Invalid(error.message),
        PortFailureKind::Forbidden => ExtensionError::Denied(error.message),
        PortFailureKind::Canceled => ExtensionError::Canceled,
        PortFailureKind::Timeout => ExtensionError::Timeout,
        _ => ExtensionError::Failed(format!("{}: {}", error.code, error.message)),
    }
}