Skip to content
Merged
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
31 changes: 31 additions & 0 deletions apps/staged/src-tauri/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions apps/staged/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ tauri-plugin-store = "2.5.0"

# MCP server for project sessions
rmcp = { version = "3.5", features = ["server", "transport-streamable-http-server"] }
axum = { version = "0.8", features = ["ws"] }
axum = { version = "0.8", features = ["ws", "http2"] }
axum-extra = { version = "0.12", features = ["cookie"] }
tower-http = { version = "0.7", features = ["fs", "cors"] }
tower-http = { version = "0.7", features = ["fs", "cors", "compression-br", "compression-gzip"] }
rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs", "std", "tls12"] }
tokio-rustls = { version = "0.26", default-features = false, features = ["aws_lc_rs", "tls12"] }
rand = "0.10"
Expand Down
14 changes: 4 additions & 10 deletions apps/staged/src-tauri/src/agent/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ mod tests {
}
);
let metadata = store
.get_session_acp_metadata_messages(&session_id)
.get_session_acp_metadata_archive(&session_id)
.expect("metadata");
assert!(metadata.is_empty());
}
Expand Down Expand Up @@ -924,9 +924,7 @@ mod tests {
"ACP event metadata should not appear in transcript rows"
);

let metadata_rows = store
.get_session_acp_metadata_messages(&session_id)
.unwrap();
let metadata_rows = store.get_session_acp_metadata_archive(&session_id).unwrap();
assert_eq!(metadata_rows.len(), 1);
assert_eq!(
metadata_rows[0].acp.acp_event_kind.as_deref(),
Expand Down Expand Up @@ -965,9 +963,7 @@ mod tests {
)
.await;

let metadata_rows = store
.get_session_acp_metadata_messages(&session_id)
.unwrap();
let metadata_rows = store.get_session_acp_metadata_archive(&session_id).unwrap();
assert_eq!(metadata_rows.len(), 1);
assert_eq!(metadata_rows[0].acp.acp_origin.as_deref(), Some(&*origin));
assert_eq!(
Expand All @@ -987,9 +983,7 @@ mod tests {
assert_eq!(session.acp_title.as_deref(), Some("Fix the login flow"));

// The metadata-row archive of the raw update is unchanged behavior.
let metadata_rows = store
.get_session_acp_metadata_messages(&session_id)
.unwrap();
let metadata_rows = store.get_session_acp_metadata_archive(&session_id).unwrap();
assert_eq!(metadata_rows.len(), 1);
assert_eq!(
metadata_rows[0].acp.acp_event_kind.as_deref(),
Expand Down
3 changes: 3 additions & 0 deletions apps/staged/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2452,7 +2452,10 @@ pub fn run() {
session_commands::get_session_messages_since,
session_commands::get_session_acp_metadata_messages,
session_commands::get_session_acp_metadata_messages_since,
session_commands::poll_session,
session_commands::get_session_acp_initialization,
session_commands::get_tool_image,
session_commands::get_tool_call_body,
session_commands::count_assistant_messages_after,
session_commands::start_session,
session_commands::resume_session,
Expand Down
72 changes: 71 additions & 1 deletion apps/staged/src-tauri/src/session_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1128,13 +1128,18 @@ pub fn get_session(
.map_err(|e| e.to_string())
}

/// The visible transcript, or with `limit` its newest rows and with
/// `before_id` the rows older than one already loaded — see
/// [`store::TranscriptWindow`]. Neither set returns the whole transcript.
#[tauri::command]
pub fn get_session_messages(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
session_id: String,
limit: Option<u32>,
before_id: Option<i64>,
) -> Result<Vec<store::SessionMessage>, String> {
get_store(&store)?
.get_session_messages(&session_id)
.get_session_messages_window(&session_id, store::TranscriptWindow { limit, before_id })
.map_err(|e| e.to_string())
}

Expand Down Expand Up @@ -1172,6 +1177,71 @@ pub fn get_session_acp_metadata_messages_since(
.map_err(|e| e.to_string())
}

/// One tick of the session pane's poll: status, follow-up queue and both
/// transcript increments from a single snapshot (see `store/session_poll.rs`).
#[tauri::command(rename_all = "camelCase")]
pub fn poll_session(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
session_id: String,
request: store::SessionPollRequest,
) -> Result<store::SessionPoll, String> {
get_store(&store)?
.poll_session(&session_id, &request)
.map_err(|e| e.to_string())
}

/// Resolve a tool-result image reference (`<message_id>/<field>/<index>`, as
/// projected into the transcript by the store) to a data URL. The web client
/// fetches `/api/tool-images/…` directly; this is the Tauri IPC equivalent.
#[tauri::command(rename_all = "camelCase")]
pub fn get_tool_image(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
message_id: i64,
field: String,
index: usize,
) -> Result<String, String> {
let store = get_store(&store)?;
tool_image_data_url(&store, message_id, &field, index)
}

pub(crate) fn tool_image_data_url(
store: &Store,
message_id: i64,
field: &str,
index: usize,
) -> Result<String, String> {
let not_found = || format!("Tool image not found: {message_id}/{field}/{index}");
let field = store::ToolImageField::parse(field).ok_or_else(not_found)?;
let image = store
.get_tool_image(message_id, field, index)
.map_err(|e| e.to_string())?
.ok_or_else(not_found)?;
use base64::Engine;
let encoded = base64::engine::general_purpose::STANDARD.encode(&image.bytes);
Ok(format!("data:{};base64,{}", image.mime_type, encoded))
}

/// The bodies behind a transcript `$deferred` marker (`store/tool_bodies.rs`).
/// The web client fetches `/api/tool-bodies/{message_id}` directly; this is
/// the Tauri IPC equivalent.
#[tauri::command(rename_all = "camelCase")]
pub fn get_tool_call_body(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
message_id: i64,
) -> Result<store::ToolCallBody, String> {
let store = get_store(&store)?;
tool_call_body(&store, message_id).map_err(|e| e.to_string())
}

pub(crate) fn tool_call_body(
store: &Store,
message_id: i64,
) -> Result<store::ToolCallBody, store::StoreError> {
store
.get_tool_call_body(message_id)?
.ok_or_else(|| store::StoreError(format!("Tool call body not found: {message_id}")))
}

#[tauri::command]
pub fn get_session_acp_initialization(
store: tauri::State<'_, Mutex<Option<Arc<Store>>>>,
Expand Down
Loading
Loading