agent-sdk-toolkit 0.1.0-alpha.3

Optional tool-pack helpers layered over agent-sdk-core primitive contracts.
Documentation
//! Resource-reader executor. Use this module to resolve explicit resource URIs into
//! content refs through a host-provided resolver and policy. Reads may touch backing
//! stores selected by the resolver; network fetches remain host-owned.
//!
use agent_sdk_core::{
    AgentError, ExecutorRef, PolicyRef, ResourceReadRequest, ResourceRouteSnapshot, ResourceRouter,
    ToolExecutionOutput, ToolExecutionRequest, ToolExecutor, ToolPackId, ToolPackKind,
    ToolPackSnapshot,
    domain::ContentRef,
    policy::{CapabilityPermission, EffectClass, RiskClass},
};

use crate::{
    packs::{ToolkitPackBundle, tool_snapshot},
    testing::{InMemoryJsonArgumentStore, InMemoryToolkitContentStore},
};

use super::types::ResourceReaderRequest;

#[derive(Clone)]
/// Resource resource reader executor request or result value.
/// Creating the value does not fetch content; resource executors document resolver and content-store effects.
pub struct ResourceReaderExecutor {
    executor_ref: ExecutorRef,
    router: ResourceRouter,
    arguments: InMemoryJsonArgumentStore,
    content: InMemoryToolkitContentStore,
}

impl ResourceReaderExecutor {
    /// Creates a new resources::executor value with explicit
    /// caller-provided inputs. This constructor is data-only and
    /// performs no I/O or external side effects.
    pub fn new(
        router: ResourceRouter,
        arguments: InMemoryJsonArgumentStore,
        content: InMemoryToolkitContentStore,
    ) -> Self {
        Self {
            executor_ref: ExecutorRef::new("executor.toolkit.resource_reader.v1"),
            router,
            arguments,
            content,
        }
    }

    /// Returns the pack bundle currently held by this value.
    /// This returns the toolkit pack bundle that registers the executor routes; it does not
    /// activate the pack itself.
    pub fn pack_bundle(
        source: agent_sdk_core::SourceRef,
        policy_ref: PolicyRef,
    ) -> Result<ToolkitPackBundle, AgentError> {
        let snapshot = ToolPackSnapshot::new(
            ToolPackId::new("toolpack.resource_reader.v1"),
            ToolPackKind::ResourceReaders,
            "v1",
            source.clone(),
        )
        .with_resource_route(ResourceRouteSnapshot {
            scheme: "memory".to_string(),
            source: source.clone(),
            permission_policy_ref: policy_ref.clone(),
            parser_version: "toolkit.resource.memory.v1".to_string(),
            max_bytes: 64 * 1024,
            privacy: agent_sdk_core::PrivacyClass::ContentRefsOnly,
        })
        .with_tool(tool_snapshot(
            "cap.toolkit.resource_reader",
            "resource_read",
            "executor.toolkit.resource_reader.v1",
            "schema.toolkit.resource_reader.v1",
            vec![policy_ref],
            vec![CapabilityPermission::MemoryRead],
            EffectClass::Read,
            RiskClass::Medium,
            &source,
        ));
        ToolkitPackBundle::from_snapshot(snapshot)
    }
}

impl ToolExecutor for ResourceReaderExecutor {
    fn executor_ref(&self) -> &ExecutorRef {
        &self.executor_ref
    }

    fn execute(&self, request: &ToolExecutionRequest) -> Result<ToolExecutionOutput, AgentError> {
        let args_ref = request.effect_intent.content_refs.first().ok_or_else(|| {
            AgentError::missing_required_field("resource_read.argument_content_ref")
        })?;
        let resource_request: ResourceReaderRequest = self.arguments.get(args_ref)?;
        let resolution = self.router.resolve(&ResourceReadRequest {
            uri: resource_request.uri,
            source: request.resolved_call.request.source.clone(),
            policy_refs: request.resolved_call.route.policy_refs.clone(),
            max_bytes: resource_request.max_bytes,
        })?;
        let content_ref = ContentRef::new(format!(
            "content.{}.resource",
            request.resolved_call.request.tool_call_id.as_str()
        ));
        self.content.put(content_ref.clone(), &resolution)?;
        let mut output = ToolExecutionOutput::completed("resource reader returned content ref");
        output.content_refs.push(content_ref);
        Ok(output)
    }
}