use super::*;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApiBackgroundWork {
pub known: Option<bool>,
pub tasks: Vec<mj_core::relay::BackgroundCommand>,
}
impl From<&mj_core::relay::RelayOperationalState> for ApiBackgroundWork {
fn from(state: &mj_core::relay::RelayOperationalState) -> Self {
Self {
known: state.background_work_known,
tasks: state.background_commands.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ApiSession {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub background_work: Option<ApiBackgroundWork>,
pub id: String,
pub workspace_id: String,
pub title: String,
pub harness_kind: String,
pub profile_id: String,
pub target_id: String,
pub bundle_id: String,
pub state: String,
pub lifecycle: ViewerLifecycleCategory,
pub chat_phase: crate::server::ViewerChatPhase,
pub is_idle: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub activity_state: Option<mj_core::activity::ActivityState>,
pub has_error: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<String>,
pub created_at: String,
pub updated_at: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_turn_outcome: Option<MaterializedTurnOutcome>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_turn_diagnostic: Option<mj_core::diagnostic::TurnDiagnostic>,
#[serde(default)]
pub config_options: Vec<crate::server::ViewerConfigOption>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pending_elicitations: Vec<mj_core::elicitation::ElicitationRequest>,
}
impl From<&ViewerSession> for ApiSession {
fn from(session: &ViewerSession) -> Self {
Self {
background_work: None,
id: session.id.clone(),
workspace_id: session.workspace_id.clone(),
title: session.title.clone(),
harness_kind: session.harness_kind.clone(),
profile_id: session.profile_id.clone(),
target_id: session.target_id.clone(),
bundle_id: session.bundle_id.clone(),
state: session.state.clone(),
lifecycle: session.lifecycle,
chat_phase: session.chat_phase,
is_idle: session.is_idle,
activity_state: session.activity_state.clone(),
has_error: session.has_error,
error: session.launch_error.clone(),
created_at: session.created_at.clone(),
updated_at: session.updated_at.clone(),
last_turn_outcome: None,
last_turn_diagnostic: None,
config_options: session.config_options.clone(),
pending_elicitations: session.pending_elicitations.clone(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SessionListResponse {
pub sessions: Vec<ApiSession>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WorkspaceListResponse {
pub workspaces: Vec<mj_core::workspace::WorkspaceRecord>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct CreateWorkspaceRequest {
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct CreateWorkspaceResponse {
pub workspace: mj_core::workspace::WorkspaceRecord,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct StartSessionRequest {
#[serde(default)]
pub create_managed_worktree: Option<bool>,
#[serde(default)]
pub mjolnir_subagents: Option<bool>,
#[serde(default)]
pub workspace_id: Option<String>,
pub profile_id: String,
pub target_id: String,
#[serde(default)]
pub bundle_id: Option<String>,
#[serde(default)]
pub project_directory: Option<PathBuf>,
#[serde(default)]
pub title: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub effort: Option<String>,
#[serde(default)]
pub prompt: Option<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct ResumeSessionRequest {
#[serde(default)]
pub profile_id: Option<String>,
#[serde(default)]
pub target_id: Option<String>,
#[serde(default)]
pub workspace_id: Option<String>,
#[serde(default)]
pub queue: Option<mj_core::state::ResumeQueueDisposition>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResumeSessionResponse {
pub session_id: String,
pub workspace_id: String,
pub profile_id: String,
pub target_id: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StartSessionResponse {
pub session_id: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turn_id: Option<u64>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SubagentSourceRange {
pub file: PathBuf,
pub start: u64,
pub end: u64,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SpawnSubagentRequest {
pub task_name: String,
pub instructions: String,
#[serde(default)]
pub profile_id: Option<String>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub effort: Option<String>,
#[serde(default)]
pub working_directory: Option<PathBuf>,
#[serde(default)]
pub context: Option<String>,
#[serde(default)]
pub files: Vec<SubagentSourceRange>,
pub request_key: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubagentView {
pub parent_session_id: String,
pub task_name: String,
pub request_key: String,
pub session: ApiSession,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SubagentListResponse {
pub subagents: Vec<SubagentView>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct PromptRequest {
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PromptResponse {
pub turn_id: u64,
}
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WaitRequest {
#[serde(default)]
pub return_on_input: bool,
#[serde(default)]
pub turn_id: Option<u64>,
#[serde(default)]
pub timeout_secs: Option<u64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum WaitOutcome {
InputRequired,
Finished,
Error,
Cancelled,
QuotaLimit,
Timeout,
Stopped,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WaitCapacityRetry {
pub attempt: u32,
pub retry_at_ms: i64,
}
impl From<&CapacityRetry> for WaitCapacityRetry {
fn from(retry: &CapacityRetry) -> Self {
Self {
attempt: retry.attempt,
retry_at_ms: retry.retry_at_ms,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum RelayState {
Connected,
Disconnected,
Unreachable,
TargetMissing,
ProjectionIntegrity,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RelayHealth {
pub state: RelayState,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub detail: Option<String>,
}
impl From<&mj_client::session::ManagedSessionView> for RelayHealth {
fn from(view: &mj_client::session::ManagedSessionView) -> Self {
use mj_client::session::ViewError;
match &view.error {
Some(error) => Self {
state: match error {
ViewError::Unreachable(_) => RelayState::Unreachable,
ViewError::TargetMissing(_) => RelayState::TargetMissing,
ViewError::ProjectionIntegrity(_) => RelayState::ProjectionIntegrity,
},
detail: Some(error.detail().to_owned()),
},
None if view.connected => Self {
state: RelayState::Connected,
detail: None,
},
None => Self {
state: RelayState::Disconnected,
detail: None,
},
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct WaitResponse {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub diagnostic: Option<mj_core::diagnostic::TurnDiagnostic>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub pending_elicitations: Vec<mj_core::elicitation::ElicitationRequest>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub usage: Option<mj_core::usage::TokenUsage>,
pub outcome: WaitOutcome,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub stop_reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub final_message: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turn_id: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub turn_number: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub elapsed_ms: Option<i64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub capacity_retry: Option<WaitCapacityRetry>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub relay: Option<RelayHealth>,
pub session: ApiSession,
}
pub const DEFAULT_TRANSCRIPT_LIMIT: usize = 200;
pub const MAX_TRANSCRIPT_LIMIT: usize = 1_000;
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
pub struct TranscriptQuery {
#[serde(default)]
pub role: Option<mj_core::transcript::TranscriptRole>,
#[serde(default)]
pub after_seq: Option<u64>,
#[serde(default)]
pub limit: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TranscriptItemView {
pub stable_id: String,
pub position: u64,
pub seq: u64,
pub role: String,
pub text: String,
pub created_at_ms: i64,
pub last_changed_at_ms: i64,
pub body: mj_core::transcript::TranscriptBody,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TranscriptResponse {
#[serde(default)]
pub next_after_seq: u64,
pub session_id: String,
pub latest_seq: u64,
pub execution: MaterializedExecutionState,
pub items: Vec<TranscriptItemView>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TurnState {
pub execution: MaterializedExecutionState,
pub active_turn: Option<MaterializedTurn>,
pub last_turn_outcome: Option<MaterializedTurnOutcome>,
}
pub use crate::database::TurnSummary;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StartFollowup {
pub model: Option<String>,
pub effort: Option<String>,
pub prompt: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum StartStatus {
Pending,
Submitted { turn_id: u64 },
Failed { message: String },
}
pub use crate::database::TranscriptPage;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PushedBranch {
pub branch: String,
pub remote: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct FileQuery {
pub path: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ExportKind {
Patch,
Branch,
Bundle,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ExportRequest {
pub kind: ExportKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub branch: Option<String>,
}
#[derive(Debug, Clone)]
pub struct BundleExport {
pub repository: String,
pub bytes: Vec<u8>,
}
#[derive(Debug)]
pub enum ExportError {
Refused(String),
Failed(anyhow::Error),
}
impl From<ExportError> for ApiFailure {
fn from(error: ExportError) -> Self {
match error {
ExportError::Refused(message) => Self::conflict(message),
ExportError::Failed(error) => Self::from(error),
}
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WikiSearchQuery {
#[serde(default)]
pub q: Option<String>,
#[serde(default)]
pub limit: Option<usize>,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WikiBriefQuery {
#[serde(default)]
pub max_chars: Option<usize>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct WikiBriefResponse {
pub markdown: String,
}
#[derive(Debug, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WikiHitsQuery {
pub q: String,
#[serde(default)]
pub context_messages: Option<usize>,
#[serde(default)]
pub per_message_chars: Option<usize>,
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct WikiRestoreBody {
#[serde(default)]
pub workspace_id: Option<String>,
pub profile_id: String,
pub target_id: String,
#[serde(default)]
pub project_directory: Option<PathBuf>,
#[serde(default)]
pub model: Option<String>,
#[serde(default)]
pub effort: Option<String>,
}