Skip to main content

a3s_ahp/protocol/
context.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Idle event fired when the agent has been idle for a threshold duration.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct IdleEvent {
7    /// Duration of idle time in milliseconds.
8    pub idle_duration_ms: u64,
9    /// Reason for becoming idle.
10    pub idle_reason: String,
11    /// Last event type before becoming idle.
12    #[serde(skip_serializing_if = "Option::is_none")]
13    pub last_event_type: Option<String>,
14    /// Suggested action for the idle period.
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub suggested_action: Option<String>,
17}
18
19/// Heartbeat event for periodic status updates.
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct HeartbeatEvent {
22    /// Agent uptime in milliseconds.
23    pub uptime_ms: u64,
24    /// Total events processed since start.
25    pub total_events_processed: u64,
26    /// Current agent state.
27    pub current_state: String,
28    /// CPU usage percentage (0-100).
29    #[serde(skip_serializing_if = "Option::is_none")]
30    pub cpu_percent: Option<f32>,
31    /// Memory usage in bytes.
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub memory_bytes: Option<u64>,
34    /// Currently active tool count.
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub active_tools: Option<usize>,
37    /// Pending actions in queue.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub pending_actions: Option<usize>,
40    /// Queue depth.
41    #[serde(skip_serializing_if = "Option::is_none")]
42    pub queue_depth: Option<usize>,
43    /// Tokens used in current session.
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub tokens_used: Option<i32>,
46}
47
48/// Structured context passed with events for context-aware control decisions.
49///
50/// The client exposes capabilities it chooses. The server can use exposed
51/// capabilities and should ignore unrecognized or unneeded entries.
52#[derive(Debug, Clone, Default, Serialize, Deserialize)]
53pub struct EventContext {
54    /// Recent facts or knowledge retrieved.
55    #[serde(skip_serializing_if = "Option::is_none")]
56    pub recent_facts: Option<Vec<Fact>>,
57    /// Memory/knowledge base state summary.
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub memory_summary: Option<MemorySummary>,
60    /// Session statistics.
61    #[serde(skip_serializing_if = "Option::is_none")]
62    pub session_stats: Option<SessionStats>,
63    /// Current task/goal description.
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub current_task: Option<String>,
66    /// Client-defined capabilities as arbitrary key-value pairs.
67    #[serde(skip_serializing_if = "Option::is_none")]
68    pub capabilities: Option<HashMap<String, serde_json::Value>>,
69}
70
71/// A factual memory item.
72#[derive(Debug, Clone, Serialize, Deserialize)]
73pub struct Fact {
74    pub content: String,
75    pub source: String,
76    pub confidence: f32,
77}
78
79/// Memory state summary.
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct MemorySummary {
82    pub memory_type: String,
83    pub total_items: usize,
84    pub recent_topics: Vec<String>,
85}
86
87/// Session statistics.
88#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct SessionStats {
90    pub total_actions: usize,
91    pub total_tokens: i32,
92    pub duration_ms: u64,
93    pub error_count: usize,
94}
95
96/// Decision for idle/dream events.
97#[derive(Debug, Clone, Serialize, Deserialize)]
98#[serde(tag = "decision", rename_all = "lowercase")]
99pub enum IdleDecision {
100    /// Allow background consolidation/dream.
101    Allow,
102    /// Defer idle processing.
103    Defer {
104        #[serde(skip_serializing_if = "Option::is_none")]
105        reason: Option<String>,
106    },
107}
108
109/// Perception intent - why the model needs to perceive context.
110#[derive(Debug, Clone, Serialize, Deserialize)]
111#[serde(rename_all = "snake_case")]
112pub enum PerceptionIntent {
113    /// Identify or classify an entity.
114    Recognize,
115    /// Understand semantics, logic, or behavior.
116    Understand,
117    /// Find the location or path of a resource.
118    Locate,
119    /// Recall relevant information from accumulated knowledge.
120    Retrieve,
121    /// Understand the overall structure of an environment or system.
122    Explore,
123    /// Infer causality or logic based on existing information.
124    Reason,
125    /// Confirm whether an assumption or state is correct.
126    Validate,
127    /// Compare similarities and differences.
128    Compare,
129    /// Get current status or history of a process.
130    Track,
131}
132
133impl std::fmt::Display for PerceptionIntent {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        match self {
136            PerceptionIntent::Recognize => write!(f, "recognize"),
137            PerceptionIntent::Understand => write!(f, "understand"),
138            PerceptionIntent::Locate => write!(f, "locate"),
139            PerceptionIntent::Retrieve => write!(f, "retrieve"),
140            PerceptionIntent::Explore => write!(f, "explore"),
141            PerceptionIntent::Reason => write!(f, "reason"),
142            PerceptionIntent::Validate => write!(f, "validate"),
143            PerceptionIntent::Compare => write!(f, "compare"),
144            PerceptionIntent::Track => write!(f, "track"),
145        }
146    }
147}
148
149/// Perception target - what the model wants to perceive.
150#[derive(Debug, Clone, Serialize, Deserialize)]
151#[serde(rename_all = "snake_case")]
152pub enum PerceptionTarget {
153    /// A specific object or concept.
154    Entity {
155        name: String,
156        /// Entity type, such as function, file, person, document, config, or API.
157        entity_type: String,
158        /// Identifier attribute.
159        identifier: Option<String>,
160    },
161    /// A path or place.
162    Location {
163        path: String,
164        /// Location type, such as file, directory, URL, endpoint, or region.
165        location_type: String,
166    },
167    /// Something that happened or will happen.
168    Event {
169        description: String,
170        /// Event type, such as change, action, decision, error, or meeting.
171        event_type: String,
172        /// Related time range.
173        time_range: Option<TimeRange>,
174    },
175    /// Connections between multiple entities.
176    Relation {
177        entities: Vec<String>,
178        /// Relation type, such as dependency, ownership, sequence, or conflict.
179        relation_type: String,
180    },
181    /// A policy, strategy, or convention.
182    Rule {
183        name: String,
184        /// Rule type, such as policy, convention, constraint, or requirement.
185        rule_type: String,
186        /// Applicable scope.
187        scope: Option<String>,
188    },
189    /// Current condition of a system or entity.
190    State {
191        target: String,
192        aspect: String,
193        include_history: bool,
194    },
195    /// An available capability or asset.
196    Resource {
197        name: String,
198        /// Resource type, such as tool, skill, API, data, or personnel.
199        resource_type: String,
200        constraints: Option<serde_json::Value>,
201    },
202    /// A recurring phenomenon or rule.
203    Pattern {
204        pattern: String,
205        /// Pattern type, such as text, regex, structure, or behavior.
206        pattern_type: String,
207    },
208}
209
210/// Time range for events.
211#[derive(Debug, Clone, Serialize, Deserialize)]
212pub struct TimeRange {
213    pub from: Option<i64>,
214    pub to: Option<i64>,
215    #[serde(skip_serializing_if = "Option::is_none")]
216    pub relative: Option<String>,
217}
218
219/// Perception domain - the domain of the current task.
220#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
221#[serde(rename_all = "snake_case")]
222pub enum PerceptionDomain {
223    Coding,
224    Writing,
225    DataAnalysis,
226    Research,
227    ProjectManagement,
228    Conversation,
229    Operations,
230    Security,
231    General,
232}
233
234impl Default for PerceptionDomain {
235    fn default() -> Self {
236        PerceptionDomain::General
237    }
238}
239
240/// Perception modality - the form of information needed.
241#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
242#[serde(rename_all = "snake_case")]
243pub enum PerceptionModality {
244    Text,
245    Code,
246    StructuredData,
247    Table,
248    Chart,
249    Image,
250    Audio,
251    Video,
252    Any,
253}
254
255impl Default for PerceptionModality {
256    fn default() -> Self {
257        PerceptionModality::Any
258    }
259}
260
261/// Quality requirement for perception.
262#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
263#[serde(rename_all = "snake_case")]
264pub enum PerceptionUrgency {
265    /// Must have - cannot proceed without this context.
266    Critical,
267    /// Important - significantly improves response quality.
268    High,
269    /// Helpful but not essential.
270    Normal,
271    /// Can be cached or delayed.
272    Low,
273}
274
275impl Default for PerceptionUrgency {
276    fn default() -> Self {
277        PerceptionUrgency::Normal
278    }
279}
280
281/// Freshness requirement for perceived data.
282#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
283#[serde(rename_all = "snake_case")]
284pub enum PerceptionFreshness {
285    /// Need realtime data, such as git status or filesystem state.
286    Realtime,
287    /// Need recent data, such as recent policy changes.
288    Recent,
289    /// Static data is sufficient, such as code structure or archives.
290    Static,
291}
292
293impl Default for PerceptionFreshness {
294    fn default() -> Self {
295        PerceptionFreshness::Static
296    }
297}
298
299/// Constraints for perception.
300#[derive(Debug, Clone, Serialize, Deserialize)]
301pub struct PerceptionConstraints {
302    #[serde(default)]
303    pub max_results: Option<usize>,
304    #[serde(default)]
305    pub max_context_length: Option<usize>,
306    #[serde(default = "default_include_sources")]
307    pub include_sources: bool,
308}
309
310fn default_include_sources() -> bool {
311    true
312}
313
314impl Default for PerceptionConstraints {
315    fn default() -> Self {
316        Self {
317            max_results: None,
318            max_context_length: None,
319            include_sources: true,
320        }
321    }
322}
323
324/// Current perception context.
325#[derive(Debug, Clone, Serialize, Deserialize)]
326pub struct PerceptionContext {
327    pub workspace: String,
328    #[serde(skip_serializing_if = "Option::is_none")]
329    pub current_task: Option<String>,
330    #[serde(skip_serializing_if = "Option::is_none")]
331    pub query: Option<String>,
332    #[serde(skip_serializing_if = "Option::is_none")]
333    pub relevant_history: Option<Vec<HistoryItem>>,
334}
335
336/// History item.
337#[derive(Debug, Clone, Serialize, Deserialize)]
338pub struct HistoryItem {
339    pub item_type: String,
340    pub content: String,
341    pub timestamp: i64,
342}
343
344/// Context perception event fired when the model needs workspace knowledge.
345#[derive(Debug, Clone, Serialize, Deserialize)]
346pub struct ContextPerceptionEvent {
347    pub session_id: String,
348    pub intent: PerceptionIntent,
349    pub target: PerceptionTarget,
350    #[serde(default)]
351    pub domain: PerceptionDomain,
352    #[serde(default)]
353    pub preferred_modality: PerceptionModality,
354    #[serde(default)]
355    pub urgency: PerceptionUrgency,
356    #[serde(default)]
357    pub freshness: PerceptionFreshness,
358    pub context: PerceptionContext,
359    #[serde(default)]
360    pub constraints: PerceptionConstraints,
361    #[serde(skip_serializing_if = "Option::is_none")]
362    pub metadata: Option<HashMap<String, serde_json::Value>>,
363}
364
365/// Injected context from harness.
366#[derive(Debug, Clone, Serialize, Deserialize)]
367pub struct InjectedContext {
368    pub facts: Vec<Fact>,
369    #[serde(skip_serializing_if = "Option::is_none")]
370    pub file_contents: Option<Vec<FileContentSnippet>>,
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub project_summary: Option<ProjectSummary>,
373    #[serde(skip_serializing_if = "Option::is_none")]
374    pub knowledge: Option<Vec<String>>,
375    #[serde(skip_serializing_if = "Option::is_none")]
376    pub suggestions: Option<Vec<String>>,
377}
378
379/// File content snippet.
380#[derive(Debug, Clone, Serialize, Deserialize)]
381pub struct FileContentSnippet {
382    pub path: String,
383    pub snippet: String,
384    pub relevance_score: f32,
385}
386
387/// Project summary.
388#[derive(Debug, Clone, Serialize, Deserialize)]
389pub struct ProjectSummary {
390    pub project_name: String,
391    #[serde(skip_serializing_if = "Option::is_none")]
392    pub language: Option<String>,
393    #[serde(skip_serializing_if = "Option::is_none")]
394    pub key_files: Option<Vec<String>>,
395    pub structure_description: String,
396}
397
398/// Decision for context perception events.
399#[derive(Debug, Clone, Serialize, Deserialize)]
400#[serde(tag = "decision", rename_all = "lowercase")]
401pub enum ContextPerceptionDecision {
402    /// Provide context and continue.
403    Allow {
404        injected_context: InjectedContext,
405        #[serde(skip_serializing_if = "Option::is_none")]
406        metadata: Option<HashMap<String, serde_json::Value>>,
407    },
408    /// Skip context injection.
409    Block {
410        reason: String,
411        #[serde(skip_serializing_if = "Option::is_none")]
412        metadata: Option<HashMap<String, serde_json::Value>>,
413    },
414    /// Request more specific context.
415    Refine {
416        refined_intent: Option<PerceptionIntent>,
417        refined_target: Option<PerceptionTarget>,
418        scope_hints: Vec<String>,
419    },
420}