vtcode_core/tools/exec_session/
mod.rs1use std::path::{Path, PathBuf};
2use std::sync::Arc;
3use std::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering};
4
5use anyhow::{Context, Result, anyhow, bail};
6use chrono::Utc;
7use hashbrown::HashMap;
8use parking_lot::Mutex as ParkingMutex;
9use sha2::{Digest, Sha256};
10use tokio::io::AsyncWriteExt;
11use tokio::sync::{Mutex, Notify, RwLock, broadcast, watch};
12use tokio::task::JoinHandle;
13#[cfg(windows)]
14use vtcode_bash_runner::GracefulTerminationResult;
15use vtcode_bash_runner::{
16 PipeSpawnOptions, PipeStdinMode, ProcessHandle, graceful_kill_process_group_default_async,
17 spawn_pipe_process_with_options,
18};
19
20use crate::sandboxing::build_sanitized_env;
21use crate::tools::ExecSessionId;
22use crate::tools::output_spooler::{SpoolIntegrity, SpoolLineCounter, encode_digest_hex};
23use crate::tools::pty::{PtyCloseMode, PtySize};
24use crate::tools::registry::{PtySessionGuard, PtySessionManager};
25use crate::tools::types::VTCodeExecSession;
26use crate::utils::path::{canonicalize_workspace, ensure_path_within_workspace};
27use crate::zsh_exec_bridge::ZshExecBridgeSession;
28
29const PIPE_OUTPUT_HEAD_BYTES: usize = 8 * 1024;
30const PIPE_OUTPUT_TAIL_BYTES: usize = 8 * 1024;
31const EXEC_SESSION_PREVIEW_HEAD_BYTES: usize = 8 * 1024;
32const EXEC_SESSION_PREVIEW_TAIL_BYTES: usize = 8 * 1024;
33const EXEC_SESSION_COMPLETION_COMMAND_MAX_BYTES: usize = 512;
34const EXEC_SESSION_COMPLETION_DRAIN_TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_secs(1);
35const EXEC_SESSION_COMPLETION_DRAIN_POLL: tokio::time::Duration = tokio::time::Duration::from_millis(15);
36const EXEC_SESSION_CLOSE_TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_secs(12);
40const EXEC_SESSION_WATCH_ABORT_TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_secs(1);
42const EXEC_SESSION_OUTPUT_READ_LOCK_TIMEOUT: tokio::time::Duration = tokio::time::Duration::from_secs(1);
45
46#[derive(Debug, thiserror::Error)]
48#[error(
49 "exec session '{session_id}' not found in this runtime. Call `write_stdin` with the exact `session_id` copied from the original run response (`next_wait_args.session_id` or `next_continue_args` `s`/`session_id`); never guess the id. If a background completion or turn diagnostics already recorded output for this id, reuse its output. Missing session state does not prove failure; rerun only if fresh execution is still needed."
50)]
51pub(crate) struct ExecSessionNotFound {
52 pub(crate) session_id: crate::types::CompactStr,
53}
54
55fn missing_exec_session_error(session_id: &str) -> anyhow::Error {
56 ExecSessionNotFound { session_id: session_id.into() }.into()
57}
58
59pub const MAX_BACKGROUND_PROCESSES: usize = 3;
61
62pub use manager::ExecSessionManager;
63pub use output_buffer::BackgroundShortcutResult;
64pub use record::{ExecSessionBackend, ExecSessionCompletionEvent, ExecSessionUiSnapshot};
65
66mod manager;
67mod output_buffer;
68mod pipe;
69mod record;
70
71pub(crate) use manager::*;
72pub(crate) use output_buffer::*;
73pub(crate) use pipe::*;
74pub(crate) use record::*;
75
76#[cfg(all(test, unix))]
77mod tests;