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)]
35 pub background_tasks: Vec<crate::server::ViewerBackgroundTask>,
36 #[serde(default, skip_serializing_if = "Option::is_none")]
37 pub assessment: Option<mj_core::assessment::Summary>,
38 #[serde(default)]
39 pub subagents: mj_core::subagent::SubagentPolicy,
40 #[serde(default, skip_serializing_if = "Option::is_none")]
44 pub at: Option<String>,
45 #[serde(default, skip_serializing_if = "Option::is_none")]
47 pub branch: Option<String>,
48 #[serde(default, skip_serializing_if = "Option::is_none")]
50 pub base: Option<String>,
51 #[serde(default, skip_serializing_if = "Option::is_none")]
52 pub background_work: Option<ApiBackgroundWork>,
53 pub id: String,
54 pub workspace_id: String,
55 pub title: String,
56 pub harness_kind: String,
57 pub profile_id: String,
58 pub target_id: String,
59 pub bundle_id: String,
60 pub state: String,
61 pub lifecycle: ViewerLifecycleCategory,
62 pub chat_phase: crate::server::ViewerChatPhase,
63 pub is_idle: bool,
64 #[serde(default, skip_serializing_if = "Option::is_none")]
69 pub activity_state: Option<mj_core::activity::ActivityState>,
70 pub has_error: bool,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
76 pub error: Option<String>,
77 pub created_at: String,
78 pub updated_at: String,
79 #[serde(default, skip_serializing_if = "Option::is_none")]
83 pub last_turn_outcome: Option<MaterializedTurnOutcome>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 pub last_turn_diagnostic: Option<mj_core::diagnostic::TurnDiagnostic>,
86 #[serde(default)]
87 pub config_options: Vec<crate::server::ViewerConfigOption>,
88 #[serde(default, skip_serializing_if = "Vec::is_empty")]
89 pub pending_elicitations: Vec<mj_core::elicitation::ElicitationRequest>,
90}
91
92impl From<&ViewerSession> for ApiSession {
93 fn from(session: &ViewerSession) -> Self {
94 Self {
95 background_tasks: session.background_tasks.clone(),
96 assessment: None,
97 subagents: session.subagents.clone(),
98 background_work: None,
99 at: session.at.clone(),
100 branch: session.branch.clone(),
101 base: session.base.clone(),
102 id: session.id.clone(),
103 workspace_id: session.workspace_id.clone(),
104 title: session.title.clone(),
105 harness_kind: session.harness_kind.clone(),
106 profile_id: session.profile_id.clone(),
107 target_id: session.target_id.clone(),
108 bundle_id: session.bundle_id.clone(),
109 state: session.state.clone(),
110 lifecycle: session.lifecycle,
111 chat_phase: session.chat_phase,
112 is_idle: session.is_idle,
113 activity_state: session.activity_state.clone(),
114 has_error: session.has_error,
115 error: session.launch_error.clone(),
116 created_at: session.created_at.clone(),
117 updated_at: session.updated_at.clone(),
118 last_turn_outcome: None,
119 last_turn_diagnostic: None,
120 config_options: session.config_options.clone(),
121 pending_elicitations: session.pending_elicitations.clone(),
122 }
123 }
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
127#[serde(deny_unknown_fields)]
128pub struct StopBackgroundTaskRequest {
129 pub background_task_id: String,
130}
131
132#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
133pub struct SessionListResponse {
134 pub sessions: Vec<ApiSession>,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
140pub struct WorkspaceListResponse {
141 pub workspaces: Vec<mj_core::workspace::WorkspaceRecord>,
142}
143
144#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
148#[serde(deny_unknown_fields)]
149pub struct CreateWorkspaceRequest {
150 pub name: String,
151}
152
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct CreateWorkspaceResponse {
155 pub workspace: mj_core::workspace::WorkspaceRecord,
156}
157
158#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
168#[serde(deny_unknown_fields)]
169pub struct StartSessionRequest {
170 #[serde(default)]
171 pub create_managed_worktree: Option<bool>,
172 #[serde(default, skip_serializing_if = "Option::is_none")]
175 pub at: Option<String>,
176 #[serde(default, skip_serializing_if = "Option::is_none")]
179 pub branch: Option<String>,
180 #[serde(default, skip_serializing_if = "Option::is_none")]
183 pub base: Option<String>,
184 #[serde(default)]
186 pub subagents: Option<mj_core::subagent::SubagentPolicy>,
187 #[serde(default)]
188 pub workspace_id: Option<String>,
189 #[serde(default)]
191 pub profile_id: Option<String>,
192 #[serde(default)]
194 pub target_id: Option<String>,
195 #[serde(default)]
196 pub bundle_id: Option<String>,
197 #[serde(default)]
198 pub project_directory: Option<PathBuf>,
199 #[serde(default)]
200 pub title: Option<String>,
201 #[serde(default)]
202 pub model: Option<String>,
203 #[serde(default)]
204 pub effort: Option<String>,
205 #[serde(default)]
206 pub prompt: Option<String>,
207}
208
209#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
214#[serde(deny_unknown_fields)]
215pub struct ResumeSessionRequest {
216 #[serde(default)]
218 pub profile_id: Option<String>,
219 #[serde(default)]
221 pub target_id: Option<String>,
222 #[serde(default)]
224 pub workspace_id: Option<String>,
225 #[serde(default)]
229 pub queue: Option<mj_core::state::ResumeQueueDisposition>,
230}
231
232#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
235pub struct ResumeSessionResponse {
236 pub session_id: String,
237 pub workspace_id: String,
238 pub profile_id: String,
239 pub target_id: String,
240}
241
242#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
245pub struct SuspendSessionResponse {
246 #[serde(default)]
247 pub session_id: String,
248 #[serde(default)]
250 pub stopped_subagents: usize,
251 #[serde(default)]
253 pub subagents_not_handed_back: usize,
254 #[serde(default, skip_serializing_if = "Option::is_none")]
257 pub warning: Option<String>,
258}
259
260#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
261pub struct StartSessionResponse {
262 pub session_id: String,
263 #[serde(default, skip_serializing_if = "Option::is_none")]
266 pub turn_id: Option<u64>,
267}
268
269#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
270#[serde(deny_unknown_fields)]
271pub struct SpawnSubagentRequest {
272 pub task_name: String,
273 pub instructions: String,
274 #[serde(default)]
275 pub profile_id: Option<String>,
276 #[serde(default)]
277 pub model: Option<String>,
278 #[serde(default)]
279 pub effort: Option<String>,
280 #[serde(default)]
281 pub working_directory: Option<PathBuf>,
282}
283
284#[derive(Debug, Clone, PartialEq, Eq)]
287pub struct SubagentCandidate {
288 pub profile_id: String,
289 pub harness: mj_core::config::HarnessKind,
290 pub choices: mj_core::worker_launch::ProfileConfig,
291 pub remaining_percent: Option<u8>,
295}
296
297#[derive(Debug, Clone, Default, PartialEq, Eq)]
300pub struct SubagentCandidates {
301 pub offered: Vec<SubagentCandidate>,
302 pub unavailable: Vec<(String, String)>,
303}
304
305#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
306pub struct SubagentView {
307 pub parent_session_id: String,
308 pub task_name: String,
309 pub session: ApiSession,
310}
311
312#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
313pub struct SubagentListResponse {
314 pub subagents: Vec<SubagentView>,
315}
316
317#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
318#[serde(deny_unknown_fields)]
319pub struct PromptRequest {
320 pub text: String,
321}
322
323#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
324pub struct PromptResponse {
325 pub turn_id: u64,
328}
329
330#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
331#[serde(deny_unknown_fields)]
332pub struct WaitRequest {
333 #[serde(default)]
335 pub return_on_input: bool,
336 #[serde(default)]
340 pub turn_id: Option<u64>,
341 #[serde(default)]
342 pub timeout_secs: Option<u64>,
343}
344
345#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
347#[serde(rename_all = "snake_case")]
348pub enum WaitOutcome {
349 InputRequired,
351 Finished,
353 Error,
355 Cancelled,
357 QuotaLimit,
359 Timeout,
361 Stopped,
363}
364
365#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
366pub struct WaitCapacityRetry {
367 pub attempt: u32,
368 pub retry_at_ms: i64,
369}
370
371impl From<&CapacityRetry> for WaitCapacityRetry {
372 fn from(retry: &CapacityRetry) -> Self {
373 Self {
374 attempt: retry.attempt,
375 retry_at_ms: retry.retry_at_ms,
376 }
377 }
378}
379
380#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
386#[serde(rename_all = "snake_case")]
387pub enum RelayState {
388 Connected,
390 Disconnected,
392 Unreachable,
394 TargetMissing,
396 ProjectionIntegrity,
398}
399
400#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
401pub struct RelayHealth {
402 pub state: RelayState,
403 #[serde(default, skip_serializing_if = "Option::is_none")]
405 pub detail: Option<String>,
406}
407
408impl From<&mj_client::session::ManagedSessionView> for RelayHealth {
409 fn from(view: &mj_client::session::ManagedSessionView) -> Self {
410 use mj_client::session::ViewError;
411 match &view.error {
414 Some(error) => Self {
415 state: match error {
416 ViewError::Unreachable(_) => RelayState::Unreachable,
417 ViewError::TargetMissing(_) => RelayState::TargetMissing,
418 ViewError::ProjectionIntegrity(_) => RelayState::ProjectionIntegrity,
419 },
420 detail: Some(error.detail().to_owned()),
421 },
422 None if view.connected => Self {
423 state: RelayState::Connected,
424 detail: None,
425 },
426 None => Self {
427 state: RelayState::Disconnected,
428 detail: None,
429 },
430 }
431 }
432}
433
434#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
435pub struct WaitResponse {
436 #[serde(default, skip_serializing_if = "Option::is_none")]
437 pub diagnostic: Option<mj_core::diagnostic::TurnDiagnostic>,
438
439 #[serde(default, skip_serializing_if = "Vec::is_empty")]
440 pub pending_elicitations: Vec<mj_core::elicitation::ElicitationRequest>,
441 #[serde(default, skip_serializing_if = "Option::is_none")]
442 pub usage: Option<mj_core::usage::TokenUsage>,
443 pub outcome: WaitOutcome,
444 #[serde(default, skip_serializing_if = "Option::is_none")]
446 pub stop_reason: Option<String>,
447 #[serde(default, skip_serializing_if = "Option::is_none")]
449 pub message: Option<String>,
450 #[serde(default, skip_serializing_if = "Option::is_none")]
453 pub final_message: Option<String>,
454 #[serde(default, skip_serializing_if = "Option::is_none")]
458 pub report_source: Option<String>,
459 #[serde(default, skip_serializing_if = "Option::is_none")]
462 pub turn_id: Option<u64>,
463 #[serde(default, skip_serializing_if = "Option::is_none")]
466 pub requested_turn_id: Option<u64>,
467 #[serde(default, skip_serializing_if = "Option::is_none")]
469 pub turn_number: Option<u64>,
470 #[serde(default, skip_serializing_if = "Option::is_none")]
471 pub elapsed_ms: Option<i64>,
472 #[serde(default, skip_serializing_if = "Option::is_none")]
475 pub tool_calls: Option<u64>,
476 #[serde(default, skip_serializing_if = "Option::is_none")]
478 pub capacity_retry: Option<WaitCapacityRetry>,
479 #[serde(default, skip_serializing_if = "Option::is_none")]
480 pub server_retry: Option<WaitCapacityRetry>,
481 #[serde(default)]
482 pub retry_assessment_pending: bool,
483 #[serde(default, skip_serializing_if = "Option::is_none")]
484 pub quota_recovery: Option<mj_core::continuation::QuotaRecovery>,
485 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub relay: Option<RelayHealth>,
490 pub session: ApiSession,
491}
492
493pub const DEFAULT_TRANSCRIPT_LIMIT: usize = 200;
498pub const MAX_TRANSCRIPT_LIMIT: usize = 1_000;
499
500#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
501pub struct TranscriptQuery {
502 #[serde(default)]
503 pub role: Option<mj_core::transcript::TranscriptRole>,
504 #[serde(default)]
506 pub after_seq: Option<u64>,
507 #[serde(default)]
508 pub limit: Option<usize>,
509}
510
511#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
512pub struct TranscriptItemView {
513 pub stable_id: String,
514 pub position: u64,
515 pub seq: u64,
518 pub role: String,
519 pub text: String,
521 pub created_at_ms: i64,
522 pub last_changed_at_ms: i64,
523 pub body: mj_core::transcript::TranscriptBody,
525}
526
527#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
528pub struct TranscriptResponse {
529 #[serde(default)]
530 pub next_after_seq: u64,
531 pub session_id: String,
532 pub latest_seq: u64,
535 pub execution: MaterializedExecutionState,
536 pub items: Vec<TranscriptItemView>,
537}
538
539#[derive(Debug, Clone, PartialEq, Eq)]
546pub struct TurnState {
547 pub execution: MaterializedExecutionState,
548 pub active_turn: Option<MaterializedTurn>,
549 pub last_turn_outcome: Option<MaterializedTurnOutcome>,
550}
551
552pub use crate::database::TurnSummary;
553
554#[derive(Debug, Clone, Default, PartialEq, Eq)]
557pub struct StartFollowup {
558 pub model: Option<String>,
559 pub effort: Option<String>,
560 pub prompt: Option<String>,
561 pub fast_mode: bool,
562}
563
564#[derive(Debug, Clone, PartialEq, Eq)]
566pub enum StartStatus {
567 Pending,
569 Submitted { turn_id: u64 },
571 Failed { message: String },
573}
574
575pub use crate::database::TranscriptPage;
577
578#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
580pub struct PushedBranch {
581 pub branch: String,
582 pub remote: String,
583}
584
585#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
587pub struct FileQuery {
588 pub path: String,
590}
591
592#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
594#[serde(rename_all = "snake_case")]
595pub enum ExportKind {
596 Patch,
598 Branch,
600 Bundle,
602}
603
604#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
605pub struct ExportRequest {
606 pub kind: ExportKind,
607 #[serde(default, skip_serializing_if = "Option::is_none")]
609 pub branch: Option<String>,
610}
611
612#[derive(Debug, Clone)]
614pub struct BundleExport {
615 pub repository: String,
616 pub bytes: Vec<u8>,
617}
618
619#[derive(Debug)]
621pub enum ExportError {
622 Refused(String),
625 Failed(anyhow::Error),
627}
628
629impl From<ExportError> for ApiFailure {
630 fn from(error: ExportError) -> Self {
631 match error {
632 ExportError::Refused(message) => Self::conflict(message),
633 ExportError::Failed(error) => Self::from(error),
634 }
635 }
636}
637
638#[derive(Debug, Default, Deserialize)]
643#[serde(deny_unknown_fields)]
644pub struct WikiSearchQuery {
645 #[serde(default)]
646 pub q: Option<String>,
647 #[serde(default)]
648 pub limit: Option<usize>,
649}
650
651#[derive(Debug, Default, Deserialize)]
652#[serde(deny_unknown_fields)]
653pub struct WikiBriefQuery {
654 #[serde(default)]
655 pub max_chars: Option<usize>,
656}
657
658#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
659pub struct WikiBriefResponse {
660 pub markdown: String,
661}
662
663#[derive(Debug, Default, Deserialize)]
665#[serde(deny_unknown_fields)]
666pub struct WikiHitsQuery {
667 pub q: String,
668 #[serde(default)]
669 pub context_messages: Option<usize>,
670 #[serde(default)]
671 pub per_message_chars: Option<usize>,
672}
673
674#[derive(Debug, Clone, Default, Serialize, Deserialize)]
677#[serde(deny_unknown_fields)]
678pub struct WikiRestoreBody {
679 #[serde(default)]
680 pub workspace_id: Option<String>,
681 pub profile_id: String,
682 pub target_id: String,
683 #[serde(default)]
684 pub project_directory: Option<PathBuf>,
685 #[serde(default)]
686 pub model: Option<String>,
687 #[serde(default)]
688 pub effort: Option<String>,
689}
690
691#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
705#[serde(deny_unknown_fields)]
706pub struct LaunchOptions {
707 pub revision: u64,
711 pub profiles: Vec<LaunchProfile>,
712 pub targets: Vec<LaunchTarget>,
713 pub bundles: Vec<LaunchBundle>,
714 #[serde(default, skip_serializing_if = "Vec::is_empty")]
717 pub hosts: Vec<LaunchHost>,
718 #[serde(default, skip_serializing_if = "Option::is_none")]
722 pub default: Option<LaunchDefault>,
723}
724
725#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
727#[serde(deny_unknown_fields)]
728pub struct LaunchProfile {
729 pub id: String,
730 pub harness: String,
733}
734
735#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
737#[serde(deny_unknown_fields)]
738pub struct LaunchTarget {
739 pub id: String,
740 pub kind: String,
742 pub requires_project_directory: bool,
745 pub availability: LaunchAvailability,
746 #[serde(default, skip_serializing_if = "Option::is_none")]
750 pub unavailable_reason: Option<String>,
751 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
756 pub runtime_missing: bool,
757 #[serde(default, skip_serializing_if = "std::ops::Not::not")]
762 pub default_candidate: bool,
763 #[serde(default, skip_serializing_if = "Option::is_none")]
765 pub host: Option<String>,
766}
767
768#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
775#[serde(rename_all = "lowercase")]
776pub enum LaunchAvailability {
777 Ready,
779 Stale,
781 Unavailable,
783 Unknown,
785}
786
787#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
789#[serde(deny_unknown_fields)]
790pub struct LaunchBundle {
791 pub id: String,
792 pub primary_repository: String,
793 pub repositories: Vec<LaunchRepository>,
794}
795
796#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
797#[serde(deny_unknown_fields)]
798pub struct LaunchRepository {
799 pub id: String,
800 #[serde(default, skip_serializing_if = "Option::is_none")]
804 pub github: Option<String>,
805 pub destination: String,
806}
807
808#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
811#[serde(deny_unknown_fields)]
812pub struct LaunchHost {
813 pub id: String,
814 pub label: String,
815 pub targets: Vec<String>,
817 pub stale: bool,
818 pub refreshing: bool,
819 pub has_error: bool,
820}
821
822#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
824#[serde(deny_unknown_fields)]
825pub struct LaunchDefault {
826 pub profile_id: String,
827 pub target_id: String,
828}