agent-runtime-definition-builder 0.1.1

Internal immutable Agent definition builder
Documentation
use async_trait::async_trait;
use runtime_extension_api::{
    ContextEvent, ContextHook, ContextPatch, ContextResource, ContextSensitivity, ContextSlot,
    ExtensionContext, ExtensionError,
};
use sha2::{Digest, Sha256};

use crate::DefinitionBuildError;

/// An immutable, always-active Skill backed by UTF-8 instructions.
///
/// Use `StaticSkill` when Skill content is known while the Definition is built.
/// Selectable or execution-mutating Skills should implement `ContextHook`
/// directly and keep their state keyed by `ExecutionId`.
pub struct StaticSkill {
    id: String,
    content: String,
    version: String,
    required: bool,
    sensitivity: ContextSensitivity,
    digest: String,
}

impl StaticSkill {
    pub fn new(
        id: impl Into<String>,
        content: impl Into<String>,
    ) -> Result<Self, DefinitionBuildError> {
        let id = id.into();
        let content = content.into();
        if id.trim().is_empty() || content.trim().is_empty() {
            return Err(DefinitionBuildError::InvalidSkill(
                "id and content must not be empty".into(),
            ));
        }
        let digest = format!("sha256:{:x}", Sha256::digest(content.as_bytes()));
        Ok(Self {
            id,
            content,
            version: "1".into(),
            required: false,
            sensitivity: ContextSensitivity::Internal,
            digest,
        })
    }

    pub fn version(mut self, value: impl Into<String>) -> Self {
        self.version = value.into();
        self
    }

    pub fn required(mut self, value: bool) -> Self {
        self.required = value;
        self
    }

    pub fn sensitivity(mut self, value: ContextSensitivity) -> Self {
        self.sensitivity = value;
        self
    }
}

#[async_trait]
impl ContextHook for StaticSkill {
    fn name(&self) -> &str {
        &self.id
    }

    fn definition_fingerprint(&self) -> String {
        format!(
            "static-skill:{}:{}:{}:{:?}",
            self.version, self.digest, self.required, self.sensitivity
        )
    }

    async fn apply(
        &self,
        _: &ExtensionContext,
        _: &ContextEvent,
        _: &runtime_extension_api::ContextFrame,
    ) -> Result<ContextPatch, ExtensionError> {
        Ok(ContextPatch {
            resources: vec![ContextResource {
                id: self.id.clone(),
                slot: ContextSlot::Skills,
                source: format!("definition:skill:{}", self.id),
                content: self.content.clone(),
                required: self.required,
                sensitivity: self.sensitivity,
                version: self.version.clone(),
                digest: self.digest.clone(),
                estimated_tokens: self.content.len().div_ceil(4) as u64,
            }],
            ..ContextPatch::default()
        })
    }
}