1use chrono::{DateTime, Utc};
7
8#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
10pub struct ActorName {
11 pub name: String,
13 pub org: Option<String>,
15 pub node: Option<String>,
17}
18
19#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
21pub struct EventContext {
22 pub org_id: Option<String>,
23 pub plan_id: Option<i64>,
24 pub task_id: Option<i64>,
25}
26
27#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
29pub struct DomainEvent {
30 pub actor: ActorName,
31 pub kind: EventKind,
32 pub timestamp: DateTime<Utc>,
33 pub context: EventContext,
34}
35
36#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
38#[serde(tag = "type")]
39pub enum EventKind {
40 PlanCreated {
42 plan_id: i64,
43 name: String,
44 },
45 TaskAssigned {
46 task_id: i64,
47 agent: String,
48 org: String,
49 },
50 TaskCompleted {
51 task_id: i64,
52 },
53 PlanCompleted {
54 plan_id: i64,
55 name: String,
56 },
57 WaveCompleted {
58 wave_id: i64,
59 plan_id: i64,
60 },
61
62 MessageSent {
64 from: String,
65 to: String,
66 preview: String,
67 },
68 DelegationStarted {
69 from_org: String,
70 to_org: String,
71 task: String,
72 },
73 DelegationCompleted {
74 delegation_id: String,
75 plan_id: i64,
76 peer_name: String,
77 },
78
79 AgentOnline {
81 name: String,
82 org: String,
83 node: String,
84 },
85 AgentOffline {
86 name: String,
87 reason: String,
88 },
89
90 HealthDegraded {
92 module: String,
93 reason: String,
94 },
95 BudgetAlert {
96 org: String,
97 spent: f64,
98 limit: f64,
99 },
100 ExtensionLoaded {
101 id: String,
102 version: String,
103 },
104
105 FilesClaimed {
107 task_id: i64,
108 agent: String,
109 file_paths: Vec<String>,
110 },
111 FilesReleased {
112 task_id: i64,
113 file_paths: Vec<String>,
114 },
115
116 OrgAsked {
118 org_id: String,
119 question: String,
120 intent: String,
121 escalated: bool,
122 latency_ms: u64,
123 },
124}
125
126#[derive(Debug, Clone)]
128pub struct EventFilter {
129 pub kind_prefix: Option<String>,
131 pub org: Option<String>,
133 pub actor: Option<String>,
135}
136
137pub trait DomainEventSink: Send + Sync {
140 fn emit(&self, event: DomainEvent);
141}
142
143pub fn make_event(actor_name: &str, kind: EventKind, context: EventContext) -> DomainEvent {
145 DomainEvent {
146 actor: ActorName {
147 name: actor_name.to_string(),
148 org: None,
149 node: None,
150 },
151 kind,
152 timestamp: Utc::now(),
153 context,
154 }
155}