Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion crates/tui/src/core/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ use super::capacity_memory::{
};
use super::coherence::{CoherenceSignal, CoherenceState, next_coherence_state};
use super::events::{Event, TurnOutcomeStatus};
use super::ops::{Op, USER_SHELL_TOOL_ID_PREFIX};
use super::ops::{Op, SessionSnapshot, USER_SHELL_TOOL_ID_PREFIX};
use super::session::Session;
use super::tool_parser;
use super::turn::{TurnContext, TurnToolCall, post_turn_snapshot, pre_turn_snapshot};
Expand Down Expand Up @@ -1264,6 +1264,21 @@ impl Engine {
Op::CompactContext => {
self.handle_manual_compaction().await;
}
Op::GetSessionSnapshot { tx } => {
let total_tokens = self.session.total_usage.input_tokens
+ self.session.total_usage.output_tokens;
let snapshot = SessionSnapshot {
messages: self.session.messages.clone(),
total_tokens,
model: self.session.model.clone(),
workspace: self.session.workspace.clone(),
system_prompt: self.session.system_prompt.clone(),
mode: self.current_mode.as_setting().to_string(),
};
if let Some(tx) = tx.lock().ok().and_then(|mut g| g.take()) {
let _ = tx.send(snapshot);
}
}
Op::PurgeContext => {
self.handle_purge().await;
}
Expand Down
10 changes: 10 additions & 0 deletions crates/tui/src/core/engine/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,14 @@ impl EngineHandle {
self.tx_steer.send(content.into()).await?;
Ok(())
}

/// Request a snapshot of the current session state.
/// Returns the snapshot directly via a oneshot channel, avoiding
/// competition with the SSE event stream on the mpsc receiver.
pub async fn get_session_snapshot(&self) -> Result<crate::core::ops::SessionSnapshot> {
let (tx, rx) = tokio::sync::oneshot::channel();
let tx = std::sync::Arc::new(std::sync::Mutex::new(Some(tx)));
self.send(Op::GetSessionSnapshot { tx }).await?;
rx.await.map_err(|_| anyhow::anyhow!("Engine dropped session snapshot oneshot"))
}
}
19 changes: 19 additions & 0 deletions crates/tui/src/core/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@ use std::path::PathBuf;
/// Prefix used for tool-call ids created by local composer shell shortcuts.
pub const USER_SHELL_TOOL_ID_PREFIX: &str = "user_shell_";

/// Snapshot of session state for saving to disk.
/// Returned by `Op::GetSessionSnapshot` via a oneshot channel.
#[derive(Debug, Clone)]
pub struct SessionSnapshot {
pub messages: Vec<Message>,
pub total_tokens: u64,
pub model: String,
pub workspace: PathBuf,
pub system_prompt: Option<SystemPrompt>,
pub mode: String,
}

/// Operations that can be submitted to the engine.
#[derive(Debug, Clone)]
pub enum Op {
Expand Down Expand Up @@ -97,6 +109,13 @@ pub enum Op {
/// Run context compaction immediately.
CompactContext,

/// Get a snapshot of the current session state (messages, tokens, etc.)
/// for saving to disk. Returns the result via the oneshot sender so
/// the caller doesn't have to compete with the SSE event stream.
GetSessionSnapshot {
tx: std::sync::Arc<std::sync::Mutex<Option<tokio::sync::oneshot::Sender<SessionSnapshot>>>>,
},

/// Run agent-driven context purging.
PurgeContext,

Expand Down
Loading
Loading