deepstrike_core/runtime/kernel/wire/
event.rs1use serde::{Deserialize, Serialize};
4
5use super::scalar::{AttemptId, BoundedJson, DeliveryId, FiniteF64, SignalId, TaskId, WireU64};
6use super::syscall::SyscallRequest;
7
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[serde(tag = "kind", rename_all = "snake_case")]
16pub enum ExternalEvent {
17 DeliverSignal(DeliverSignal),
18 ChildCompleted(ChildCompleted),
19}
20
21#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
22#[serde(deny_unknown_fields)]
23pub struct DeliverSignal {
24 pub delivery_id: DeliveryId,
27 pub attempt: u32,
28 pub signal: LogicalSignal,
29}
30
31#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32#[serde(deny_unknown_fields)]
33pub struct LogicalSignal {
34 pub signal_id: SignalId,
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub source: Option<SignalSourceKind>,
37 #[serde(default)]
38 pub target: SignalTarget,
39 #[serde(default, skip_serializing_if = "Option::is_none")]
40 pub urgency: Option<SignalUrgency>,
41 #[serde(default, skip_serializing_if = "BoundedJson::is_null")]
42 pub payload: BoundedJson,
43 #[serde(default, skip_serializing_if = "Option::is_none")]
45 pub source_timestamp_ms: Option<WireU64>,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub dedupe_key: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
61 pub escalate_after_ms: Option<WireU64>,
62}
63
64impl LogicalSignal {
65 pub fn new(signal_id: SignalId) -> Self {
66 Self {
67 signal_id,
68 source: None,
69 target: SignalTarget::default(),
70 urgency: None,
71 payload: BoundedJson::null(),
72 source_timestamp_ms: None,
73 dedupe_key: None,
74 escalate_after_ms: None,
75 }
76 }
77
78 pub fn effective_urgency(&self, escalation_enabled: bool) -> SignalUrgency {
85 let urgency = self.urgency.unwrap_or(SignalUrgency::Normal);
86 let due_on_admission = self.escalate_after_ms.is_some_and(|after| after.get() == 0);
87 if escalation_enabled && due_on_admission {
88 urgency.escalated()
89 } else {
90 urgency
91 }
92 }
93}
94
95#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
101#[serde(tag = "kind", rename_all = "snake_case")]
102pub enum SignalTarget {
103 Operation(OperationTarget),
104 Task(TaskTarget),
105}
106
107impl Default for SignalTarget {
108 fn default() -> Self {
109 Self::Operation(OperationTarget {})
110 }
111}
112
113#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
114#[serde(deny_unknown_fields)]
115pub struct OperationTarget {}
116
117#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
118#[serde(deny_unknown_fields)]
119pub struct TaskTarget {
120 pub task_id: TaskId,
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
124#[serde(rename_all = "snake_case")]
125pub enum SignalSourceKind {
126 Cron,
127 Gateway,
128 Heartbeat,
129 Custom,
130}
131
132#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
133#[serde(rename_all = "snake_case")]
134pub enum SignalUrgency {
135 Low,
136 Normal,
137 High,
138 Critical,
139}
140
141impl SignalUrgency {
142 pub fn escalated(self) -> Self {
145 match self {
146 Self::Low => Self::Normal,
147 Self::Normal => Self::High,
148 Self::High | Self::Critical => Self::Critical,
149 }
150 }
151}
152
153#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
161#[serde(deny_unknown_fields)]
162pub struct ChildCompleted {
163 pub task_id: TaskId,
164 pub attempt_id: AttemptId,
165 pub result: ChildResult,
166 #[serde(default, skip_serializing_if = "Vec::is_empty")]
167 pub parent_requests: Vec<SyscallRequest>,
168}
169
170#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
171#[serde(deny_unknown_fields)]
172pub struct ChildResult {
173 #[serde(default)]
174 pub status: ChildStatus,
175 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub output: Option<String>,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub error: Option<String>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub usage: Option<UsageFacts>,
181 #[serde(default, skip_serializing_if = "Option::is_none")]
184 pub score: Option<FiniteF64>,
185}
186
187#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
188#[serde(rename_all = "snake_case")]
189pub enum ChildStatus {
190 #[default]
191 Completed,
192 Failed,
193 Cancelled,
194}
195
196#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
198#[serde(deny_unknown_fields)]
199pub struct UsageFacts {
200 #[serde(default, skip_serializing_if = "Option::is_none")]
201 pub input_tokens: Option<WireU64>,
202 #[serde(default, skip_serializing_if = "Option::is_none")]
203 pub output_tokens: Option<WireU64>,
204 #[serde(default, skip_serializing_if = "Option::is_none")]
205 pub turns: Option<u32>,
206}