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 native_agent: Option<crate::relay::RelayEvent>,
102 pub last_activity_at_ms: Option<i64>,
106 pub execution: Option<MaterializedExecutionState>,
107 pub session_title: Option<Option<String>>,
108 pub configuration: Option<BTreeMap<String, serde_json::Value>>,
109 pub transcript: Vec<TranscriptMutation>,
110 pub queued_prompts: Option<Vec<MaterializedQueuedPrompt>>,
111 pub pending_elicitations: Option<Vec<crate::elicitation::ElicitationRequest>>,
112 pub active_turn: Option<Option<MaterializedTurn>>,
115 pub clear_turn_outcome: bool,
117 pub last_turn_outcome: Option<MaterializedTurnOutcome>,
118 pub config_results: Vec<(String, Option<String>)>,
119 pub provider_cost: Option<crate::usage::ProviderCost>,
120 pub api_events: Vec<ApiEventData>,
121}
122
123#[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, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
166pub struct TranscriptCursor {
167 pub position: u64,
168 pub stable_id: String,
169}
170
171impl TranscriptCursor {
172 pub fn of(item: &TranscriptItem) -> Self {
173 Self {
174 position: item.position,
175 stable_id: item.stable_id.clone(),
176 }
177 }
178}
179
180#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
182pub struct TranscriptHistoryPage {
183 pub items: Vec<Arc<TranscriptItem>>,
184 pub before: Option<TranscriptCursor>,
185 pub frontier: u64,
186}
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
190pub struct TranscriptRetention {
191 pub items: usize,
192 pub bytes: usize,
193 pub remaining: bool,
196}
197
198#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
200pub struct StoredReview {
201 pub workflow: crate::second_opinion::ReviewWorkflow,
202 pub generation: u64,
206 pub context_baseline: u64,
208 pub native_lost: bool,
210 pub reviewer_transcript: Vec<std::sync::Arc<crate::state::TranscriptItem>>,
214}
215
216#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
222pub struct TurnReviewState {
223 pub baselines: std::collections::BTreeMap<std::path::PathBuf, String>,
224 pub reviewed_through_ordinal: u64,
225 pub prior_review: Option<crate::review::lanes::PriorReviewContext>,
228 pub active: Option<String>,
231 #[serde(default)]
235 pub pending_forward: Option<crate::review::driver::PendingForward>,
236}
237
238#[derive(Debug, Clone, PartialEq, Eq)]
244pub struct BoundedPromptHistory {
245 pub entries: Vec<PromptHistoryEntry>,
246 pub truncated: bool,
247}
248
249#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
250pub struct UsageCoverage {
251 pub recorded_turns: u64,
252 pub full_turn_reports: u64,
253 pub last_request_reports: u64,
254 pub unspecified_reports: u64,
255 pub missing_reports: u64,
256}
257
258#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
259pub struct UsageCounterTotal {
260 pub tokens: u64,
261 pub reported_turns: u64,
263}
264
265#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
266pub struct UsagePage {
267 pub session_id: String,
268 pub turns: Vec<MaterializedTurnOutcome>,
269 pub next_after_seq: u64,
270 pub latest_seq: u64,
271 pub totals: BTreeMap<String, UsageCounterTotal>,
273 pub coverage: UsageCoverage,
274 #[serde(default, skip_serializing_if = "Option::is_none")]
275 pub provider_session_cost: Option<ProviderCost>,
276}
277
278#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
279pub struct ApiEvent {
280 pub seq: u64,
281 pub session_id: String,
282 pub recorded_at_ms: i64,
283 #[serde(flatten)]
284 pub event: ApiEventData,
285}
286
287#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
288#[serde(tag = "type", content = "data", rename_all = "snake_case")]
289pub enum ApiEventData {
290 RuntimeResolved {
291 receipt: crate::harness_runtime::RuntimeReceipt,
292 },
293 TurnStarted {
294 turn: MaterializedTurn,
295 },
296 TurnEnded {
297 turn: MaterializedTurnOutcome,
298 },
299 Error {
300 message: String,
301 command_id: Option<String>,
302 },
303 InputRequired {
304 #[serde(default, skip_serializing_if = "Option::is_none")]
306 request: Option<ElicitationRequest>,
307 turn_id: Option<u64>,
308 },
309 InputResolved {
310 elicitation_id: String,
311 turn_id: Option<u64>,
312 action: String,
313 },
314 ActivityChanged {
315 activity: ApiActivityState,
316 },
317}
318
319impl ApiEventData {
320 pub fn kind(&self) -> &'static str {
321 match self {
322 Self::RuntimeResolved { .. } => "runtime_resolved",
323 Self::TurnStarted { .. } => "turn_started",
324 Self::TurnEnded { .. } => "turn_ended",
325 Self::Error { .. } => "error",
326 Self::InputRequired { .. } => "input_required",
327 Self::InputResolved { .. } => "input_resolved",
328 Self::ActivityChanged { .. } => "activity_changed",
329 }
330 }
331}
332
333#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335pub struct ApiActivityState {
336 pub state: String,
337 pub details: Option<ApiActivityDetails>,
338 pub is_idle: bool,
339 pub waiting_for_input: bool,
340 pub capacity_retry: bool,
341}
342
343#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
344#[serde(deny_unknown_fields)]
345pub struct ApiActivityDetails {
346 pub kind: ApiActivityKind,
347 #[serde(default, skip_serializing_if = "Option::is_none")]
348 pub turn_started_at_ms: Option<i64>,
349 #[serde(default, skip_serializing_if = "Option::is_none")]
350 pub step_started_at_ms: Option<i64>,
351 #[serde(default, skip_serializing_if = "Option::is_none")]
352 pub background_started_at_ms: Option<i64>,
353 #[serde(default, skip_serializing_if = "Option::is_none")]
354 pub idle_since_ms: Option<i64>,
355 #[serde(default, skip_serializing_if = "Option::is_none")]
360 pub last_activity_at_ms: Option<i64>,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
362 pub label: Option<String>,
363}
364
365#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
366#[serde(rename_all = "lowercase")]
367pub enum ApiActivityKind {
368 Turn,
369 Step,
370 Background,
371 Idle,
372 Lifecycle,
373}
374
375#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
376pub struct ApiEventFilter {
377 pub session_id: Option<String>,
378 pub workspace_id: Option<String>,
379}
380
381#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
382pub struct ApiEventPage {
383 pub events: Vec<ApiEvent>,
384 pub next_after_seq: u64,
385 pub latest_seq: u64,
386}