Compare commits

...

1 Commits

Author SHA1 Message Date
Michael Bolin
fde2b273d1 feat: include path to rollout file in /status output 2025-08-06 14:59:25 -07:00
8 changed files with 54 additions and 13 deletions

View File

@@ -760,6 +760,10 @@ async fn submission_loop(
}
};
// Determine rollout path if available before moving the recorder.
let rollout_path: Option<std::path::PathBuf> =
rollout_recorder.as_ref().map(|r| r.path().to_path_buf());
let client = ModelClient::new(
config.clone(),
auth.clone(),
@@ -859,6 +863,7 @@ async fn submission_loop(
model,
history_log_id,
history_entry_count,
rollout_path,
}),
})
.chain(mcp_connection_errors.into_iter());

View File

@@ -647,6 +647,10 @@ pub struct SessionConfiguredEvent {
/// Current number of entries in the history log.
pub history_entry_count: usize,
/// Absolute path to the rollout file for this session, if recording is enabled.
#[serde(skip_serializing_if = "Option::is_none")]
pub rollout_path: Option<std::path::PathBuf>,
}
/// User's decision in response to an ExecApprovalRequest.
@@ -709,6 +713,7 @@ mod tests {
model: "codex-mini-latest".to_string(),
history_log_id: 0,
history_entry_count: 0,
rollout_path: None,
}),
};
let serialized = serde_json::to_string(&event).unwrap();

View File

@@ -66,6 +66,8 @@ pub struct SavedSession {
#[derive(Clone)]
pub(crate) struct RolloutRecorder {
tx: Sender<RolloutCmd>,
/// Absolute path to the rollout file for this session.
path: std::path::PathBuf,
}
enum RolloutCmd {
@@ -87,6 +89,7 @@ impl RolloutRecorder {
file,
session_id,
timestamp,
path,
} = create_log_file(config, uuid)?;
let timestamp_format: &[FormatItem] = format_description!(
@@ -118,7 +121,7 @@ impl RolloutRecorder {
cwd,
));
Ok(Self { tx })
Ok(Self { tx, path })
}
pub(crate) async fn record_items(&self, items: &[ResponseItem]) -> std::io::Result<()> {
@@ -223,7 +226,13 @@ impl RolloutRecorder {
cwd,
));
info!("Resumed rollout successfully from {path:?}");
Ok((Self { tx }, saved))
Ok((
Self {
tx,
path: path.to_path_buf(),
},
saved,
))
}
pub async fn shutdown(&self) -> std::io::Result<()> {
@@ -240,6 +249,11 @@ impl RolloutRecorder {
}
}
}
/// Return the absolute path to the rollout file recorded by this session.
pub fn path(&self) -> &std::path::Path {
&self.path
}
}
struct LogFileInfo {
@@ -251,6 +265,9 @@ struct LogFileInfo {
/// Timestamp for the start of the session.
timestamp: OffsetDateTime,
/// Absolute path to the rollout file on disk.
path: std::path::PathBuf,
}
fn create_log_file(config: &Config, session_id: Uuid) -> std::io::Result<LogFileInfo> {
@@ -284,6 +301,7 @@ fn create_log_file(config: &Config, session_id: Uuid) -> std::io::Result<LogFile
file,
session_id,
timestamp,
path,
})
}

View File

@@ -490,10 +490,7 @@ impl EventProcessor for EventProcessorWithHumanOutput {
}
EventMsg::SessionConfigured(session_configured_event) => {
let SessionConfiguredEvent {
session_id,
model,
history_log_id: _,
history_entry_count: _,
session_id, model, ..
} = session_configured_event;
ts_println!(

View File

@@ -908,6 +908,7 @@ mod tests {
model: "codex-mini-latest".into(),
history_log_id: 42,
history_entry_count: 3,
rollout_path: None,
}),
};

View File

@@ -244,6 +244,7 @@ mod tests {
model: "gpt-4o".to_string(),
history_log_id: 1,
history_entry_count: 1000,
rollout_path: None,
}),
};
@@ -284,6 +285,7 @@ mod tests {
model: "gpt-4o".to_string(),
history_log_id: 1,
history_entry_count: 1000,
rollout_path: None,
};
let event = Event {
id: "1".to_string(),

View File

@@ -79,6 +79,8 @@ pub(crate) struct ChatWidget<'a> {
current_stream: Option<StreamKind>,
stream_header_emitted: bool,
live_max_rows: u16,
/// Absolute path to the rollout file for the active session (if available).
rollout_path: Option<PathBuf>,
}
struct UserMessage {
@@ -207,6 +209,7 @@ impl ChatWidget<'_> {
current_stream: None,
stream_header_emitted: false,
live_max_rows: 3,
rollout_path: None,
}
}
@@ -283,6 +286,8 @@ impl ChatWidget<'_> {
EventMsg::SessionConfigured(event) => {
self.bottom_pane
.set_history_metadata(event.history_log_id, event.history_entry_count);
// Record rollout path for status reporting.
self.rollout_path = event.rollout_path.clone();
// Record session information at the top of the conversation.
self.add_to_history(HistoryCell::new_session_info(&self.config, event, true));
@@ -552,6 +557,7 @@ impl ChatWidget<'_> {
self.add_to_history(HistoryCell::new_status_output(
&self.config,
&self.token_usage,
self.rollout_path.as_ref(),
));
}

View File

@@ -166,12 +166,7 @@ impl HistoryCell {
event: SessionConfiguredEvent,
is_first_event: bool,
) -> Self {
let SessionConfiguredEvent {
model,
session_id: _,
history_log_id: _,
history_entry_count: _,
} = event;
let SessionConfiguredEvent { model, .. } = event;
if is_first_event {
let cwd_str = match relativize_to_home(&config.cwd) {
Some(rel) if !rel.as_os_str().is_empty() => format!("~/{}", rel.display()),
@@ -450,7 +445,11 @@ impl HistoryCell {
}
}
pub(crate) fn new_status_output(config: &Config, usage: &TokenUsage) -> Self {
pub(crate) fn new_status_output(
config: &Config,
usage: &TokenUsage,
rollout_path: Option<&std::path::PathBuf>,
) -> Self {
let mut lines: Vec<Line<'static>> = Vec::new();
lines.push(Line::from("/status".magenta()));
@@ -459,6 +458,14 @@ impl HistoryCell {
lines.push(Line::from(vec![format!("{key}: ").bold(), value.into()]));
}
// Rollout file path (if available)
if let Some(p) = rollout_path {
lines.push(Line::from(vec![
"rollout: ".bold(),
p.display().to_string().into(),
]));
}
// Token usage
lines.push(Line::from(""));
lines.push(Line::from("token usage".bold()));