Skip to main content

agent_sdk_core/application/
projection.rs

1//! Application-layer coordination over core primitives. Use these services to lower
2//! helpers, drive runs, validate output, coordinate tools, approvals, delivery,
3//! isolation, telemetry, and feature layers. Methods in this layer may call
4//! configured ports, mutate in-memory stores, append journals, or publish events as
5//! documented. This file contains the projection portion of that contract.
6//!
7use crate::{
8    context::{ContextItem, ContextProjection, ProjectionRole},
9    domain::{AgentError, AgentErrorKind, RetryClassification},
10    provider::{
11        ProviderMessage, ProviderMessageRole, ProviderProjectedMetadata, ProviderProjectionPolicy,
12        ProviderRequest,
13    },
14};
15
16/// Project context projection.
17/// This derives a provider projection from admitted context and does not resolve raw content or
18/// call the provider.
19pub fn project_context_projection(
20    projection: &ContextProjection,
21    policy: &ProviderProjectionPolicy,
22) -> Result<ProviderRequest, AgentError> {
23    if projection.items.is_empty() {
24        return Err(AgentError::new(
25            AgentErrorKind::ProjectionFailure,
26            RetryClassification::RepairNeeded,
27            "provider projection requires at least one admitted context item",
28        ));
29    }
30
31    Ok(ProviderRequest {
32        schema_version: ProviderRequest::SCHEMA_VERSION,
33        projection_policy_ref: policy.projection_policy_ref.clone(),
34        projection_item_count: projection.items.len(),
35        structured_output_hint: None,
36        messages: projection
37            .items
38            .iter()
39            .map(|item| project_item(item, policy))
40            .collect(),
41    })
42}
43
44fn project_item(item: &ContextItem, policy: &ProviderProjectionPolicy) -> ProviderMessage {
45    ProviderMessage {
46        role: role_for_item(item),
47        content: projected_content(item),
48        privacy: item.privacy_class,
49        projected_metadata: policy
50            .allow_private_metadata_projection
51            .then(|| projected_metadata(item)),
52    }
53}
54
55fn projected_content(item: &ContextItem) -> String {
56    item.inline_redacted_summary
57        .clone()
58        .unwrap_or_else(|| item.redacted_summary.clone())
59}
60
61fn role_for_item(item: &ContextItem) -> ProviderMessageRole {
62    match item.projection_role {
63        ProjectionRole::System => ProviderMessageRole::System,
64        ProjectionRole::Developer => ProviderMessageRole::Developer,
65        ProjectionRole::User => ProviderMessageRole::User,
66        ProjectionRole::ToolResult => ProviderMessageRole::Tool,
67        _ => ProviderMessageRole::Context,
68    }
69}
70
71fn projected_metadata(item: &ContextItem) -> ProviderProjectedMetadata {
72    ProviderProjectedMetadata {
73        source_kind: item.source_ref.kind.clone(),
74        source_id: item.source_ref.as_str().to_string(),
75        destination_kind: item.destination_ref.kind.clone(),
76        destination_id: item.destination_ref.as_str().to_string(),
77        subject_kind: format!("{:?}", item.producer_ref.kind),
78        subject_id: item.producer_ref.as_str().to_string(),
79    }
80}