1use super::*;
2
3#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
5#[serde(deny_unknown_fields)]
6pub struct DiffOptions {
7 pub base: Option<String>,
8 #[serde(default)]
9 pub json: bool,
10}
11
12#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14pub struct ApiBackgroundWork {
15 pub known: Option<bool>,
16 pub tasks: Vec<mj_core::relay::BackgroundCommand>,
17}
18
19impl From<&mj_core::relay::RelayOperationalState> for ApiBackgroundWork {
20 fn from(state: &mj_core::relay::RelayOperationalState) -> Self {
21 Self {
22 known: state.background_work_known,
23 tasks: state.background_commands.clone(),
24 }
25 }
26}
27
28#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
32pub struct ApiSession {
33 #[serde(default, skip_serializing_if = "Option::is_none")]
34 pub background_work: Option<ApiBackgroundWork>,
35 pub id: String,
36 pub workspace_id: String,
37 pub title: String,
38 pub harness_kind: String,
39 pub profile_id: String,
40 pub target_id: String,
41 pub bundle_id: String,
42 pub state: String,
43 pub lifecycle: ViewerLifecycleCategory,
44 pub chat_phase: crate::server::ViewerChatPhase,
45 pub is_idle: bool,
46 #[serde(default, skip_serializing_if = "Option::is_none")]
51 pub activity_state: Option<mj_core::activity::ActivityState>,
52 pub has_error: bool,
53 #[serde(default, skip_serializing_if = "Option::is_none")]
58 pub error: Option<String>,
59 pub created_at: String,
60 pub updated_at: String,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub last_turn_outcome: Option<MaterializedTurnOutcome>,
66 #[serde(default, skip_serializing_if = "Option::is_none")]
67 pub last_turn_diagnostic: Option<mj_core::diagnostic::TurnDiagnostic>,
68 #[serde(default)]
69 pub config_options: Vec<crate::server::ViewerConfigOption>,
70 #[serde(default, skip_serializing_if = "Vec::is_empty")]
71 pub pending_elicitations: Vec<mj_core::elicitation::ElicitationRequest>,
72}
73
74impl From<&ViewerSession> for ApiSession {
75 fn from(session: &ViewerSession) -> Self {
76 Self {
77 background_work: None,
78 id: session.id.clone(),
79 workspace_id: session.workspace_id.clone(),
80 title: session.title.clone(),
81 harness_kind: session.harness_kind.clone(),
82 profile_id: session.profile_id.clone(),
83 target_id: session.target_id.clone(),
84 bundle_id: session.bundle_id.clone(),
85 state: session.state.clone(),
86 lifecycle: session.lifecycle,
87 chat_phase: session.chat_phase,
88 is_idle: session.is_idle,
89 activity_state: session.activity_state.clone(),
90 has_error: session.has_error,
91 error: session.launch_error.clone(),
92 created_at: session.created_at.clone(),
93 updated_at: session.updated_at.clone(),
94 last_turn_outcome: None,
95 last_turn_diagnostic: None,
96 config_options: session.config_options.clone(),
97 pending_elicitations: session.pending_elicitations.clone(),
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
103pub struct SessionListResponse {
104 pub sessions: Vec<ApiSession>,
105}
106
107#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
110pub struct WorkspaceListResponse {
111 pub workspaces: Vec<mj_core::workspace::WorkspaceRecord>,
112}
113
114#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
118#[serde(deny_unknown_fields)]
119pub struct CreateWorkspaceRequest {
120 pub name: String,
121}
122
123#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
124pub struct CreateWorkspaceResponse {
125 pub workspace: mj_core::workspace::WorkspaceRecord,
126}
127
128#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
138#[serde(deny_unknown_fields)]
139pub struct StartSessionRequest {
140 #[serde(default)]
141 pub create_managed_worktree: Option<bool>,
142 #[serde(default)]
144 pub launch_base: Option<String>,
145 #[serde(default)]
147 pub mjolnir_subagents: Option<bool>,
148 #[serde(default)]
149 pub workspace_id: Option<String>,
150 #[serde(default)]
152 pub profile_id: Option<String>,
153 #[serde(default)]
155 pub target_id: Option<String>,
156 #[serde(default)]
157 pub bundle_id: Option<String>,
158 #[serde(default)]
159 pub project_directory: Option<PathBuf>,
160 #[serde(default)]
161 pub title: Option<String>,
162 #[serde(default)]
163 pub model: Option<String>,
164 #[serde(default)]
165 pub effort: Option<String>,
166 #[serde(default)]
167 pub prompt: Option<String>,
168}
169
170#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
175#[serde(deny_unknown_fields)]
176pub struct ResumeSessionRequest {
177 #[serde(default)]
179 pub profile_id: Option<String>,
180 #[serde(default)]
182 pub target_id: Option<String>,
183 #[serde(default)]
185 pub workspace_id: Option<String>,
186 #[serde(default)]
190 pub queue: Option<mj_core::state::ResumeQueueDisposition>,
191}
192
193#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
196pub struct ResumeSessionResponse {
197 pub session_id: String,
198 pub workspace_id: String,
199 pub profile_id: String,
200 pub target_id: String,
201}
202
203#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
204pub struct StartSessionResponse {
205 pub session_id: String,
206 #[serde(default, skip_serializing_if = "Option::is_none")]
209 pub turn_id: Option<u64>,
210}
211
212#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
213#[serde(deny_unknown_fields)]
214pub struct SubagentSourceRange {
215 pub file: PathBuf,
216 pub start: u64,
217 pub end: u64,
218}
219
220#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
221#[serde(deny_unknown_fields)]
222pub struct SpawnSubagentRequest {
223 pub task_name: String,
224 pub instructions: String,
225 #[serde(default)]
226 pub profile_id: Option<String>,
227 #[serde(default)]
228 pub model: Option<String>,
229 #[serde(default)]
230 pub effort: Option<String>,
231 #[serde(default)]
232 pub working_directory: Option<PathBuf>,
233 #[serde(default)]
234 pub context: Option<String>,
235 #[serde(default)]
236 pub files: Vec<SubagentSourceRange>,
237 pub request_key: String,
238}
239
240#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
241pub struct SubagentView {
242 pub parent_session_id: String,
243 pub task_name: String,
244 pub request_key: String,
245 pub session: ApiSession,
246}
247
248#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
249pub struct SubagentListResponse {
250 pub subagents: Vec<SubagentView>,
251}
252
253#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
254#[serde(deny_unknown_fields)]
255pub struct PromptRequest {
256 pub text: String,
257}
258
259#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
260pub struct PromptResponse {
261 pub turn_id: u64,
264}
265
266#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
267#[serde(deny_unknown_fields)]
268pub struct WaitRequest {
269 #[serde(default)]
271 pub return_on_input: bool,
272 #[serde(default)]
276 pub turn_id: Option<u64>,
277 #[serde(default)]
278 pub timeout_secs: Option<u64>,
279}
280
281#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
283#[serde(rename_all = "snake_case")]
284pub enum WaitOutcome {
285 InputRequired,
287 Finished,
289 Error,
291 Cancelled,
293 QuotaLimit,
295 Timeout,
297 Stopped,
299}
300
301#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
302pub struct WaitCapacityRetry {
303 pub attempt: u32,
304 pub retry_at_ms: i64,
305}
306
307impl From<&CapacityRetry> for WaitCapacityRetry {
308 fn from(retry: &CapacityRetry) -> Self {
309 Self {
310 attempt: retry.attempt,
311 retry_at_ms: retry.retry_at_ms,
312 }
313 }
314}
315
316#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
322#[serde(rename_all = "snake_case")]
323pub enum RelayState {
324 Connected,
326 Disconnected,
328 Unreachable,
330 TargetMissing,
332 ProjectionIntegrity,
334}
335
336#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
337pub struct RelayHealth {
338 pub state: RelayState,
339 #[serde(default, skip_serializing_if = "Option::is_none")]
341 pub detail: Option<String>,
342}
343
344impl From<&mj_client::session::ManagedSessionView> for RelayHealth {
345 fn from(view: &mj_client::session::ManagedSessionView) -> Self {
346 use mj_client::session::ViewError;
347 match &view.error {
350 Some(error) => Self {
351 state: match error {
352 ViewError::Unreachable(_) => RelayState::Unreachable,
353 ViewError::TargetMissing(_) => RelayState::TargetMissing,
354 ViewError::ProjectionIntegrity(_) => RelayState::ProjectionIntegrity,
355 },
356 detail: Some(error.detail().to_owned()),
357 },
358 None if view.connected => Self {
359 state: RelayState::Connected,
360 detail: None,
361 },
362 None => Self {
363 state: RelayState::Disconnected,
364 detail: None,
365 },
366 }
367 }
368}
369
370#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
371pub struct WaitResponse {
372 #[serde(default, skip_serializing_if = "Option::is_none")]
373 pub diagnostic: Option<mj_core::diagnostic::TurnDiagnostic>,
374
375 #[serde(default, skip_serializing_if = "Vec::is_empty")]
376 pub pending_elicitations: Vec<mj_core::elicitation::ElicitationRequest>,
377 #[serde(default, skip_serializing_if = "Option::is_none")]
378 pub usage: Option<mj_core::usage::TokenUsage>,
379 pub outcome: WaitOutcome,
380 #[serde(default, skip_serializing_if = "Option::is_none")]
382 pub stop_reason: Option<String>,
383 #[serde(default, skip_serializing_if = "Option::is_none")]
385 pub message: Option<String>,
386 #[serde(default, skip_serializing_if = "Option::is_none")]
388 pub final_message: Option<String>,
389 #[serde(default, skip_serializing_if = "Option::is_none")]
390 pub turn_id: Option<u64>,
391 #[serde(default, skip_serializing_if = "Option::is_none")]
393 pub turn_number: Option<u64>,
394 #[serde(default, skip_serializing_if = "Option::is_none")]
395 pub elapsed_ms: Option<i64>,
396 #[serde(default, skip_serializing_if = "Option::is_none")]
399 pub capacity_retry: Option<WaitCapacityRetry>,
400 #[serde(default, skip_serializing_if = "Option::is_none")]
401 pub quota_recovery: Option<mj_core::continuation::QuotaRecovery>,
402 #[serde(default, skip_serializing_if = "Option::is_none")]
406 pub relay: Option<RelayHealth>,
407 pub session: ApiSession,
408}
409
410pub const DEFAULT_TRANSCRIPT_LIMIT: usize = 200;
415pub const MAX_TRANSCRIPT_LIMIT: usize = 1_000;
416
417#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
418pub struct TranscriptQuery {
419 #[serde(default)]
420 pub role: Option<mj_core::transcript::TranscriptRole>,
421 #[serde(default)]
423 pub after_seq: Option<u64>,
424 #[serde(default)]
425 pub limit: Option<usize>,
426}
427
428#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
429pub struct TranscriptItemView {
430 pub stable_id: String,
431 pub position: u64,
432 pub seq: u64,
435 pub role: String,
436 pub text: String,
438 pub created_at_ms: i64,
439 pub last_changed_at_ms: i64,
440 pub body: mj_core::transcript::TranscriptBody,
442}
443
444#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
445pub struct TranscriptResponse {
446 #[serde(default)]
447 pub next_after_seq: u64,
448 pub session_id: String,
449 pub latest_seq: u64,
452 pub execution: MaterializedExecutionState,
453 pub items: Vec<TranscriptItemView>,
454}
455
456#[derive(Debug, Clone, PartialEq, Eq)]
463pub struct TurnState {
464 pub execution: MaterializedExecutionState,
465 pub active_turn: Option<MaterializedTurn>,
466 pub last_turn_outcome: Option<MaterializedTurnOutcome>,
467}
468
469pub use crate::database::TurnSummary;
470
471#[derive(Debug, Clone, Default, PartialEq, Eq)]
474pub struct StartFollowup {
475 pub model: Option<String>,
476 pub effort: Option<String>,
477 pub prompt: Option<String>,
478}
479
480#[derive(Debug, Clone, PartialEq, Eq)]
482pub enum StartStatus {
483 Pending,
485 Submitted { turn_id: u64 },
487 Failed { message: String },
489}
490
491pub use crate::database::TranscriptPage;
493
494#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
496pub struct PushedBranch {
497 pub branch: String,
498 pub remote: String,
499}
500
501#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
503pub struct FileQuery {
504 pub path: String,
506}
507
508#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
510#[serde(rename_all = "snake_case")]
511pub enum ExportKind {
512 Patch,
514 Branch,
516 Bundle,
518}
519
520#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
521pub struct ExportRequest {
522 pub kind: ExportKind,
523 #[serde(default, skip_serializing_if = "Option::is_none")]
525 pub branch: Option<String>,
526}
527
528#[derive(Debug, Clone)]
530pub struct BundleExport {
531 pub repository: String,
532 pub bytes: Vec<u8>,
533}
534
535#[derive(Debug)]
537pub enum ExportError {
538 Refused(String),
541 Failed(anyhow::Error),
543}
544
545impl From<ExportError> for ApiFailure {
546 fn from(error: ExportError) -> Self {
547 match error {
548 ExportError::Refused(message) => Self::conflict(message),
549 ExportError::Failed(error) => Self::from(error),
550 }
551 }
552}
553
554#[derive(Debug, Default, Deserialize)]
559#[serde(deny_unknown_fields)]
560pub struct WikiSearchQuery {
561 #[serde(default)]
562 pub q: Option<String>,
563 #[serde(default)]
564 pub limit: Option<usize>,
565}
566
567#[derive(Debug, Default, Deserialize)]
568#[serde(deny_unknown_fields)]
569pub struct WikiBriefQuery {
570 #[serde(default)]
571 pub max_chars: Option<usize>,
572}
573
574#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
575pub struct WikiBriefResponse {
576 pub markdown: String,
577}
578
579#[derive(Debug, Default, Deserialize)]
581#[serde(deny_unknown_fields)]
582pub struct WikiHitsQuery {
583 pub q: String,
584 #[serde(default)]
585 pub context_messages: Option<usize>,
586 #[serde(default)]
587 pub per_message_chars: Option<usize>,
588}
589
590#[derive(Debug, Clone, Default, Serialize, Deserialize)]
593#[serde(deny_unknown_fields)]
594pub struct WikiRestoreBody {
595 #[serde(default)]
596 pub workspace_id: Option<String>,
597 pub profile_id: String,
598 pub target_id: String,
599 #[serde(default)]
600 pub project_directory: Option<PathBuf>,
601 #[serde(default)]
602 pub model: Option<String>,
603 #[serde(default)]
604 pub effort: Option<String>,
605}
606
607#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
621#[serde(deny_unknown_fields)]
622pub struct LaunchOptions {
623 pub revision: u64,
627 pub profiles: Vec<LaunchProfile>,
628 pub targets: Vec<LaunchTarget>,
629 pub bundles: Vec<LaunchBundle>,
630 #[serde(default, skip_serializing_if = "Vec::is_empty")]
633 pub hosts: Vec<LaunchHost>,
634 #[serde(default, skip_serializing_if = "Option::is_none")]
638 pub default: Option<LaunchDefault>,
639}
640
641#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
643#[serde(deny_unknown_fields)]
644pub struct LaunchProfile {
645 pub id: String,
646 pub harness: String,
649}
650
651#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
653#[serde(deny_unknown_fields)]
654pub struct LaunchTarget {
655 pub id: String,
656 pub kind: String,
658 pub requires_project_directory: bool,
661 pub availability: LaunchAvailability,
662 #[serde(default, skip_serializing_if = "Option::is_none")]
666 pub unavailable_reason: Option<String>,
667 #[serde(default, skip_serializing_if = "Option::is_none")]
669 pub host: Option<String>,
670}
671
672#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
679#[serde(rename_all = "lowercase")]
680pub enum LaunchAvailability {
681 Ready,
683 Stale,
685 Unavailable,
687 Unknown,
689}
690
691#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
693#[serde(deny_unknown_fields)]
694pub struct LaunchBundle {
695 pub id: String,
696 pub primary_repository: String,
697 pub repositories: Vec<LaunchRepository>,
698}
699
700#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
701#[serde(deny_unknown_fields)]
702pub struct LaunchRepository {
703 pub id: String,
704 #[serde(default, skip_serializing_if = "Option::is_none")]
708 pub github: Option<String>,
709 pub destination: String,
710}
711
712#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
715#[serde(deny_unknown_fields)]
716pub struct LaunchHost {
717 pub id: String,
718 pub label: String,
719 pub targets: Vec<String>,
721 pub stale: bool,
722 pub refreshing: bool,
723 pub has_error: bool,
724}
725
726#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
728#[serde(deny_unknown_fields)]
729pub struct LaunchDefault {
730 pub profile_id: String,
731 pub target_id: String,
732}