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)]
125pub struct ClientSessionState {
126 pub draft: String,
127 pub through_event_ordinal: u64,
128}
129
130#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)]
133pub struct DetachedSessionDraft {
134 pub text: String,
135 pub inherited_input: Option<String>,
136}
137
138#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct TurnSummary {
141 pub turn_number: u64,
144 pub turn_started_at_ms: i64,
145 pub last_changed_at_ms: i64,
148 pub final_message: Option<String>,
150}
151
152#[derive(Debug, Clone)]
155pub struct TranscriptPage {
156 pub items: Vec<Arc<TranscriptItem>>,
157 pub latest_seq: u64,
160 pub next_after_seq: u64,
161 pub execution: MaterializedExecutionState,
162}
163
164#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
166pub struct TranscriptRetention {
167 pub items: usize,
168 pub bytes: usize,
169 pub remaining: bool,
172}
173
174#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
176pub struct StoredReview {
177 pub workflow: crate::second_opinion::ReviewWorkflow,
178 pub generation: u64,
182 pub context_baseline: u64,
184 pub native_lost: bool,
186 pub reviewer_transcript: Vec<std::sync::Arc<crate::state::TranscriptItem>>,
190}
191
192#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
198pub struct TurnReviewState {
199 pub baselines: std::collections::BTreeMap<std::path::PathBuf, String>,
200 pub reviewed_through_ordinal: u64,
201 pub prior_review: Option<crate::review::lanes::PriorReviewContext>,
204 pub active: Option<String>,
207 #[serde(default)]
211 pub pending_forward: Option<crate::review::driver::PendingForward>,
212}
213
214#[derive(Debug, Clone, PartialEq, Eq)]
220pub struct BoundedPromptHistory {
221 pub entries: Vec<PromptHistoryEntry>,
222 pub truncated: bool,
223}
224
225#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
226pub struct UsageCoverage {
227 pub recorded_turns: u64,
228 pub full_turn_reports: u64,
229 pub last_request_reports: u64,
230 pub unspecified_reports: u64,
231 pub missing_reports: u64,
232}
233
234#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub struct UsageCounterTotal {
236 pub tokens: u64,
237 pub reported_turns: u64,
239}
240
241#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
242pub struct UsagePage {
243 pub session_id: String,
244 pub turns: Vec<MaterializedTurnOutcome>,
245 pub next_after_seq: u64,
246 pub latest_seq: u64,
247 pub totals: BTreeMap<String, UsageCounterTotal>,
249 pub coverage: UsageCoverage,
250 #[serde(default, skip_serializing_if = "Option::is_none")]
251 pub provider_session_cost: Option<ProviderCost>,
252}
253
254#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
255pub struct ApiEvent {
256 pub seq: u64,
257 pub session_id: String,
258 pub recorded_at_ms: i64,
259 #[serde(flatten)]
260 pub event: ApiEventData,
261}
262
263#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
264#[serde(tag = "type", content = "data", rename_all = "snake_case")]
265pub enum ApiEventData {
266 TurnStarted {
267 turn: MaterializedTurn,
268 },
269 TurnEnded {
270 turn: MaterializedTurnOutcome,
271 },
272 Error {
273 message: String,
274 command_id: Option<String>,
275 },
276 InputRequired {
277 request: ElicitationRequest,
278 turn_id: Option<u64>,
279 },
280 InputResolved {
281 elicitation_id: String,
282 turn_id: Option<u64>,
283 action: String,
284 },
285 ActivityChanged {
286 activity: ApiActivityState,
287 },
288}
289
290impl ApiEventData {
291 pub fn kind(&self) -> &'static str {
292 match self {
293 Self::TurnStarted { .. } => "turn_started",
294 Self::TurnEnded { .. } => "turn_ended",
295 Self::Error { .. } => "error",
296 Self::InputRequired { .. } => "input_required",
297 Self::InputResolved { .. } => "input_resolved",
298 Self::ActivityChanged { .. } => "activity_changed",
299 }
300 }
301}
302
303#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
305pub struct ApiActivityState {
306 pub state: String,
307 pub details: Option<ApiActivityDetails>,
308 pub is_idle: bool,
309 pub waiting_for_input: bool,
310 pub capacity_retry: bool,
311}
312
313#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
314#[serde(deny_unknown_fields)]
315pub struct ApiActivityDetails {
316 pub kind: ApiActivityKind,
317 #[serde(default, skip_serializing_if = "Option::is_none")]
318 pub turn_started_at_ms: Option<i64>,
319 #[serde(default, skip_serializing_if = "Option::is_none")]
320 pub step_started_at_ms: Option<i64>,
321 #[serde(default, skip_serializing_if = "Option::is_none")]
322 pub background_started_at_ms: Option<i64>,
323 #[serde(default, skip_serializing_if = "Option::is_none")]
324 pub idle_since_ms: Option<i64>,
325 #[serde(default, skip_serializing_if = "Option::is_none")]
326 pub label: Option<String>,
327}
328
329#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
330#[serde(rename_all = "lowercase")]
331pub enum ApiActivityKind {
332 Turn,
333 Step,
334 Background,
335 Idle,
336 Lifecycle,
337}
338
339#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
340pub struct ApiEventFilter {
341 pub session_id: Option<String>,
342 pub workspace_id: Option<String>,
343}
344
345#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
346pub struct ApiEventPage {
347 pub events: Vec<ApiEvent>,
348 pub next_after_seq: u64,
349 pub latest_seq: u64,
350}