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<SessionConfiguration>,
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 pub tool_calls: u64,
152}
153
154#[derive(Debug, Clone)]
157pub struct TranscriptPage {
158 pub items: Vec<Arc<TranscriptItem>>,
159 pub latest_seq: u64,
162 pub next_after_seq: u64,
163 pub execution: MaterializedExecutionState,
164}
165
166#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
168pub struct TranscriptCursor {
169 pub position: u64,
170 pub stable_id: String,
171}
172
173impl TranscriptCursor {
174 pub fn of(item: &TranscriptItem) -> Self {
175 Self {
176 position: item.position,
177 stable_id: item.stable_id.clone(),
178 }
179 }
180}
181
182#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
184pub struct TranscriptHistoryPage {
185 pub items: Vec<Arc<TranscriptItem>>,
186 pub before: Option<TranscriptCursor>,
187 pub frontier: u64,
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
192pub struct TranscriptRetention {
193 pub items: usize,
194 pub bytes: usize,
195 pub remaining: bool,
198}
199
200#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
202pub struct StoredReview {
203 pub workflow: crate::second_opinion::ReviewWorkflow,
204 pub generation: u64,
208 pub context_baseline: u64,
210 pub native_lost: bool,
212 pub reviewer_transcript: Vec<std::sync::Arc<crate::state::TranscriptItem>>,
216}
217
218#[derive(Debug, Clone, Default, PartialEq, serde::Serialize, serde::Deserialize)]
224pub struct TurnReviewState {
225 pub baselines: std::collections::BTreeMap<std::path::PathBuf, String>,
226 pub reviewed_through_ordinal: u64,
227 pub prior_review: Option<crate::review::lanes::PriorReviewContext>,
230 pub active: Option<String>,
233 #[serde(default)]
237 pub pending_forward: Option<crate::review::driver::PendingForward>,
238}
239
240#[derive(Debug, Clone, PartialEq, Eq)]
246pub struct BoundedPromptHistory {
247 pub entries: Vec<PromptHistoryEntry>,
248 pub truncated: bool,
249}
250
251#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
252pub struct UsageCoverage {
253 pub recorded_turns: u64,
254 pub full_turn_reports: u64,
255 pub last_request_reports: u64,
256 pub unspecified_reports: u64,
257 pub missing_reports: u64,
258}
259
260#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
261pub struct UsageCounterTotal {
262 pub tokens: u64,
263 pub reported_turns: u64,
265}
266
267#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
268pub struct UsagePage {
269 pub session_id: String,
270 pub turns: Vec<MaterializedTurnOutcome>,
271 pub next_after_seq: u64,
272 pub latest_seq: u64,
273 pub totals: BTreeMap<String, UsageCounterTotal>,
275 pub coverage: UsageCoverage,
276 #[serde(default, skip_serializing_if = "Option::is_none")]
277 pub provider_session_cost: Option<ProviderCost>,
278}
279
280#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
281pub struct ApiEvent {
282 pub seq: u64,
283 pub session_id: String,
284 pub recorded_at_ms: i64,
285 #[serde(flatten)]
286 pub event: ApiEventData,
287}
288
289#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
290#[serde(tag = "type", content = "data", rename_all = "snake_case")]
291pub enum ApiEventData {
292 RuntimeResolved {
293 receipt: crate::harness_runtime::RuntimeReceipt,
294 },
295 TurnStarted {
296 turn: MaterializedTurn,
297 },
298 TurnEnded {
299 turn: crate::event_outcome::ApiTurnOutcome,
300 },
301 CommandEnded {
302 #[serde(flatten)]
303 result: crate::event_outcome::CommandResult,
304 },
305 SessionFault {
306 reason: crate::event_outcome::OutcomeReason,
307 message: String,
308 command_id: Option<String>,
309 },
310 LegacyNotice {
311 original_type: String,
312 message: String,
313 command_id: Option<String>,
314 },
315 InputRequired {
316 #[serde(default, skip_serializing_if = "Option::is_none")]
318 request: Option<ElicitationRequest>,
319 turn_id: Option<u64>,
320 },
321 InputResolved {
322 elicitation_id: String,
323 turn_id: Option<u64>,
324 action: String,
325 },
326 ActivityChanged {
327 activity: ApiActivityState,
328 },
329}
330
331impl ApiEventData {
332 pub fn kind(&self) -> &'static str {
333 match self {
334 Self::RuntimeResolved { .. } => "runtime_resolved",
335 Self::TurnStarted { .. } => "turn_started",
336 Self::TurnEnded { .. } => "turn_ended",
337 Self::CommandEnded { .. } => "command_ended",
338 Self::SessionFault { .. } => "session_fault",
339 Self::LegacyNotice { .. } => "legacy_notice",
340 Self::InputRequired { .. } => "input_required",
341 Self::InputResolved { .. } => "input_resolved",
342 Self::ActivityChanged { .. } => "activity_changed",
343 }
344 }
345}
346
347#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
349pub struct ApiActivityState {
350 pub state: String,
351 pub details: Option<ApiActivityDetails>,
352 pub is_idle: bool,
353 pub waiting_for_input: bool,
354 pub capacity_retry: bool,
355}
356
357#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
358#[serde(deny_unknown_fields)]
359pub struct ApiActivityDetails {
360 pub kind: ApiActivityKind,
361 #[serde(default, skip_serializing_if = "Option::is_none")]
362 pub turn_started_at_ms: Option<i64>,
363 #[serde(default, skip_serializing_if = "Option::is_none")]
364 pub step_started_at_ms: Option<i64>,
365 #[serde(default, skip_serializing_if = "Option::is_none")]
366 pub background_started_at_ms: Option<i64>,
367 #[serde(default, skip_serializing_if = "Option::is_none")]
368 pub idle_since_ms: Option<i64>,
369 #[serde(default, skip_serializing_if = "Option::is_none")]
374 pub last_activity_at_ms: Option<i64>,
375 #[serde(default, skip_serializing_if = "Option::is_none")]
376 pub label: Option<String>,
377}
378
379#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
380#[serde(rename_all = "lowercase")]
381pub enum ApiActivityKind {
382 Turn,
383 Step,
384 Background,
385 Idle,
386 Lifecycle,
387}
388
389#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
390pub struct ApiEventFilter {
391 pub session_id: Option<String>,
392 pub workspace_id: Option<String>,
393}
394
395#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
396pub struct ApiEventPage {
397 pub events: Vec<ApiEvent>,
398 pub next_after_seq: u64,
399 pub latest_seq: u64,
400}