vtcode_core/tools/exec_session/
record.rs1use super::*;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum ExecSessionBackend {
7 Pipe,
8 Pty,
9}
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub(crate) enum ExecSessionLaunchMode {
13 Foreground,
14 UserBackground,
15 ManagedBackground,
16}
17
18impl ExecSessionLaunchMode {
19 pub(crate) fn is_background(self) -> bool {
20 !matches!(self, Self::Foreground)
21 }
22
23 pub(crate) fn reserves_background_slot(self) -> bool {
24 matches!(self, Self::UserBackground)
25 }
26
27 fn shows_in_background_drawer(self) -> bool {
28 matches!(self, Self::UserBackground)
29 }
30
31 pub(crate) fn sets_foreground_session(self) -> bool {
32 matches!(self, Self::Foreground)
33 }
34}
35
36pub(crate) fn bounded_completion_command(command: &str) -> String {
37 let without_controls: String = command
38 .chars()
39 .map(|character| if character.is_control() { ' ' } else { character })
40 .collect();
41 let collapsed = vtcode_commons::formatting::collapse_whitespace(&without_controls);
42 let redacted = vtcode_commons::sanitizer::redact_secrets(collapsed);
43 if redacted.len() <= EXEC_SESSION_COMPLETION_COMMAND_MAX_BYTES {
44 return redacted;
45 }
46 vtcode_commons::formatting::truncate_byte_budget(
47 &redacted,
48 EXEC_SESSION_COMPLETION_COMMAND_MAX_BYTES.saturating_sub(3),
49 "...",
50 )
51}
52
53#[derive(Debug, Clone, PartialEq, Eq)]
55pub struct ExecSessionUiSnapshot {
56 pub metadata: VTCodeExecSession,
57 pub preview: String,
58 pub termination_requested: bool,
59 pub updated_at: chrono::DateTime<Utc>,
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct ExecSessionCompletionEvent {
68 pub session_id: ExecSessionId,
70 pub command: String,
72 pub managed_background: bool,
76 pub termination_requested: bool,
78 pub exit_code: i32,
80}
81
82pub(crate) struct ExecSessionRecord {
83 pub(crate) metadata: VTCodeExecSession,
84 pub(crate) backend: ExecSessionBackend,
85 _pty_guard: Option<PtySessionGuard>,
86 pub(crate) foreground_pty_counter: ParkingMutex<Option<Arc<AtomicUsize>>>,
87 pub(crate) background: AtomicBool,
88 pub(crate) background_promoted_from_foreground: AtomicBool,
89 pub(crate) show_in_background_drawer: AtomicBool,
90 pub(crate) background_slot_reserved: AtomicBool,
91 pub(crate) background_completion_published: AtomicBool,
92 pub(crate) termination_requested: AtomicBool,
93 pub(crate) completed_at: ParkingMutex<Option<chrono::DateTime<Utc>>>,
94 preview: ParkingMutex<SessionPreviewState>,
95 pub(crate) output_read_lock: Mutex<()>,
96 pub(crate) background_watch: ParkingMutex<Option<JoinHandle<()>>>,
97 pub(crate) foreground_watch: ParkingMutex<Option<JoinHandle<()>>>,
98}
99
100impl ExecSessionRecord {
101 pub(crate) fn new(
102 metadata: VTCodeExecSession,
103 backend: ExecSessionBackend,
104 pty_guard: Option<PtySessionGuard>,
105 launch_mode: ExecSessionLaunchMode,
106 background_slot_reserved: bool,
107 ) -> Self {
108 Self {
109 metadata,
110 backend,
111 _pty_guard: pty_guard,
112 foreground_pty_counter: ParkingMutex::new(None),
113 background: AtomicBool::new(launch_mode.is_background()),
114 background_promoted_from_foreground: AtomicBool::new(false),
115 show_in_background_drawer: AtomicBool::new(launch_mode.shows_in_background_drawer()),
116 background_slot_reserved: AtomicBool::new(background_slot_reserved),
117 background_completion_published: AtomicBool::new(false),
118 termination_requested: AtomicBool::new(false),
119 completed_at: ParkingMutex::new(None),
120 preview: ParkingMutex::new(SessionPreviewState::default()),
121 output_read_lock: Mutex::new(()),
122 background_watch: ParkingMutex::new(None),
123 foreground_watch: ParkingMutex::new(None),
124 }
125 }
126
127 pub(crate) fn remember_output(&self, output: Option<&str>, drain: bool) {
128 let Some(output) = output else {
129 return;
130 };
131
132 let mut preview = self.preview.lock();
133 if drain {
134 preview.retained.append(output);
135 preview.pending = None;
136 } else {
137 preview.pending = Some(output.to_string());
138 }
139 }
140
141 pub(crate) fn preview(&self) -> String {
142 let preview = self.preview.lock();
143 let mut retained = preview.retained.clone();
144 if let Some(pending) = preview.pending.as_deref() {
145 retained.append(pending);
146 }
147 retained.render()
148 }
149}