1use std::fmt;
10
11use onemessagebus::{Message, SchemaId, Vocabulary};
12use schemars::JsonSchema;
13use serde::{Deserialize, Serialize};
14use serde_json::{Map, Value};
15
16use crate::Agent;
17
18pub use onemessagebus::ArtifactRef;
19
20pub type Envelope = onemessagebus::Envelope<Agent>;
22
23pub type EventFilter = onemessagebus::Filter<Agent>;
25
26pub type Matcher = onemessagebus::Matcher<Agent>;
29
30pub type Emitter = onemessagebus::Emitter<Agent>;
32
33pub type Reader = onemessagebus::Reader<Agent>;
35
36pub type Merge = onemessagebus::Merge<Agent>;
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
41#[serde(rename_all = "lowercase")]
42pub enum Source {
43 Agentgraph,
45 Vcs,
47 Pipeline,
49}
50
51impl Source {
52 #[must_use]
54 pub const fn every() -> [Source; 3] {
55 [Source::Agentgraph, Source::Vcs, Source::Pipeline]
56 }
57
58 #[must_use]
60 pub const fn as_str(self) -> &'static str {
61 match self {
62 Source::Agentgraph => "agentgraph",
63 Source::Vcs => "vcs",
64 Source::Pipeline => "pipeline",
65 }
66 }
67
68 #[must_use]
70 pub fn write_version(self) -> u32 {
71 Agent::write_version(&self)
72 }
73}
74
75impl fmt::Display for Source {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 f.write_str(self.as_str())
78 }
79}
80
81#[derive(
88 Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
89)]
90#[serde(rename_all = "kebab-case")]
91pub enum Phase {
92 Development,
94 Integrate,
96 Review,
98 Release,
100}
101
102impl Phase {
103 #[must_use]
105 pub const fn as_str(self) -> &'static str {
106 match self {
107 Phase::Development => "development",
108 Phase::Integrate => "integrate",
109 Phase::Review => "review",
110 Phase::Release => "release",
111 }
112 }
113
114 #[must_use]
116 pub const fn every() -> [Phase; 4] {
117 [
118 Phase::Development,
119 Phase::Integrate,
120 Phase::Review,
121 Phase::Release,
122 ]
123 }
124}
125
126impl fmt::Display for Phase {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 f.write_str(self.as_str())
129 }
130}
131
132#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
140#[serde(deny_unknown_fields)]
141pub struct Dimensions {
142 #[serde(default, skip_serializing_if = "Option::is_none")]
145 pub phase: Option<Phase>,
146}
147
148impl Dimensions {
149 #[must_use]
151 pub const fn none() -> Self {
152 Self { phase: None }
153 }
154
155 #[must_use]
157 pub const fn at(phase: Phase) -> Self {
158 Self { phase: Some(phase) }
159 }
160}
161
162impl From<Phase> for Dimensions {
163 fn from(phase: Phase) -> Self {
164 Self::at(phase)
165 }
166}
167
168impl From<Option<Phase>> for Dimensions {
169 fn from(phase: Option<Phase>) -> Self {
170 Self { phase }
171 }
172}
173
174#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize, JsonSchema)]
180pub struct Labels {
181 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub run_id: Option<String>,
184 #[serde(default, skip_serializing_if = "Option::is_none")]
186 pub round: Option<u64>,
187 #[serde(default, skip_serializing_if = "Option::is_none")]
189 pub node: Option<String>,
190 #[serde(default, skip_serializing_if = "Option::is_none")]
192 pub step: Option<String>,
193 #[serde(default, skip_serializing_if = "Option::is_none")]
195 pub member: Option<String>,
196 #[serde(default, skip_serializing_if = "Option::is_none")]
198 pub persona: Option<String>,
199 #[serde(flatten)]
201 pub extra: Map<String, Value>,
202}
203
204impl Message for Labels {
205 const SCHEMA: SchemaId = SchemaId::literal("agent", "labels", 1);
206}
207
208#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
212#[serde(deny_unknown_fields)]
213pub struct MatchFields {
214 #[serde(default, skip_serializing_if = "Option::is_none")]
216 pub phase: Option<Phase>,
217 #[serde(default, skip_serializing_if = "Option::is_none")]
219 pub run_id: Option<String>,
220 #[serde(default, skip_serializing_if = "Option::is_none")]
222 pub node: Option<String>,
223 #[serde(default, skip_serializing_if = "Option::is_none")]
225 pub step: Option<String>,
226 #[serde(default, skip_serializing_if = "Option::is_none")]
228 pub member: Option<String>,
229 #[serde(default, skip_serializing_if = "Option::is_none")]
231 pub persona: Option<String>,
232}
233
234pub trait AgentEnvelope {
237 fn phase(&self) -> Option<Phase>;
239}
240
241impl AgentEnvelope for Envelope {
242 fn phase(&self) -> Option<Phase> {
243 self.dimensions.phase
244 }
245}
246
247pub trait AgentFilter {
249 fn admits(&self, source: Source, kind: &str, labels: &Labels, phase: Option<Phase>) -> bool;
251}
252
253impl AgentFilter for EventFilter {
254 fn admits(&self, source: Source, kind: &str, labels: &Labels, phase: Option<Phase>) -> bool {
255 self.allows(&source, kind, &Dimensions { phase }, labels)
256 }
257}