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