Skip to main content

a3s_code_core/agent_api/
projected_ui.rs

1//! Host inspection handle for one exact projected UI generation.
2
3use std::fmt;
4use std::sync::Arc;
5
6use super::AgentSession;
7use crate::capability::{
8    CapabilityDescriptor, CapabilityId, CapabilityKind, CapabilityValue, CodeCatalogGeneration,
9    ScopeCloseReport, SessionCapabilityRun, Sha256Digest, UiBinding, UiDocument,
10    UseCapabilityGeneration,
11};
12use crate::error::Result;
13
14/// Non-clone host handle retaining one exact Code projection and A3S Use lease.
15///
16/// The handle exposes immutable path-free content and the exact dependency
17/// edges selected at admission. It grants no renderer, filesystem, network,
18/// process, secret, state, or backend-message authority. The embedding host
19/// must close it after the complete render or interaction window drains.
20#[must_use = "a projected UI handle must remain alive for the complete host render window"]
21pub struct ProjectedUiHandle {
22    descriptor: CapabilityDescriptor,
23    binding: Arc<UiBinding>,
24    capability_run: SessionCapabilityRun,
25}
26
27impl ProjectedUiHandle {
28    pub fn capability_id(&self) -> &CapabilityId {
29        self.descriptor.id()
30    }
31
32    pub fn public_name(&self) -> &str {
33        self.binding.public_name()
34    }
35
36    pub fn title(&self) -> &str {
37        self.binding.title()
38    }
39
40    pub fn description(&self) -> &str {
41        self.binding.description()
42    }
43
44    pub fn icon(&self) -> &str {
45        self.binding.icon()
46    }
47
48    pub fn order(&self) -> i32 {
49        self.binding.order()
50    }
51
52    pub fn document(&self) -> &UiDocument {
53        self.binding.document()
54    }
55
56    pub fn surface_digest(&self) -> &Sha256Digest {
57        self.binding.surface_digest()
58    }
59
60    /// Exact Tool, Skill, MCP, and Flow edges authorized for this UI value.
61    pub fn dependencies(&self) -> &[CapabilityId] {
62        self.descriptor.dependencies()
63    }
64
65    pub fn catalog_generation(&self) -> CodeCatalogGeneration {
66        self.capability_run.projection().set().generation()
67    }
68
69    pub fn use_generation(&self) -> Option<&UseCapabilityGeneration> {
70        self.capability_run.run_scope().use_generation()
71    }
72
73    /// Whether Session shutdown has cancelled this host render window.
74    pub fn is_cancelled(&self) -> bool {
75        self.capability_run
76            .run_scope()
77            .cancellation()
78            .is_cancelled()
79    }
80
81    /// Close the exact Code/Use scope after the host has drained this UI.
82    pub async fn close(self) -> Result<ScopeCloseReport> {
83        self.capability_run.close().await.map_err(Into::into)
84    }
85}
86
87impl fmt::Debug for ProjectedUiHandle {
88    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
89        formatter
90            .debug_struct("ProjectedUiHandle")
91            .field("capability_id", &self.capability_id())
92            .field("public_name", &self.public_name())
93            .field("catalog_generation", &self.catalog_generation())
94            .field("use_generation", &self.use_generation())
95            .field("surface_digest", &self.surface_digest())
96            .finish_non_exhaustive()
97    }
98}
99
100impl AgentSession {
101    /// Admit one exact named UI value from the current atomic catalog.
102    ///
103    /// Missing names return `None` without acquiring an A3S Use lease. The
104    /// returned handle pins the selected descriptor, bytes, Code generation,
105    /// and exact Use generation across later N+1 publication.
106    pub async fn projected_ui(&self, public_name: &str) -> Result<Option<ProjectedUiHandle>> {
107        let Some(admitted) = self
108            .admit_projected_host_capability(CapabilityKind::Ui, public_name, |value| match value {
109                CapabilityValue::Ui(binding) => Some(Arc::clone(binding)),
110                _ => None,
111            })
112            .await?
113        else {
114            return Ok(None);
115        };
116
117        Ok(Some(ProjectedUiHandle {
118            descriptor: admitted.descriptor,
119            binding: admitted.value,
120            capability_run: admitted.capability_run,
121        }))
122    }
123}