feat(tui): implement agent turn engine for background processing and enhance input handling

This commit is contained in:
asepharyana
2026-07-20 10:55:09 +07:00
parent da2ed6da25
commit 792695b65a
31 changed files with 603 additions and 153 deletions
+82 -11
View File
@@ -418,7 +418,7 @@ pub enum Overlay {
Effort,
/// MCP server management panel.
Mcp,
/// TODO list overlay.
/// Task list overlay.
Todo,
/// Session rewind / history scrubber.
Rewind,
@@ -541,7 +541,7 @@ impl Default for MiscState {
}
// ---------------------------------------------------------------------------
// EditorState (simplified — used by the Editor overlay)
// EditorState — used by the Editor overlay
// ---------------------------------------------------------------------------
/// Simple inline editor state for the TUI.
@@ -668,10 +668,17 @@ pub fn current_effort(state: &AppStateRest) -> usize {
}
/// Cycle effort level up or down.
pub fn cycle_effort(state: &mut AppStateRest, _forward: bool) {
// Simplified: cycle through levels
pub fn cycle_effort(state: &mut AppStateRest, forward: bool) {
let n = EFFORT_LEVELS.len();
state.misc.effort_level = (state.misc.effort_level % n) + 1;
if forward {
state.misc.effort_level = (state.misc.effort_level % n) + 1;
} else {
state.misc.effort_level = if state.misc.effort_level <= 1 {
n
} else {
state.misc.effort_level - 1
};
}
state.mark_dirty();
}
@@ -699,9 +706,37 @@ pub enum LearningItem {
},
}
/// Return learning items from state (simplified — uses session_runtime data).
pub fn get_learning_items(_state: &AppStateRest) -> Vec<LearningItem> {
Vec::new()
/// Return learning items from state.
///
/// Reads lesson markdown files from the `lessons/` subdirectory
/// under the memory directory.
pub fn get_learning_items(state: &AppStateRest) -> Vec<LearningItem> {
let lessons_dir = state.memory_dir.join("lessons");
if !lessons_dir.exists() {
return Vec::new();
}
let mut items = Vec::new();
if let Ok(entries) = std::fs::read_dir(&lessons_dir) {
for entry in entries.flatten() {
let path = entry.path();
if path.extension().and_then(|e| e.to_str()) == Some("md") {
if let Ok(content) = std::fs::read_to_string(&path) {
items.push(LearningItem::Stored {
name: path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string(),
content,
lifecycle: "filesystem".to_string(),
scope: "filesystem".to_string(),
description: String::new(),
});
}
}
}
}
items
}
/// Cycle the selected index within bounds.
@@ -726,15 +761,23 @@ pub fn rewind_count(state: &AppStateRest) -> usize {
}
// ---------------------------------------------------------------------------
// Context window helpers (stubs for status bar)
// Context window helpers
// ---------------------------------------------------------------------------
/// Resolve the window size for context window management.
///
/// Uses the model name from settings to determine max context window,
/// falling back to settings-configured max or 128k default.
pub fn resolve_context_window(
_app_config: &zesdex_domain::cms::AppConfig,
_settings: &zesdex_domain::cms::Settings,
settings: &zesdex_domain::cms::Settings,
) -> usize {
// Default to 128k for most modern models
// Use configured max tokens from settings, or default to 128k
if let Some(max) = settings.max_tokens {
if max > 0 {
return max as usize;
}
}
128_000
}
@@ -836,6 +879,34 @@ pub const DEFAULT_HELP_TEXT: &str = r#" Zesdex TUI — Keyboard Shortcuts
Esc Dismiss editor
"#;
impl Default for AppStateRest {
fn default() -> Self {
AppStateRest {
settings: Settings::default(),
app_config: AppConfig::default(),
workspace_roots: Vec::new(),
session_id: String::new(),
session_dir: PathBuf::new(),
memory_dir: PathBuf::new(),
worktrees_dir: PathBuf::new(),
dir_cache: Arc::new(tokio::sync::RwLock::new(DirCache::new())),
mention_index: MentionIndex::new(),
session_runtime: None,
transcript_cache: TranscriptCache::new(200),
scroll: ScrollState::new(),
input: InputState::new(),
misc: MiscState::new(),
turn_events: Arc::new(Mutex::new(VecDeque::new())),
turn_in_flight_flag: Arc::new(Mutex::new(false)),
abort_flag: Arc::new(AtomicBool::new(false)),
workflow_engine: SimpleWorkflowEngine::new(),
dirty: true,
quit: false,
help_text: DEFAULT_HELP_TEXT,
}
}
}
impl AppStateRest {
/// Construct initial TUI state.
pub fn new(