fix(tui): preserve high-water mark in sidebar, add copy_cost_from helper

Sidebar cost total now uses displayed_session_cost_for_currency so the
high-water mark protects against cost reversal during API reconciliation
(#244). The session+agents breakdown is only shown when it matches the
displayed total; otherwise a single total line is rendered.

Add SessionMetadata::copy_cost_from to eliminate the last remaining
manual four-field cost copy (fork-session path).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
lbcheng
2026-05-08 19:07:48 +08:00
parent 5accda22d6
commit b6b1599b8c
3 changed files with 24 additions and 8 deletions
+1 -4
View File
@@ -2797,10 +2797,7 @@ fn fork_session(session_id: Option<String>, last: bool, workspace: &Path) -> Res
saved.metadata.total_tokens,
system_prompt.as_ref(),
);
forked.metadata.session_cost_usd = saved.metadata.session_cost_usd;
forked.metadata.session_cost_cny = saved.metadata.session_cost_cny;
forked.metadata.subagent_cost_usd = saved.metadata.subagent_cost_usd;
forked.metadata.subagent_cost_cny = saved.metadata.subagent_cost_cny;
forked.metadata.copy_cost_from(&saved.metadata);
manager.save_session(&forked)?;
let source_title = saved.metadata.title.trim();
+10
View File
@@ -110,6 +110,16 @@ pub struct SessionMetadata {
pub subagent_cost_cny: f64,
}
impl SessionMetadata {
/// Copy cost fields from another metadata (used when forking a session).
pub fn copy_cost_from(&mut self, other: &SessionMetadata) {
self.session_cost_usd = other.session_cost_usd;
self.session_cost_cny = other.session_cost_cny;
self.subagent_cost_usd = other.subagent_cost_usd;
self.subagent_cost_cny = other.subagent_cost_cny;
}
}
/// A saved session containing full conversation history
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SavedSession {
+13 -4
View File
@@ -671,16 +671,25 @@ fn render_context_panel(f: &mut Frame, area: Rect, app: &App) {
)));
// ── Session cost ─────────────────────────────────────────────
let displayed_total = app.displayed_session_cost_for_currency(app.cost_currency);
let session_cost = app.session_cost_for_currency(app.cost_currency);
let agent_cost = app.subagent_cost_for_currency(app.cost_currency);
let total_cost = session_cost + agent_cost;
lines.push(Line::from(Span::styled(
let real_total = session_cost + agent_cost;
// Only show the additive breakdown when it matches the displayed
// total; when the high-water mark is in effect (post-reconciliation),
// the breakdown would not sum to the displayed value (#244).
let cost_line = if (displayed_total - real_total).abs() < 1e-9 {
format!(
"cost: {} (session {} + agents {})",
app.format_cost_amount(total_cost),
app.format_cost_amount(displayed_total),
app.format_cost_amount(session_cost),
app.format_cost_amount(agent_cost)
),
)
} else {
format!("cost: {}", app.format_cost_amount(displayed_total))
};
lines.push(Line::from(Span::styled(
cost_line,
Style::default().fg(palette::TEXT_MUTED),
)));