Skip to main content

hel/hel_state/
session_move.rs

1//! Durable intent for a verified stop followed by destination restoration.
2
3use super::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(rename_all = "kebab-case")]
7pub enum ResumeQueueDisposition {
8    Start,
9    Discard,
10}
11
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(deny_unknown_fields)]
14pub struct MoveSelection {
15    #[serde(default)]
16    pub clear_resource_allocation: bool,
17    pub session_id: String,
18    pub profile_id: Option<String>,
19    pub target_template_id: Option<String>,
20    pub additional_mounts: Option<Vec<AdditionalMount>>,
21    pub resource_allocation: Option<SessionResourceAllocation>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25#[serde(deny_unknown_fields)]
26pub struct MovePreparation {
27    pub selection: MoveSelection,
28    pub source_profile_id: String,
29    pub source_target_template_id: String,
30    pub cross_harness: bool,
31    pub active: bool,
32    pub queued_commands: Vec<MaterializedQueuedPrompt>,
33    pub fingerprint: String,
34    pub operation_id: String,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
38#[serde(deny_unknown_fields)]
39pub struct MoveSessionRequest {
40    pub preparation: MovePreparation,
41    pub queue: Option<ResumeQueueDisposition>,
42    pub acknowledge_interruption: bool,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
46#[serde(deny_unknown_fields)]
47pub struct MoveOutcome {
48    pub operation_id: String,
49    pub session_id: String,
50    pub profile_id: String,
51    pub target_template_id: String,
52    pub outcome: String,
53    pub error: Option<String>,
54    pub recovery: Option<String>,
55}
56
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
58#[serde(rename_all = "snake_case")]
59pub enum MovePhase {
60    Preparing,
61    ClosingSource,
62    ResumingDestination,
63    StartingQueue,
64    Completed,
65    Failed,
66    Cancelled,
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
70#[serde(deny_unknown_fields)]
71pub struct MoveOperation {
72    pub operation_id: String,
73    pub selection: MoveSelection,
74    pub source_profile_id: String,
75    pub source_target_template_id: String,
76    pub source_target: Option<TargetLocator>,
77    pub source_native_session_id: Option<String>,
78    pub source_additional_mounts: Vec<AdditionalMount>,
79    pub source_resource_allocation: Option<SessionResourceAllocation>,
80    pub destination_target: Option<TargetLocator>,
81    pub destination_native_session_id: Option<String>,
82    pub destination_store_id: Option<String>,
83    pub configuration_fingerprint: String,
84    pub checkpoint: Option<CheckpointMetadata>,
85    /// Stopped identity retained across partially written resume conversions.
86    pub recovery_session: Option<SessionRecord>,
87    pub queue: ResumeQueueDisposition,
88    pub phase: MovePhase,
89    /// A durable boundary: once set, never restore or replay on another relay.
90    pub queue_admission_started: bool,
91    pub queue_admission_finished: bool,
92    pub cancellation_requested: bool,
93    pub created_at: String,
94    pub updated_at: String,
95    pub error: Option<String>,
96}
97
98impl MoveOperation {
99    pub fn retains_checkpoint(&self) -> bool {
100        !matches!(self.phase, MovePhase::Completed | MovePhase::Cancelled)
101            || (self.queue_admission_started && !self.queue_admission_finished)
102    }
103
104    pub fn is_active(&self) -> bool {
105        matches!(
106            self.phase,
107            MovePhase::Preparing
108                | MovePhase::ClosingSource
109                | MovePhase::ResumingDestination
110                | MovePhase::StartingQueue
111        )
112    }
113}