Skip to main content

starweaver_runtime/
dependency_assembly.rs

1//! Shared least-authority tool dependency assembly.
2
3use starweaver_context::{
4    AgentContext, AgentContextHandle, ContextMutationHandles, DependencyStore,
5};
6use starweaver_tools::{ToolDependencyProfile, ToolDependencyRequirements};
7
8/// Dependencies and mutation cells assembled for one tool invocation.
9#[derive(Clone, Debug)]
10pub struct ToolDependencyAssembly {
11    /// Typed dependencies exposed to the tool.
12    pub dependencies: DependencyStore,
13    /// Full compatibility context handle, present only for the Legacy profile.
14    pub legacy_context: Option<AgentContextHandle>,
15    /// Isolated mutable context capability cells.
16    pub context_mutations: ContextMutationHandles,
17    /// Context capability names authorized for this invocation.
18    pub authorized_context_capabilities: std::collections::BTreeSet<String>,
19}
20
21impl ToolDependencyAssembly {
22    /// Apply tool-owned context changes back to the runtime context.
23    pub fn apply_to(&self, context: &mut AgentContext) {
24        if let Some(handle) = &self.legacy_context {
25            absorb_legacy_context(context, &handle.snapshot());
26        }
27        self.context_mutations
28            .apply_to(context, &self.authorized_context_capabilities);
29    }
30}
31
32/// Assemble one named tool's dependencies from declared requirements and current host grants.
33#[must_use]
34pub fn assemble_tool_dependencies_for_name(
35    context: &AgentContext,
36    tool_name: &str,
37    requirements: &ToolDependencyRequirements,
38    strict_grant: &starweaver_context::ToolCapabilityGrant,
39) -> ToolDependencyAssembly {
40    let strict_grant = if tool_name.is_empty() {
41        strict_grant.clone()
42    } else {
43        context.tool_capability_grant(tool_name)
44    };
45    let authorized_host_capabilities = if requirements.profile == ToolDependencyProfile::Strict {
46        requirements
47            .host_capabilities
48            .intersection(&strict_grant.host_capabilities)
49            .cloned()
50            .collect()
51    } else {
52        requirements.host_capabilities.clone()
53    };
54    let authorized_context_capabilities = if requirements.profile == ToolDependencyProfile::Strict {
55        requirements
56            .context_capabilities
57            .intersection(&strict_grant.context_capabilities)
58            .cloned()
59            .collect()
60    } else {
61        requirements.context_capabilities.clone()
62    };
63    let shell_environment_authorized = requirements.shell_environment
64        && (requirements.profile != ToolDependencyProfile::Strict
65            || strict_grant.shell_environment);
66    let context_mutations = ContextMutationHandles::from_context(context);
67    let legacy_context = (requirements.profile == ToolDependencyProfile::Legacy)
68        .then(|| AgentContextHandle::new(context.clone()));
69    let mut dependencies = match requirements.profile {
70        ToolDependencyProfile::Legacy => context.tool_dependency_store(),
71        ToolDependencyProfile::Filtered => context.filtered_tool_dependency_store(
72            &authorized_host_capabilities,
73            shell_environment_authorized,
74        ),
75        ToolDependencyProfile::Strict => context.strict_tool_dependency_store(
76            &authorized_host_capabilities,
77            shell_environment_authorized,
78        ),
79    };
80    if let Some(handle) = &legacy_context {
81        dependencies.insert(handle.clone());
82    }
83    context_mutations.insert_grants(&mut dependencies, &authorized_context_capabilities);
84    ToolDependencyAssembly {
85        dependencies,
86        legacy_context,
87        context_mutations,
88        authorized_context_capabilities,
89    }
90}
91
92fn absorb_legacy_context(context: &mut AgentContext, snapshot: &AgentContext) {
93    context.usage.clone_from(&snapshot.usage);
94    context.notes.clone_from(&snapshot.notes);
95    context.state.clone_from(&snapshot.state);
96    context.tools.clone_from(&snapshot.tools);
97    context.events.clone_from(&snapshot.events);
98    context.messages.clone_from(&snapshot.messages);
99    context.metadata.clone_from(&snapshot.metadata);
100    context.agent_registry.clone_from(&snapshot.agent_registry);
101    context
102        .subagent_history
103        .clone_from(&snapshot.subagent_history);
104    context
105        .handoff_message
106        .clone_from(&snapshot.handoff_message);
107    context
108        .runtime
109        .context_manage_tool_names
110        .clone_from(&snapshot.runtime.context_manage_tool_names);
111    context
112        .runtime
113        .tool_tags
114        .clone_from(&snapshot.runtime.tool_tags);
115    context
116        .runtime
117        .wrapper_metadata
118        .clone_from(&snapshot.runtime.wrapper_metadata);
119}