Skip to main content

laser_sdk/agent/
scope.rs

1use crate::agent::consumer::AgentMessage;
2use crate::agent::router::Router;
3use crate::error::LaserError;
4use crate::laser::Laser;
5use crate::provenance::{AgentTopic, Provenance};
6use crate::types::AgentId;
7use laser_wire::agent::{AgentCard, CapabilityDescriptor};
8use std::time::Duration;
9
10impl Laser {
11    /// The agent accessor: every verb on the returned scope acts as the agent
12    /// `id` (sends carry it as the source, the card and presence advertise
13    /// it). Free and synchronous, IO happens at the verbs. The coordination
14    /// identity of the fabric, never an LLM wrapper.
15    pub fn agent(&self, id: AgentId) -> AgentScope {
16        AgentScope {
17            laser: self.clone(),
18            id,
19        }
20    }
21}
22
23/// One agent identity over the fabric: publish as it, request/reply as it,
24/// advertise its card and live inbox, or open a contract from it. Build it
25/// with [`Laser::agent`]. The handler runtime itself stays
26/// [`Agent::builder`](crate::agent::Agent): this scope is the client-side
27/// face of the same identity.
28#[derive(Clone)]
29pub struct AgentScope {
30    laser: Laser,
31    id: AgentId,
32}
33
34impl AgentScope {
35    /// Append `payload` to an agent `topic` as this agent: the provenance
36    /// gains this id as its `agent` and the partition is keyed by
37    /// conversation.
38    pub async fn send(
39        &self,
40        topic: AgentTopic<'_>,
41        payload: impl Into<Vec<u8>>,
42        provenance: &Provenance,
43    ) -> Result<(), LaserError> {
44        let mut provenance = provenance.clone();
45        provenance.agent = Some(self.id.clone());
46        self.laser.send_agent(topic, payload, &provenance).await
47    }
48
49    /// Request/reply as this agent: publish to `request_topic` and await the
50    /// correlated reply on `reply_topic` up to `timeout`.
51    pub async fn ask(
52        &self,
53        request_topic: AgentTopic<'_>,
54        reply_topic: AgentTopic<'_>,
55        payload: impl Into<Vec<u8>>,
56        provenance: &Provenance,
57        timeout: Duration,
58    ) -> Result<AgentMessage, LaserError> {
59        let mut provenance = provenance.clone();
60        provenance.agent = Some(self.id.clone());
61        self.laser
62            .request(request_topic, reply_topic, payload, &provenance, timeout)
63            .await
64    }
65
66    /// Open a directed [`contract`](Laser::contract) from this agent to
67    /// `router`'s target, with `.from(..)` already set.
68    pub fn contract(&self, router: Router) -> crate::agent::contract::ContractBuilder<'_> {
69        self.laser.contract(router).from(self.id.clone())
70    }
71
72    /// Publish this agent's capability card to the registry, so capability
73    /// routing can discover it.
74    pub async fn publish_card(&self, card: &AgentCard) -> Result<(), LaserError> {
75        self.laser.publish_card(self.id.clone(), card).await
76    }
77
78    /// Advertise `capabilities` the way a spawning agent does: a durable card
79    /// on the registry plus, where the server serves presence, this agent's
80    /// live inbox at `listen_on`. Fails when this connection already belongs to
81    /// another advertised agent, so presence cannot be overwritten silently.
82    pub async fn advertise(
83        &self,
84        listen_on: AgentTopic<'static>,
85        capabilities: Vec<CapabilityDescriptor>,
86    ) -> Result<(), LaserError> {
87        crate::agent::builder::advertise(&self.laser, &self.id, &listen_on, capabilities).await
88    }
89
90    /// This agent's id.
91    pub fn id(&self) -> &AgentId {
92        &self.id
93    }
94}