a3s_code_core/agent_api/
projected_ui.rs1use 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#[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 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 pub fn is_cancelled(&self) -> bool {
75 self.capability_run
76 .run_scope()
77 .cancellation()
78 .is_cancelled()
79 }
80
81 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 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}