Skip to content

Commit 9256c07

Browse files
committed
fix(tui): 补全遗留会话信息回退
1 parent 7954d02 commit 9256c07

1 file changed

Lines changed: 148 additions & 0 deletions

File tree

src/cortex-tui/src/runner/event_loop/commands.rs

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,85 @@
66
//! This file provides the command result handling infrastructure.
77
88
use anyhow::Result;
9+
use std::path::Path;
910

1011
use crate::app::AppView;
1112
use crate::commands::{CommandResult, FormRegistry, ModalType, ViewType};
1213
use crate::session::{CortexSession, ExportFormat, default_export_filename, export_session};
14+
use cortex_engine::rollout::get_rollout_path;
15+
use cortex_engine::rollout::read_rollout;
16+
use cortex_engine::rollout::reader::{RolloutItem, SessionMetaEntry};
17+
use cortex_protocol::{ConversationId, EventMsg};
1318

1419
use super::core::EventLoop;
1520

21+
fn legacy_session_title(meta: &SessionMetaEntry) -> String {
22+
match meta.model.as_deref() {
23+
Some(model) if !model.is_empty() && model != "unknown" => model.to_string(),
24+
_ => Path::new(&meta.cwd)
25+
.file_name()
26+
.map(|name| name.to_string_lossy().to_string())
27+
.unwrap_or_else(|| "Session".to_string()),
28+
}
29+
}
30+
31+
fn legacy_session_message_count(entries: &[cortex_engine::rollout::RolloutEntry]) -> usize {
32+
entries
33+
.iter()
34+
.filter(|entry| {
35+
matches!(
36+
&entry.item,
37+
RolloutItem::EventMsg(
38+
EventMsg::UserMessage(_)
39+
| EventMsg::AgentMessage(_)
40+
| EventMsg::ExecCommandEnd(_)
41+
)
42+
)
43+
})
44+
.count()
45+
}
46+
47+
fn format_legacy_session_info(
48+
cortex_home: &std::path::PathBuf,
49+
conversation_id: &ConversationId,
50+
provider: &str,
51+
) -> Result<Option<String>> {
52+
let rollout_path = get_rollout_path(cortex_home, conversation_id);
53+
if !rollout_path.exists() {
54+
return Ok(None);
55+
}
56+
57+
let entries = read_rollout(&rollout_path)?;
58+
let Some(meta) = entries.iter().find_map(|entry| match &entry.item {
59+
RolloutItem::SessionMeta(meta) => Some(meta),
60+
_ => None,
61+
}) else {
62+
return Ok(None);
63+
};
64+
65+
let mut output = String::from("Session Info:\n");
66+
output.push_str(&format!(" ID: {}\n", meta.id));
67+
output.push_str(&format!(" Title: {}\n", legacy_session_title(meta)));
68+
if !provider.is_empty() {
69+
output.push_str(&format!(" Provider: {}\n", provider));
70+
}
71+
if let Some(model) = meta.model.as_deref() {
72+
output.push_str(&format!(" Model: {}\n", model));
73+
}
74+
output.push_str(&format!(
75+
" Messages: {}\n",
76+
legacy_session_message_count(&entries)
77+
));
78+
79+
let created = chrono::DateTime::parse_from_rfc3339(&meta.timestamp)
80+
.map(|dt| dt.format("%Y-%m-%d %H:%M").to_string())
81+
.unwrap_or_else(|_| meta.timestamp.clone());
82+
output.push_str(&format!(" Created: {}\n", created));
83+
output.push_str(&format!(" CWD: {}\n", meta.cwd));
84+
85+
Ok(Some(output))
86+
}
87+
1688
impl EventLoop {
1789
/// Handles a command result from the CommandExecutor.
1890
pub(super) async fn handle_command_result(&mut self, result: CommandResult) -> Result<()> {
@@ -514,6 +586,18 @@ impl EventLoop {
514586
session.meta.created_at.format("%Y-%m-%d %H:%M")
515587
));
516588
self.add_system_message(&output);
589+
} else if let Some(ref bridge) = self.session_bridge {
590+
match format_legacy_session_info(
591+
&bridge.config().cortex_home,
592+
bridge.conversation_id(),
593+
&self.app_state.provider,
594+
) {
595+
Ok(Some(output)) => self.add_system_message(&output),
596+
Ok(None) => self.add_system_message("No active session."),
597+
Err(err) => {
598+
self.add_system_message(&format!("Failed to load session info: {}", err))
599+
}
600+
}
517601
} else {
518602
self.add_system_message("No active session.");
519603
}
@@ -654,3 +738,67 @@ impl EventLoop {
654738
}
655739
}
656740
}
741+
742+
#[cfg(test)]
743+
mod tests {
744+
use super::*;
745+
use cortex_engine::rollout::recorder::{RolloutRecorder, SessionMeta};
746+
use cortex_protocol::{AgentMessageEvent, Event, UserMessageEvent};
747+
use std::path::PathBuf;
748+
749+
#[test]
750+
fn formats_legacy_session_info_from_rollout() {
751+
let temp_dir = tempfile::tempdir().unwrap();
752+
let cortex_home = temp_dir.path().to_path_buf();
753+
let conversation_id = ConversationId::new();
754+
755+
let mut recorder = RolloutRecorder::new(&cortex_home, conversation_id).unwrap();
756+
recorder.init().unwrap();
757+
recorder
758+
.record_meta(&SessionMeta {
759+
id: conversation_id,
760+
parent_id: None,
761+
fork_point: None,
762+
timestamp: "2026-04-10T03:00:00Z".to_string(),
763+
cwd: PathBuf::from("/tmp/imported-session"),
764+
model: "claude-3-7-sonnet".to_string(),
765+
cli_version: "test".to_string(),
766+
instructions: None,
767+
})
768+
.unwrap();
769+
recorder
770+
.record_event(&Event {
771+
id: "1".to_string(),
772+
msg: EventMsg::UserMessage(UserMessageEvent {
773+
id: None,
774+
parent_id: None,
775+
message: "hello".to_string(),
776+
images: None,
777+
}),
778+
})
779+
.unwrap();
780+
recorder
781+
.record_event(&Event {
782+
id: "1".to_string(),
783+
msg: EventMsg::AgentMessage(AgentMessageEvent {
784+
id: None,
785+
parent_id: None,
786+
message: "world".to_string(),
787+
finish_reason: None,
788+
}),
789+
})
790+
.unwrap();
791+
recorder.flush().unwrap();
792+
793+
let output = format_legacy_session_info(&cortex_home, &conversation_id, "cortex")
794+
.unwrap()
795+
.unwrap();
796+
797+
assert!(output.contains("Session Info:"));
798+
assert!(output.contains(&format!("ID: {}", conversation_id)));
799+
assert!(output.contains("Provider: cortex"));
800+
assert!(output.contains("Model: claude-3-7-sonnet"));
801+
assert!(output.contains("Messages: 2"));
802+
assert!(output.contains("CWD: /tmp/imported-session"));
803+
}
804+
}

0 commit comments

Comments
 (0)