agent_sdk_toolkit/resources/
executor.rs1use agent_sdk_core::{
6 AgentError, ExecutorRef, PolicyRef, ResourceReadRequest, ResourceRouteSnapshot, ResourceRouter,
7 ToolExecutionOutput, ToolExecutionRequest, ToolExecutor, ToolPackId, ToolPackKind,
8 ToolPackSnapshot,
9 domain::ContentRef,
10 policy::{CapabilityPermission, EffectClass, RiskClass},
11};
12
13use crate::{
14 packs::{ToolkitPackBundle, tool_snapshot},
15 testing::{InMemoryJsonArgumentStore, InMemoryToolkitContentStore},
16};
17
18use super::types::ResourceReaderRequest;
19
20#[derive(Clone)]
21pub struct ResourceReaderExecutor {
24 executor_ref: ExecutorRef,
25 router: ResourceRouter,
26 arguments: InMemoryJsonArgumentStore,
27 content: InMemoryToolkitContentStore,
28}
29
30impl ResourceReaderExecutor {
31 pub fn new(
35 router: ResourceRouter,
36 arguments: InMemoryJsonArgumentStore,
37 content: InMemoryToolkitContentStore,
38 ) -> Self {
39 Self {
40 executor_ref: ExecutorRef::new("executor.toolkit.resource_reader.v1"),
41 router,
42 arguments,
43 content,
44 }
45 }
46
47 pub fn pack_bundle(
51 source: agent_sdk_core::SourceRef,
52 policy_ref: PolicyRef,
53 ) -> Result<ToolkitPackBundle, AgentError> {
54 let snapshot = ToolPackSnapshot::new(
55 ToolPackId::new("toolpack.resource_reader.v1"),
56 ToolPackKind::ResourceReaders,
57 "v1",
58 source.clone(),
59 )
60 .with_resource_route(ResourceRouteSnapshot {
61 scheme: "memory".to_string(),
62 source: source.clone(),
63 permission_policy_ref: policy_ref.clone(),
64 parser_version: "toolkit.resource.memory.v1".to_string(),
65 max_bytes: 64 * 1024,
66 privacy: agent_sdk_core::PrivacyClass::ContentRefsOnly,
67 })
68 .with_tool(tool_snapshot(
69 "cap.toolkit.resource_reader",
70 "resource_read",
71 "executor.toolkit.resource_reader.v1",
72 "schema.toolkit.resource_reader.v1",
73 vec![policy_ref],
74 vec![CapabilityPermission::MemoryRead],
75 EffectClass::Read,
76 RiskClass::Medium,
77 &source,
78 ));
79 ToolkitPackBundle::from_snapshot(snapshot)
80 }
81}
82
83impl ToolExecutor for ResourceReaderExecutor {
84 fn executor_ref(&self) -> &ExecutorRef {
85 &self.executor_ref
86 }
87
88 fn execute(&self, request: &ToolExecutionRequest) -> Result<ToolExecutionOutput, AgentError> {
89 let args_ref = request.effect_intent.content_refs.first().ok_or_else(|| {
90 AgentError::missing_required_field("resource_read.argument_content_ref")
91 })?;
92 let resource_request: ResourceReaderRequest = self.arguments.get(args_ref)?;
93 let resolution = self.router.resolve(&ResourceReadRequest {
94 uri: resource_request.uri,
95 source: request.resolved_call.request.source.clone(),
96 policy_refs: request.resolved_call.route.policy_refs.clone(),
97 max_bytes: resource_request.max_bytes,
98 })?;
99 let content_ref = ContentRef::new(format!(
100 "content.{}.resource",
101 request.resolved_call.request.tool_call_id.as_str()
102 ));
103 self.content.put(content_ref.clone(), &resolution)?;
104 let mut output = ToolExecutionOutput::completed("resource reader returned content ref");
105 output.content_refs.push(content_ref);
106 Ok(output)
107 }
108}