Skip to main content

a3s_ahp/protocol/
events.rs

1use super::Fact;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4
5/// Stable lifecycle status for an agent run.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[serde(rename_all = "snake_case")]
8pub enum RunStatus {
9    Created,
10    Planning,
11    Executing,
12    Verifying,
13    Completed,
14    Failed,
15    Cancelled,
16}
17
18/// Run lifecycle event.
19///
20/// This is the durable, cross-runtime state transition contract. Runtimes may
21/// expose richer SDK-specific events, but they should be reducible to these
22/// states for harness supervision, replay, and audit.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct RunLifecycleEvent {
25    pub run_id: String,
26    pub session_id: String,
27    pub status: RunStatus,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub prompt: Option<String>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub result_summary: Option<String>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub error: Option<String>,
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub started_at: Option<String>,
36    pub updated_at: String,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub metadata: Option<HashMap<String, serde_json::Value>>,
39}
40
41/// Reference to a durable artifact owned by the runtime or harness.
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct ArtifactRef {
44    pub id: String,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub kind: Option<String>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub uri: Option<String>,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub path: Option<String>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub mime_type: Option<String>,
53    #[serde(skip_serializing_if = "Option::is_none")]
54    pub sha256: Option<String>,
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub size_bytes: Option<u64>,
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub summary: Option<String>,
59}
60
61/// Reference to evidence supporting a task or verification check.
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct EvidenceRef {
64    pub kind: String,
65    pub summary: String,
66    #[serde(skip_serializing_if = "Option::is_none")]
67    pub artifact: Option<ArtifactRef>,
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub metadata: Option<HashMap<String, serde_json::Value>>,
70}
71
72/// Stable task status used by harness-facing task list snapshots.
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
74#[serde(rename_all = "snake_case")]
75pub enum TaskStatus {
76    Pending,
77    InProgress,
78    Completed,
79    Failed,
80    Skipped,
81    Cancelled,
82}
83
84/// A single task in the authoritative harness-facing task list.
85#[derive(Debug, Clone, Serialize, Deserialize)]
86pub struct TaskItem {
87    pub id: String,
88    pub title: String,
89    pub status: TaskStatus,
90    #[serde(default, skip_serializing_if = "Vec::is_empty")]
91    pub depends_on: Vec<String>,
92    #[serde(default, skip_serializing_if = "Vec::is_empty")]
93    pub evidence: Vec<EvidenceRef>,
94    #[serde(default, skip_serializing_if = "Vec::is_empty")]
95    pub artifacts: Vec<ArtifactRef>,
96    #[serde(skip_serializing_if = "Option::is_none")]
97    pub error: Option<String>,
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub updated_at: Option<String>,
100    #[serde(skip_serializing_if = "Option::is_none")]
101    pub metadata: Option<HashMap<String, serde_json::Value>>,
102}
103
104/// Full task-list snapshot for UI rendering, replay, and audit.
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct TaskListEvent {
107    pub run_id: String,
108    pub session_id: String,
109    pub tasks: Vec<TaskItem>,
110    pub updated_at: String,
111    #[serde(skip_serializing_if = "Option::is_none")]
112    pub metadata: Option<HashMap<String, serde_json::Value>>,
113}
114
115/// Verification status for a run or check.
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
117#[serde(rename_all = "snake_case")]
118pub enum VerificationStatus {
119    Pending,
120    Running,
121    Passed,
122    Failed,
123    Skipped,
124    NeedsReview,
125}
126
127/// A single verification check with compact evidence references.
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct VerificationCheck {
130    pub id: String,
131    pub subject: String,
132    pub status: VerificationStatus,
133    #[serde(skip_serializing_if = "Option::is_none")]
134    pub command: Option<String>,
135    #[serde(skip_serializing_if = "Option::is_none")]
136    pub message: Option<String>,
137    #[serde(default, skip_serializing_if = "Vec::is_empty")]
138    pub evidence: Vec<EvidenceRef>,
139    #[serde(default, skip_serializing_if = "Vec::is_empty")]
140    pub artifacts: Vec<ArtifactRef>,
141    #[serde(skip_serializing_if = "Option::is_none")]
142    pub metadata: Option<HashMap<String, serde_json::Value>>,
143}
144
145/// Verification snapshot for a run.
146#[derive(Debug, Clone, Serialize, Deserialize)]
147pub struct VerificationEvent {
148    pub run_id: String,
149    pub session_id: String,
150    pub status: VerificationStatus,
151    #[serde(default, skip_serializing_if = "Vec::is_empty")]
152    pub checks: Vec<VerificationCheck>,
153    #[serde(default, skip_serializing_if = "Vec::is_empty")]
154    pub residual_risks: Vec<String>,
155    pub updated_at: String,
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub metadata: Option<HashMap<String, serde_json::Value>>,
158}
159
160/// Memory recall event - model needs to retrieve from memory.
161#[derive(Debug, Clone, Serialize, Deserialize)]
162pub struct MemoryRecallEvent {
163    pub session_id: String,
164    pub query: String,
165    pub memory_type: String,
166    pub max_results: usize,
167    pub working_directory: String,
168}
169
170/// Decision for memory recall events.
171#[derive(Debug, Clone, Serialize, Deserialize)]
172#[serde(tag = "decision", rename_all = "lowercase")]
173pub enum MemoryRecallDecision {
174    /// Allow recall and provide facts.
175    Allow {
176        injected_facts: Vec<Fact>,
177        #[serde(skip_serializing_if = "Option::is_none")]
178        metadata: Option<HashMap<String, serde_json::Value>>,
179    },
180    /// Block recall.
181    Block {
182        reason: String,
183        #[serde(skip_serializing_if = "Option::is_none")]
184        metadata: Option<HashMap<String, serde_json::Value>>,
185    },
186}
187
188/// Planning strategy.
189#[derive(Debug, Clone, Serialize, Deserialize)]
190#[serde(rename_all = "snake_case")]
191pub enum PlanningStrategy {
192    None,
193    StepByStep,
194    TreeOfThoughts,
195    GraphPlanning,
196    Custom(String),
197}
198
199/// Planning event - model is doing task planning.
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct PlanningEvent {
202    pub session_id: String,
203    pub task_description: String,
204    pub available_strategies: Vec<PlanningStrategy>,
205    #[serde(skip_serializing_if = "Option::is_none")]
206    pub constraints: Option<serde_json::Value>,
207}
208
209/// Decision for planning events.
210#[derive(Debug, Clone, Serialize, Deserialize)]
211#[serde(tag = "decision", rename_all = "lowercase")]
212pub enum PlanningDecision {
213    /// Allow planning with strategy.
214    Allow {
215        selected_strategy: PlanningStrategy,
216        #[serde(skip_serializing_if = "Option::is_none")]
217        planning_template: Option<serde_json::Value>,
218        #[serde(skip_serializing_if = "Option::is_none")]
219        metadata: Option<HashMap<String, serde_json::Value>>,
220    },
221    /// Block planning.
222    Block {
223        reason: String,
224        #[serde(skip_serializing_if = "Option::is_none")]
225        metadata: Option<HashMap<String, serde_json::Value>>,
226    },
227    /// Modify planning parameters.
228    Modify {
229        modified_task: String,
230        #[serde(skip_serializing_if = "Option::is_none")]
231        hints: Option<Vec<String>>,
232    },
233}
234
235/// Reasoning type.
236#[derive(Debug, Clone, Serialize, Deserialize)]
237#[serde(rename_all = "snake_case")]
238pub enum ReasoningType {
239    ChainOfThought,
240    TreeOfThoughts,
241    ReAct,
242    Reflexion,
243    Other(String),
244}
245
246/// Reasoning event - model is doing chain-of-thought reasoning.
247#[derive(Debug, Clone, Serialize, Deserialize)]
248pub struct ReasoningEvent {
249    pub session_id: String,
250    pub reasoning_type: ReasoningType,
251    pub problem_statement: String,
252    #[serde(skip_serializing_if = "Option::is_none")]
253    pub hints: Option<Vec<String>>,
254}
255
256/// Decision for reasoning events.
257#[derive(Debug, Clone, Serialize, Deserialize)]
258#[serde(tag = "decision", rename_all = "lowercase")]
259pub enum ReasoningDecision {
260    /// Allow reasoning with hints.
261    Allow {
262        #[serde(skip_serializing_if = "Option::is_none")]
263        hints: Option<Vec<String>>,
264        #[serde(skip_serializing_if = "Option::is_none")]
265        metadata: Option<HashMap<String, serde_json::Value>>,
266    },
267    /// Block reasoning.
268    Block {
269        reason: String,
270        #[serde(skip_serializing_if = "Option::is_none")]
271        metadata: Option<HashMap<String, serde_json::Value>>,
272    },
273}
274
275/// Success event - operation succeeded.
276#[derive(Debug, Clone, Serialize, Deserialize)]
277pub struct SuccessEvent {
278    pub session_id: String,
279    pub action_type: String,
280    pub action_summary: String,
281    pub duration_ms: u64,
282}
283
284/// Rate limit type.
285#[derive(Debug, Clone, Serialize, Deserialize)]
286#[serde(rename_all = "snake_case")]
287pub enum RateLimitType {
288    LlmTokenLimit,
289    LlmRequestLimit,
290    ApiRequestLimit,
291    ToolExecutionLimit,
292    Custom(String),
293}
294
295/// Rate limit event - rate limit triggered.
296#[derive(Debug, Clone, Serialize, Deserialize)]
297pub struct RateLimitEvent {
298    pub session_id: String,
299    pub limit_type: RateLimitType,
300    pub retry_after_ms: u64,
301    pub current_usage: String,
302}
303
304/// Decision for rate limit events.
305#[derive(Debug, Clone, Serialize, Deserialize)]
306#[serde(tag = "decision", rename_all = "lowercase")]
307pub enum RateLimitDecision {
308    /// Retry after delay.
309    Retry {
310        retry_after_ms: u64,
311        #[serde(skip_serializing_if = "Option::is_none")]
312        metadata: Option<HashMap<String, serde_json::Value>>,
313    },
314    /// Queue the request.
315    Queue,
316    /// Skip the action.
317    Skip { reason: String },
318}
319
320/// Confirmation type.
321#[derive(Debug, Clone, Serialize, Deserialize)]
322#[serde(rename_all = "snake_case")]
323pub enum ConfirmationType {
324    SafetyConfirm,
325    UserConfirm,
326    CostConfirm,
327    Custom(String),
328}
329
330/// Confirmation event - user confirmation needed.
331#[derive(Debug, Clone, Serialize, Deserialize)]
332pub struct ConfirmationEvent {
333    pub session_id: String,
334    pub confirmation_type: ConfirmationType,
335    pub message: String,
336    #[serde(skip_serializing_if = "Option::is_none")]
337    pub options: Option<Vec<String>>,
338}
339
340/// Decision for confirmation events.
341#[derive(Debug, Clone, Serialize, Deserialize)]
342#[serde(tag = "decision", rename_all = "lowercase")]
343pub enum ConfirmationDecision {
344    /// Escalate to human.
345    Escalate,
346    /// Approved.
347    Approve,
348    /// Rejected.
349    Reject { reason: String },
350}
351
352/// Intent detection event fired before context perception.
353///
354/// The harness can use LLM classification, keyword matching, or any custom logic
355/// to determine the user's intent.
356#[derive(Debug, Clone, Serialize, Deserialize)]
357pub struct IntentDetectionEvent {
358    pub session_id: String,
359    pub prompt: String,
360    pub workspace: String,
361    /// Optional language hint auto-detected from input.
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub language_hint: Option<String>,
364}
365
366/// Optional hints about the detected intent target.
367#[derive(Debug, Clone, Serialize, Deserialize)]
368pub struct TargetHints {
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub target_type: Option<String>,
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub target_name: Option<String>,
373    #[serde(skip_serializing_if = "Option::is_none")]
374    pub domain: Option<String>,
375}
376
377/// Decision for intent detection events.
378#[derive(Debug, Clone, Serialize, Deserialize)]
379#[serde(tag = "decision", rename_all = "lowercase")]
380pub enum IntentDetectionDecision {
381    /// Intent detected successfully.
382    Allow {
383        /// Detected intent string.
384        detected_intent: String,
385        /// Confidence score 0.0 - 1.0.
386        confidence: f32,
387        /// Optional target hints extracted from the prompt.
388        #[serde(skip_serializing_if = "Option::is_none")]
389        target_hints: Option<TargetHints>,
390    },
391    /// Skip intent detection and use local fallback.
392    Block { reason: String },
393}