1use serde::{Deserialize, Serialize};
8
9pub const CONTEXT_BUNDLE_SCHEMA_VERSION: u32 = 1;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "snake_case")]
14pub enum AgentRole {
15 Coder,
16 Reviewer,
17 Planner,
18 Researcher,
19}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
22#[serde(rename_all = "snake_case")]
23pub enum RiskClass {
24 Low,
25 Medium,
26 High,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
33#[serde(rename_all = "snake_case")]
34pub enum DegradedMode {
35 Full,
36 CanonicalOnly,
37 Blocked,
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
44#[serde(rename_all = "snake_case")]
45pub enum SourceKind {
46 Canonical,
47 Generated,
48 GraphDerived,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
52#[serde(rename_all = "snake_case")]
53pub enum ItemValidity {
54 Current,
55 Stale,
56 Superseded,
57}
58
59#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
60#[serde(rename_all = "snake_case")]
61pub enum TrustClass {
62 Trusted,
63 Standard,
64 Quarantined,
65}
66
67#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
68#[serde(rename_all = "snake_case")]
69pub enum ContextIntent {
70 SessionStart,
71 ResumeWork,
75 ExplainDecision,
76 DebugFailure,
77 ApplyPreference,
78 ReviewChange,
79 ExploreHistory,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
85#[serde(rename_all = "snake_case")]
86pub enum ChannelKind {
87 Preferences,
88 Lessons,
89 Core,
90 Workstreams,
91 MemoryIndex,
92 Sessions,
93}
94
95impl ChannelKind {
96 pub const ORDERED: [ChannelKind; 6] = [
99 ChannelKind::Preferences,
100 ChannelKind::Lessons,
101 ChannelKind::Core,
102 ChannelKind::Workstreams,
103 ChannelKind::MemoryIndex,
104 ChannelKind::Sessions,
105 ];
106}
107
108#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
109pub struct ProjectRef {
110 pub key: String,
111}
112
113#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
114pub struct ContextRequest {
115 pub schema_version: u32,
116 pub task: String,
117 pub project: ProjectRef,
118 pub branch: Option<String>,
119 pub worktree: Option<String>,
120 pub role: AgentRole,
121 pub as_of_epoch: i64,
122 pub token_budget: u32,
123 pub risk: RiskClass,
124 pub include_superseded: bool,
125}
126
127#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
130pub struct ContextItem {
131 pub stable_key: String,
132 pub channel: ChannelKind,
133 pub title: String,
134 pub text: String,
135 pub source_kind: SourceKind,
136 pub canonical_ref: Option<String>,
137 pub projection_ref: Option<String>,
138 pub evidence_refs: Vec<String>,
139 pub validity: ItemValidity,
140 pub trust: TrustClass,
141 pub project: Option<String>,
142 pub branch: Option<String>,
143}
144
145#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
146pub struct PlannedChannel {
147 pub channel: ChannelKind,
148 pub item_limit: u32,
149 pub relevance_governed: bool,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct ContextFilters {
155 pub project: String,
156 pub branch: Option<String>,
157 pub include_superseded: bool,
158 pub as_of_epoch: i64,
159}
160
161#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
163pub struct SectionBudgets {
164 pub total_tokens: u32,
165 pub preferences: u32,
166 pub lessons: u32,
167 pub core: u32,
168 pub workstreams: u32,
169 pub memory_index: u32,
170 pub sessions: u32,
171}
172
173impl SectionBudgets {
174 pub fn for_channel(&self, channel: ChannelKind) -> u32 {
175 match channel {
176 ChannelKind::Preferences => self.preferences,
177 ChannelKind::Lessons => self.lessons,
178 ChannelKind::Core => self.core,
179 ChannelKind::Workstreams => self.workstreams,
180 ChannelKind::MemoryIndex => self.memory_index,
181 ChannelKind::Sessions => self.sessions,
182 }
183 }
184}
185
186#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
187pub struct ContextPlan {
188 pub schema_version: u32,
189 pub policy_version: String,
190 pub relevance_policy_version: String,
191 pub intent: ContextIntent,
192 pub relevance_query: Option<String>,
193 pub relevance_k: u32,
194 pub channels: Vec<PlannedChannel>,
195 pub filters: ContextFilters,
196 pub section_budgets: SectionBudgets,
197 pub plan_hash: String,
199}
200
201#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
202pub struct AuditEntry {
203 pub stable_key: String,
204 pub channel: ChannelKind,
205 pub source_kind: SourceKind,
206 pub validity: ItemValidity,
207 pub selected: bool,
208 pub reason: String,
211 pub relevance_score: Option<f64>,
212 pub token_estimate: u32,
213}
214
215#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
216pub struct ContextAudit {
217 pub schema_version: u32,
218 pub policy_version: String,
219 pub relevance_policy_version: String,
220 pub plan_hash: String,
221 pub degraded_mode: DegradedMode,
222 pub candidates_considered: u32,
223 pub selected_count: u32,
224 pub dropped_count: u32,
225 pub token_estimate: u32,
226 pub token_budget: u32,
227 pub truncation_reason: Option<String>,
228 pub entries: Vec<AuditEntry>,
229}
230
231#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct ContextBundle {
233 pub schema_version: u32,
234 pub plan_hash: String,
235 pub degraded_mode: DegradedMode,
236 pub preferences: Vec<ContextItem>,
237 pub failure_lessons: Vec<ContextItem>,
238 pub current_truth: Vec<ContextItem>,
239 pub workstreams: Vec<ContextItem>,
240 pub memory_index: Vec<ContextItem>,
241 pub recent_sessions: Vec<ContextItem>,
242 pub audit: ContextAudit,
243}
244
245impl ContextBundle {
246 pub fn section_mut(&mut self, channel: ChannelKind) -> &mut Vec<ContextItem> {
247 match channel {
248 ChannelKind::Preferences => &mut self.preferences,
249 ChannelKind::Lessons => &mut self.failure_lessons,
250 ChannelKind::Core => &mut self.current_truth,
251 ChannelKind::Workstreams => &mut self.workstreams,
252 ChannelKind::MemoryIndex => &mut self.memory_index,
253 ChannelKind::Sessions => &mut self.recent_sessions,
254 }
255 }
256}