1use crate::elicitation::ElicitationRequest;
3use crate::state::*;
4use crate::usage::ProviderCost;
5use serde::{Deserialize, Serialize};
6use std::collections::BTreeMap;
7use std::sync::Arc;
8#[derive(Debug)]
11pub struct ProjectionIntegrityError(pub String);
12
13impl std::fmt::Display for ProjectionIntegrityError {
14 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 formatter.write_str(&self.0)
16 }
17}
18
19impl std::error::Error for ProjectionIntegrityError {}
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub struct StoreSchemaMismatch {
28 pub found: i64,
29 pub supported: i64,
30 pub reason: StoreSchemaMismatchReason,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum StoreSchemaMismatchReason {
35 NeedsMigration,
36 Incompatible { minimum_compatible: i64 },
37 InvalidCompatibilityMetadata,
38 Rollback { previous: i64 },
39}
40
41impl std::fmt::Display for StoreSchemaMismatch {
42 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 let Self {
44 found,
45 supported,
46 reason,
47 } = self;
48 match reason {
49 StoreSchemaMismatchReason::Incompatible { minimum_compatible } => write!(
50 formatter,
51 "Mjolnir database schema {found} requires at least build schema {minimum_compatible} for reads and writes; this build supports {supported}; upgrade Mjolnir, run this build with an isolated data directory (--instance NAME or MJ_DATA_DIR), or restore a backup made by the older build"
52 ),
53 StoreSchemaMismatchReason::NeedsMigration => write!(
54 formatter,
55 "Mjolnir database schema {found} is not the supported schema {supported}; start the Mjolnir daemon to migrate it"
56 ),
57 StoreSchemaMismatchReason::InvalidCompatibilityMetadata => write!(
58 formatter,
59 "Mjolnir database schema {found} has missing or invalid compatibility metadata; refusing access from build schema {supported}"
60 ),
61 StoreSchemaMismatchReason::Rollback { previous } => write!(
62 formatter,
63 "Mjolnir database schema rolled back from {previous} to {found} underneath this writer; refusing writes from build schema {supported}"
64 ),
65 }
66 }
67}
68
69impl std::error::Error for StoreSchemaMismatch {}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
72pub enum HistoryScope {
73 Project,
74 Session,
75 All,
76}
77
78#[derive(Debug, Clone, PartialEq, Eq)]
79pub struct PromptHistoryEntry {
80 pub id: i64,
81 pub session_id: String,
82 pub text: String,
83}
84
85#[derive(Debug, Clone, Copy, PartialEq, Eq)]
86pub enum ProjectionApplyOutcome {
87 Applied,
88 AlreadyApplied,
89}
90
91#[derive(Debug, Clone, PartialEq)]
92pub enum TranscriptMutation {
93 Upsert(TranscriptItem),
94 Remove { stable_id: String },
95}
96
97#[derive(Debug, Clone, PartialEq, Default)]
100pub struct MaterializedSessionMutation {
101 pub last_activity_at_ms: Option<i64>,
105 pub execution: Option<MaterializedExecutionState>,
106 pub session_title: Option<Option<String>>,
107 pub configuration: Option<BTreeMap<String, serde_json::Value>>,
108 pub transcript: Vec<TranscriptMutation>,
109 pub queued_prompts: Option<Vec<MaterializedQueuedPrompt>>,
110 pub pending_elicitations: Option<Vec<crate::elicitation::ElicitationRequest>>,
111 pub active_turn: Option<Option<MaterializedTurn>>,
114 pub last_turn_outcome: Option<MaterializedTurnOutcome>,
116 pub config_results: Vec<(String, Option<String>)>,
117 pub provider_cost: Option<crate::usage::ProviderCost>,
118 pub api_events: Vec<ApiEventData>,
119}
120
121#[derive(Debug, Clone, Default, PartialEq, Eq)]
123pub struct ClientSessionState {
124 pub draft: String,
125 pub through_event_ordinal: u64,
126}
127
128#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
131pub struct DetachedSessionDraft {
132 pub text: String,
133 pub inherited_input: Option<String>,
134}
135
136#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct TurnSummary {
139 pub turn_number: u64,
142 pub turn_started_at_ms: i64,
143 pub last_changed_at_ms: i64,
146 pub final_message: Option<String>,
148}
149
150#[derive(Debug, Clone)]
153pub struct TranscriptPage {
154 pub items: Vec<Arc<TranscriptItem>>,
155 pub latest_seq: u64,
158 pub next_after_seq: u64,
159 pub execution: MaterializedExecutionState,
160}
161
162#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
164pub struct TranscriptRetention {
165 pub items: usize,
166 pub bytes: usize,
167 pub remaining: bool,
170}
171
172#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
174pub struct StoredReview {
175 pub workflow: crate::second_opinion::ReviewWorkflow,
176 pub generation: u64,
180 pub context_baseline: u64,
182 pub native_lost: bool,
184 pub reviewer_transcript: Vec<std::sync::Arc<crate::state::TranscriptItem>>,
188}
189
190#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
196pub struct TurnReviewState {
197 pub baselines: std::collections::BTreeMap<std::path::PathBuf, String>,
198 pub reviewed_through_ordinal: u64,
199 pub prior_review: Option<crate::review::lanes::PriorReviewContext>,
202 pub active: Option<String>,
205 #[serde(default)]
209 pub pending_forward: Option<crate::review::driver::PendingForward>,
210}
211
212#[derive(Debug, Clone, PartialEq, Eq)]
218pub struct BoundedPromptHistory {
219 pub entries: Vec<PromptHistoryEntry>,
220 pub truncated: bool,
221}
222
223#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
224pub struct UsageCoverage {
225 pub recorded_turns: u64,
226 pub full_turn_reports: u64,
227 pub last_request_reports: u64,
228 pub unspecified_reports: u64,
229 pub missing_reports: u64,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
233pub struct UsageCounterTotal {
234 pub tokens: u64,
235 pub reported_turns: u64,
237}
238
239#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
240pub struct UsagePage {
241 pub session_id: String,
242 pub turns: Vec<MaterializedTurnOutcome>,
243 pub next_after_seq: u64,
244 pub latest_seq: u64,
245 pub totals: BTreeMap<String, UsageCounterTotal>,
247 pub coverage: UsageCoverage,
248 #[serde(default, skip_serializing_if = "Option::is_none")]
249 pub provider_session_cost: Option<ProviderCost>,
250}
251
252#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
253pub struct ApiEvent {
254 pub seq: u64,
255 pub session_id: String,
256 pub recorded_at_ms: i64,
257 #[serde(flatten)]
258 pub event: ApiEventData,
259}
260
261#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
262#[serde(tag = "type", content = "data", rename_all = "snake_case")]
263pub enum ApiEventData {
264 TurnStarted {
265 turn: MaterializedTurn,
266 },
267 TurnEnded {
268 turn: MaterializedTurnOutcome,
269 },
270 Error {
271 message: String,
272 command_id: Option<String>,
273 },
274 InputRequired {
275 request: ElicitationRequest,
276 turn_id: Option<u64>,
277 },
278 InputResolved {
279 elicitation_id: String,
280 turn_id: Option<u64>,
281 action: String,
282 },
283 ActivityChanged {
284 activity: ApiActivityState,
285 },
286}
287
288impl ApiEventData {
289 pub fn kind(&self) -> &'static str {
290 match self {
291 Self::TurnStarted { .. } => "turn_started",
292 Self::TurnEnded { .. } => "turn_ended",
293 Self::Error { .. } => "error",
294 Self::InputRequired { .. } => "input_required",
295 Self::InputResolved { .. } => "input_resolved",
296 Self::ActivityChanged { .. } => "activity_changed",
297 }
298 }
299}
300
301#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
303pub struct ApiActivityState {
304 pub state: String,
305 pub details: Option<ApiActivityDetails>,
306 pub is_idle: bool,
307 pub waiting_for_input: bool,
308 pub capacity_retry: bool,
309}
310
311#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
312#[serde(deny_unknown_fields)]
313pub struct ApiActivityDetails {
314 pub kind: ApiActivityKind,
315 #[serde(default, skip_serializing_if = "Option::is_none")]
316 pub turn_started_at_ms: Option<i64>,
317 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub step_started_at_ms: Option<i64>,
319 #[serde(default, skip_serializing_if = "Option::is_none")]
320 pub background_started_at_ms: Option<i64>,
321 #[serde(default, skip_serializing_if = "Option::is_none")]
322 pub idle_since_ms: Option<i64>,
323 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub label: Option<String>,
325}
326
327#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
328#[serde(rename_all = "lowercase")]
329pub enum ApiActivityKind {
330 Turn,
331 Step,
332 Background,
333 Idle,
334 Lifecycle,
335}
336
337#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
338pub struct ApiEventFilter {
339 pub session_id: Option<String>,
340 pub workspace_id: Option<String>,
341}
342
343#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
344pub struct ApiEventPage {
345 pub events: Vec<ApiEvent>,
346 pub next_after_seq: u64,
347 pub latest_seq: u64,
348}