diff --git a/lexicons/run/tiles/chat/sessionSnapshot.json b/lexicons/run/tiles/chat/sessionSnapshot.json new file mode 100644 index 0000000..f5ed7ed --- /dev/null +++ b/lexicons/run/tiles/chat/sessionSnapshot.json @@ -0,0 +1,84 @@ +{ + "lexicon": 1, + "id": "run.tiles.chat.sessionSnapshot", + "defs": { + "main": { + "type": "record", + "key": "tid", + "record": { + "type": "object", + "required": [ + "name", + "sessionId", + "createdAt", + "turns" + ], + "properties": { + "name": { "type": "string" }, + "sessionId": { "type": "string" }, + "createdAt": { + "type": "string", + "format": "datetime" + }, + + "turns": { + "type": "array", + "items": { + "type": "object", + "required": [ + "provider", + "model", + "messages" + ], + "properties": { + "api": { "type": "string" }, + "provider": { "type": "string" }, + "model": { "type": "string" }, + + "messages": { + "type": "array", + "items": { + "type": "ref", + "ref": "#message" + } + } + } + } + } + } + } + }, + + "message": { + "type": "object", + "required": ["role", "content"], + "properties": { + "role": { "type": "string" }, + "content": { + "type": "array", + "items": { + "type": "ref", + "ref": "#contentItem" + } + }, + "timestamp": { "type": "integer" }, + "toolName": { "type": "string" }, + "stopReason": {"type": "string"} + }, + "nullable": ["toolName", "stopReason"] + }, + + "contentItem": { + "type": "object", + "required": ["type"], + "properties": { + "type": { "type": "string" }, + "text": { "type": "string" }, + "thinking": { "type": "string" }, + "name": { "type": "string" }, + "arguments": { "type": "string" } + }, + "nullable": ["arguments", "name", "thinking", "text"] + } + } +} diff --git a/tiles/src/core/account/atproto.rs b/tiles/src/core/account/atproto.rs index 4572a07..a8b1975 100644 --- a/tiles/src/core/account/atproto.rs +++ b/tiles/src/core/account/atproto.rs @@ -32,8 +32,7 @@ use hickory_resolver::TokioResolver; use crate::{ core::storage::db::Dbconn, daemon::start_internal_server, - repl::SharedSession, - utils::{crypto::encrypt_to_base64, get_unix_time_now}, + utils::{crypto::encrypt_to_base64, get_unix_time_now, lexicons::SessionSnapshotRecord}, }; // TODO: Make this dynamic porting @@ -330,7 +329,7 @@ pub fn fetch_logged_in_data(conn: &Connection) -> Result // TODO: Add tests for share session plss pub async fn share_session( conn: &Connection, - shared_session: SharedSession, + shared_session: &SessionSnapshotRecord, is_private: bool, ) -> Result<()> { if let Some(auth_data) = fetch_logged_in_data(conn)? { @@ -378,7 +377,7 @@ pub async fn share_session( .repo .create_record( atrium_api::com::atproto::repo::create_record::InputData { - collection: "run.tiles.session" + collection: "run.tiles.chat.sessionSnapshot" .parse() .map_err(|_e| anyhow!("Failed to parse to nsid"))?, repo: did_struct.clone().into(), diff --git a/tiles/src/core/chats.rs b/tiles/src/core/chats.rs index 488e8b4..2f2b2c2 100644 --- a/tiles/src/core/chats.rs +++ b/tiles/src/core/chats.rs @@ -62,6 +62,7 @@ pub struct Session { pub name: String, pub created_at: u64, creator_id: String, + pub snapshot: Option, } type Responder = oneshot::Sender; @@ -348,14 +349,15 @@ pub fn create_session(conn: &Connection, id: &str, name: &str, user_id: &str) -> // log a warning if session already exists, and skip the conflict let mut stmt = conn.prepare( - "insert into sessions(id, name, creator_id, created_at) values (?1, ?2, ?3, ?4)", + "insert into sessions(id, name, creator_id, created_at, snapshot) values (?1, ?2, ?3, ?4, ?5)", )?; match stmt.execute(params![ id.to_owned(), name.to_owned(), user_id.to_owned(), - get_unix_time_now() as f64 + get_unix_time_now() as f64, + None:: ]) { Ok(_res) => { let sesh = fetch_session(conn, id)?; @@ -374,7 +376,7 @@ pub fn create_session(conn: &Connection, id: &str, name: &str, user_id: &str) -> pub fn fetch_session(conn: &Connection, session_id: &str) -> Result { let sesh = conn.query_row( - "SELECT id, name, creator_id, created_at FROM sessions WHERE id = ?1", + "SELECT id, name, creator_id, created_at, snapshot FROM sessions WHERE id = ?1", [session_id], |row| { Ok(Session { @@ -382,6 +384,7 @@ pub fn fetch_session(conn: &Connection, session_id: &str) -> Result { name: row.get(1)?, creator_id: row.get(2)?, created_at: row.get::(3)? as u64, + snapshot: row.get(4)?, }) }, )?; @@ -389,7 +392,8 @@ pub fn fetch_session(conn: &Connection, session_id: &str) -> Result { } pub fn fetch_sessions(conn: &Connection) -> Result> { - let query = "select id, name, creator_id, created_at from sessions order by created_at desc"; + let query = + "select id, name, creator_id, created_at, snapshot from sessions order by created_at desc"; let mut stmt = conn.prepare(query)?; let session_rows = stmt.query_map([], |row| { @@ -398,6 +402,7 @@ pub fn fetch_sessions(conn: &Connection) -> Result> { name: row.get(1)?, creator_id: row.get(2)?, created_at: row.get::(3)? as u64, + snapshot: row.get(4)?, }) })?; @@ -445,6 +450,16 @@ pub fn fetch_chats_by_session_id(conn: &Connection, session_id: &str) -> Result< fetch_delta_chats(conn, query, params) } +pub fn update_snapshot(conn: &Connection, id: &str, snapshot: String) -> Result { + let mut stmt = conn.prepare("update sessions set snapshot = ?1 where id = ?2")?; + match stmt.execute(params![snapshot, id.to_owned(),]) { + Ok(_res) => { + let sesh = fetch_session(conn, id)?; + Ok(sesh) + } + Err(err) => Err(anyhow!("Err updating session snapshot due to {}", err)), + } +} #[cfg(test)] pub mod tests { @@ -1294,7 +1309,8 @@ pub mod tests { id TEXT PRIMARY KEY, name TEXT NOT NULL, creator_id TEXT NOT NULL, - created_at INTEGER NOT NULL + created_at INTEGER NOT NULL, + snapshot TEXT )", [], ) diff --git a/tiles/src/core/storage/db.rs b/tiles/src/core/storage/db.rs index 3376fcb..38cf740 100644 --- a/tiles/src/core/storage/db.rs +++ b/tiles/src/core/storage/db.rs @@ -107,6 +107,7 @@ const CHATS_MIGRATION_ARRAY: &[M] = &[ ), M::up("CREATE INDEX idx_chats_session_id ON chats(session_id);"), M::up("ALTER TABLE CHATS ADD COLUMN model_name TEXT;"), + M::up("ALTER TABLE sessions ADD COLUMN snapshot TEXT;"), ]; const CHATS_MIGRATIONS: Migrations = Migrations::from_slice(CHATS_MIGRATION_ARRAY); diff --git a/tiles/src/repl.rs b/tiles/src/repl.rs index 0ae75f7..a3ba480 100644 --- a/tiles/src/repl.rs +++ b/tiles/src/repl.rs @@ -1,8 +1,8 @@ use crate::core::account::atproto::{fetch_logged_in_data, login, share_session}; use crate::core::account::local::get_current_user; use crate::core::chats::{ - Session, create_session, fetch_chats_by_session_id, fetch_models_used_by_session, - fetch_session, fetch_sessions, save_chat, + Session, create_session, fetch_chats_by_session_id, fetch_session, fetch_sessions, save_chat, + update_snapshot, }; use crate::core::storage::db::Dbconn; use crate::utils::config::{ @@ -11,8 +11,8 @@ use crate::utils::config::{ update_llama_config, }; use crate::utils::hf_model_downloader::*; +use crate::utils::lexicons::{SessionSnapshotRecord, Turn}; use anyhow::{Context, Result, anyhow}; -use atrium_api::types::string::Datetime; use log::{info, warn}; use nix::unistd::setsid; use owo_colors::OwoColorize; @@ -23,7 +23,7 @@ use rustyline::hint::Hinter; use rustyline::history::DefaultHistory; use rustyline::validate::Validator; use rustyline::{Config, Editor, Helper}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use serde_json::{Value, json}; use std::collections::HashMap; use std::fs::{self, OpenOptions}; @@ -170,24 +170,44 @@ struct PiTurnEndEventMsg { content: Vec, } -#[derive(Serialize, Deserialize, Debug)] -struct PiMsgEvent { +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct PiMsgEvent { role: Role, content: Vec, #[serde(rename = "stopReason")] stop_reason: Option, + timestamp: u64, + #[serde(rename = "toolName")] + tool_name: Option, } -#[derive(Serialize, Deserialize, Debug)] +#[derive(Serialize, Deserialize, Debug, Clone)] struct PiMsgContent { r#type: String, text: Option, thinking: Option, - arguments: Option, + #[serde(default, deserialize_with = "map_to_option_string")] + pub arguments: Option, // Tool name name: Option, } +fn map_to_option_string<'de, D>(deserializer: D) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let opt = Option::::deserialize(deserializer)?; + + match opt { + Some(Value::String(s)) => Ok(Some(s)), + Some(Value::Object(map)) => serde_json::to_string(&map) + .map(Some) + .map_err(serde::de::Error::custom), + Some(other) => Ok(Some(other.to_string())), + None => Ok(None), + } +} + #[derive(Serialize, Deserialize, Debug)] struct PiAgentEndEvent { messages: Vec, @@ -453,6 +473,7 @@ struct ReplSession { pub last_chat_id: Option, pub session_started: bool, pub reasoning: ReasoningEffort, + pub session_snapshot: SessionSnapshotRecord, } impl ReplSession { @@ -465,6 +486,7 @@ impl ReplSession { last_chat_id: None, session_started: false, reasoning: state.thinking_level.parse().unwrap_or(ReasoningEffort::Low), + session_snapshot: SessionSnapshotRecord::new("", &state.session_id), } } @@ -631,7 +653,7 @@ async fn start_repl(modelfile: &Modelfile, _run_args: &RunArgs, db_conn: &Dbconn // Reads the user input let readline = editor.readline(">>> "); let input = match readline { - Ok(line) => line.trim().to_string().to_lowercase(), + Ok(line) => line.trim().to_string(), Err(_) => { handle_repl_exit(pi_stdin).await?; break; @@ -643,7 +665,7 @@ async fn start_repl(modelfile: &Modelfile, _run_args: &RunArgs, db_conn: &Dbconn } // Process the user input in the repl - match handle_input(&input) { + match handle_input(&input.to_lowercase()) { InputType::Skip => continue, InputType::Exit => { handle_repl_exit(pi_stdin).await?; @@ -1009,23 +1031,11 @@ async fn process_share_session( return Ok(()); } let session = &delta_chats.sessions[0]; - - let mut shared_contents: Vec = vec![]; - for chat in delta_chats.chats { - shared_contents.push(SharedContent { - role: chat.role, - content: chat.content, - }); - } - - let models_used = fetch_models_used_by_session(&conn.chat, session_id)?; - let shared_sessions = SharedSession { - r#type: "run.tiles.session".to_string(), - session_id: session_id.to_string(), - name: session.name.clone(), - contents: shared_contents, - created_at: Datetime::now().as_str().to_string(), - models_used, + let shared_session: SessionSnapshotRecord = if let Some(snapshot_record) = &session.snapshot { + serde_json::from_str(snapshot_record)? + } else { + eprintln!("Older sessions are not supported"); + return Ok(()); }; let share_choice_prompt = format!( @@ -1041,7 +1051,7 @@ async fn process_share_session( let clean_input = input.trim(); let is_private = clean_input.is_empty() || clean_input.to_lowercase() == "y"; - match share_session(&conn.common, shared_sessions.clone(), is_private).await { + match share_session(&conn.common, &shared_session, is_private).await { Err(err) if &err.to_string() == "NOT_LOGGED_IN" => { let login_prompt = format!("{}", "Sharing a chat session requires logging in, as the data is stored on your ATmosphere PDS.\nDo you want to proceed with the login flow? (Y/n)".yellow()); @@ -1056,7 +1066,7 @@ async fn process_share_session( println!("Please enter your ATmosphere handle (ex: john.bsky.team)"); stdin.read_line(&mut input)?; login(conn, input.trim()).await?; - share_session(&conn.common, shared_sessions, is_private).await?; + share_session(&conn.common, &shared_session, is_private).await?; } } Err(err) => { @@ -1112,11 +1122,16 @@ fn load_session(db_conn: &Dbconn, args: &[&str], repl_session: &mut ReplSession) println!("{}", chat.content); } let last_chat_id = delta_chats.chats.last().map(|chat| chat.id.clone()); - + let saved_session = delta_chats.sessions[0].clone(); repl_session.session_id = session_id.to_string(); repl_session.set_pending_resume_session(true); repl_session.set_resumed_session(chat_history); repl_session.last_chat_id = last_chat_id; + repl_session.session_snapshot = if let Some(snapshot_record) = saved_session.snapshot { + serde_json::from_str(&snapshot_record)? + } else { + SessionSnapshotRecord::new(&saved_session.name, &saved_session.id) + }; Ok(()) } @@ -1398,7 +1413,15 @@ fn save_agent_session( current_user: &crate::core::account::local::User, ) -> Result<()> { let mut full_response: String = String::from(""); + let mut turn = Turn { + api: Some(String::from("open-responses")), + provider: Some(String::from("tiles")), + model: repl_session.current_modelname.clone(), + messages: vec![], + }; for msg in agent_end_event.messages { + //TODO: could avoid this cloning here + let msg_copy = msg.clone(); match msg.role { Role::User => { let input = get_pi_msg_content(msg.content); @@ -1410,6 +1433,7 @@ fn save_agent_session( ¤t_user.user_id, )?; repl_session.session_started = true; + repl_session.session_snapshot.name = input.clone(); None } else { repl_session.last_chat_id.clone() @@ -1429,8 +1453,9 @@ fn save_agent_session( let response = get_pi_msg_content(msg.content); full_response.push_str(&response); } - _ => continue, + _ => (), } + turn.messages.push(msg_copy); } let chat_response = ChatResponse { input: full_response, @@ -1442,6 +1467,12 @@ fn save_agent_session( }; let chat = save_chat(&db_conn.chat, current_user, chat_response)?; repl_session.last_chat_id = Some(chat.id); + repl_session.session_snapshot.turns.push(turn); + update_snapshot( + &db_conn.chat, + &repl_session.session_id, + serde_json::to_string(&repl_session.session_snapshot)?, + )?; Ok(()) } @@ -1457,8 +1488,7 @@ fn get_pi_msg_content(msgs: Vec) -> String { { content.push("\n**[ToolCall]**\n".to_string()); content.push(format!("Tool: {}", msg.name.unwrap_or("None".to_string()))); - let arguments = serde_json::to_string(&args).unwrap_or("{}".to_string()); - content.push(format!("Arguments: {}", arguments)); + content.push(format!("Arguments: {}", &args)); } } content.join("\n") @@ -1608,6 +1638,8 @@ mod tests { name: None }], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::Assistant, @@ -1636,6 +1668,8 @@ mod tests { }, ], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::ToolResult, @@ -1647,6 +1681,8 @@ mod tests { name: None }], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::Assistant, @@ -1658,6 +1694,8 @@ mod tests { name: None }], stop_reason: Some("stop".to_string()), + timestamp: 1783321582953, + tool_name: None }, ], }; @@ -1710,6 +1748,8 @@ mod tests { name: None }], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::Assistant, @@ -1726,7 +1766,7 @@ mod tests { r#type: String::from("toolCall"), text: None, thinking: None, - arguments: Some(json!({"command": "bash"})), + arguments: Some(serde_json::to_string(&json!({"command": "bash"})).unwrap()), name: Some(String::from("bash")) }, PiMsgContent { @@ -1738,6 +1778,8 @@ mod tests { }, ], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::ToolResult, @@ -1745,10 +1787,12 @@ mod tests { r#type: String::from("text"), text: None, thinking: None, - arguments: Some(json!({"command": "bash"})), + arguments: Some(serde_json::to_string(&json!({"command": "bash"})).unwrap()), name: Some(String::from("bash")) }], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::Assistant, @@ -1760,6 +1804,8 @@ mod tests { name: None }], stop_reason: Some("stop".to_string()), + timestamp: 1783321582953, + tool_name: None }, ], }; @@ -1807,6 +1853,8 @@ mod tests { name: None }], stop_reason: None, + timestamp: 1783321582953, + tool_name: None }, PiMsgEvent { role: Role::Assistant, @@ -1818,6 +1866,8 @@ mod tests { name: None }], stop_reason: Some("stop".to_string()), + timestamp: 1783321582953, + tool_name: None }, ], }; @@ -1847,7 +1897,8 @@ mod tests { id TEXT PRIMARY KEY, name TEXT NOT NULL, creator_id TEXT NOT NULL, - created_at INTEGER NOT NULL + created_at INTEGER NOT NULL, + snapshot TEXT )", [], ) diff --git a/tiles/src/utils/lexicons.rs b/tiles/src/utils/lexicons.rs new file mode 100644 index 0000000..406d302 --- /dev/null +++ b/tiles/src/utils/lexicons.rs @@ -0,0 +1,68 @@ +//! Module for structs to represent lexicons in Atproto + +use atrium_api::types::string::Datetime; +use serde::{Deserialize, Serialize}; + +use crate::repl::PiMsgEvent; + +#[derive(Serialize, Deserialize, Debug)] +pub struct SessionSnapshotRecord { + #[serde(rename = "$type")] + pub r#type: String, + pub name: String, + #[serde(rename = "sessionId")] + session_id: String, + #[serde(rename = "createdAt")] + created_at: String, + // One turn a user prompt + the assistant response + pub turns: Vec, +} + +impl SessionSnapshotRecord { + pub fn new(name: &str, session_id: &str) -> Self { + SessionSnapshotRecord { + r#type: String::from("run.tiles.chat.sessionSnapshot"), + name: name.to_owned(), + session_id: session_id.to_owned(), + created_at: Datetime::now().as_str().to_string(), + turns: vec![], + } + } +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct Turn { + pub api: Option, + pub provider: Option, + pub model: String, + pub messages: Vec, +} +#[derive(Serialize, Deserialize, Debug)] +pub struct ModelUsage { + input: i32, + output: i32, + #[serde(rename = "cacheRead")] + cache_read: Option, + #[serde(rename = "cacheWrite")] + cache_write: Option, + #[serde(rename = "totalTokens")] + total_tokens: i32, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ChatMessage { + role: String, + content: Vec, + timestamp: i64, + #[serde(rename = "toolName")] + tool_name: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct ContentItem { + r#type: String, + text: Option, + thinking: Option, + name: Option, + arguments: Option, +} diff --git a/tiles/src/utils/mod.rs b/tiles/src/utils/mod.rs index 3e87d4d..b4e5ad9 100644 --- a/tiles/src/utils/mod.rs +++ b/tiles/src/utils/mod.rs @@ -8,6 +8,7 @@ pub mod config; pub mod crypto; pub mod hf_model_downloader; pub mod installer; +pub mod lexicons; pub mod uninstaller; pub fn get_unix_time_now() -> u64 { SystemTime::now() -- 2.51.2 From f8bb06de8db9fe7c0a416c7fb6c12dd713c56a4f Mon Sep 17 00:00:00 2001 From: madclaws Date: Tue, 7 Jul 2026 09:51:34 +0530 Subject: [PATCH 2/3] fix: Removed nullable from lexicon by skipping None values from serialziing --- lexicons/run/tiles/chat/sessionSnapshot.json | 6 ++---- tiles/src/repl.rs | 7 +++++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/lexicons/run/tiles/chat/sessionSnapshot.json b/lexicons/run/tiles/chat/sessionSnapshot.json index f5ed7ed..f58b340 100644 --- a/lexicons/run/tiles/chat/sessionSnapshot.json +++ b/lexicons/run/tiles/chat/sessionSnapshot.json @@ -64,8 +64,7 @@ "timestamp": { "type": "integer" }, "toolName": { "type": "string" }, "stopReason": {"type": "string"} - }, - "nullable": ["toolName", "stopReason"] + } }, "contentItem": { @@ -77,8 +76,7 @@ "thinking": { "type": "string" }, "name": { "type": "string" }, "arguments": { "type": "string" } - }, - "nullable": ["arguments", "name", "thinking", "text"] + } } } } diff --git a/tiles/src/repl.rs b/tiles/src/repl.rs index a3ba480..6b7912f 100644 --- a/tiles/src/repl.rs +++ b/tiles/src/repl.rs @@ -174,9 +174,11 @@ struct PiTurnEndEventMsg { pub struct PiMsgEvent { role: Role, content: Vec, + #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "stopReason")] stop_reason: Option, timestamp: u64, + #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "toolName")] tool_name: Option, } @@ -184,11 +186,15 @@ pub struct PiMsgEvent { #[derive(Serialize, Deserialize, Debug, Clone)] struct PiMsgContent { r#type: String, + #[serde(skip_serializing_if = "Option::is_none")] text: Option, + #[serde(skip_serializing_if = "Option::is_none")] thinking: Option, + #[serde(skip_serializing_if = "Option::is_none")] #[serde(default, deserialize_with = "map_to_option_string")] pub arguments: Option, // Tool name + #[serde(skip_serializing_if = "Option::is_none")] name: Option, } @@ -1127,6 +1133,7 @@ fn load_session(db_conn: &Dbconn, args: &[&str], repl_session: &mut ReplSession) repl_session.set_pending_resume_session(true); repl_session.set_resumed_session(chat_history); repl_session.last_chat_id = last_chat_id; + repl_session.session_started = true; repl_session.session_snapshot = if let Some(snapshot_record) = saved_session.snapshot { serde_json::from_str(&snapshot_record)? } else { -- 2.51.2 From 09fb871f38d83f8d226cb9f1493ecdca781eb53e Mon Sep 17 00:00:00 2001 From: madclaws Date: Tue, 7 Jul 2026 21:37:26 +0530 Subject: [PATCH 3/3] fix: session name fix when using skill directly + other minor fixes - using Pathbuf.extension instead of `ends_with` for correct extension check as it was not working on linux. - changes shared session endpoint to `chat.tiles.run` --- tiles/src/core/account/atproto.rs | 2 +- tiles/src/core/plugin.rs | 2 +- tiles/src/repl.rs | 10 ++++++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/tiles/src/core/account/atproto.rs b/tiles/src/core/account/atproto.rs index a8b1975..57e8db6 100644 --- a/tiles/src/core/account/atproto.rs +++ b/tiles/src/core/account/atproto.rs @@ -394,7 +394,7 @@ pub async fn share_session( let base_encoded_at_url = data_encoding::BASE64.encode(url.as_bytes()); - let shareable_base_url = format!("https://tiles.run/share/{}", base_encoded_at_url); + let shareable_base_url = format!("https://chat.tiles.run/share/{}", base_encoded_at_url); let shareable_url = if is_private { format!( diff --git a/tiles/src/core/plugin.rs b/tiles/src/core/plugin.rs index 1254779..00fa674 100644 --- a/tiles/src/core/plugin.rs +++ b/tiles/src/core/plugin.rs @@ -84,7 +84,7 @@ fn install_from_local_source(local_path: PathBuf) -> Result { std::fs::create_dir_all(&tmp_path) .context("Failed to create temporary plugins directory")?; - let output = if local_path.ends_with(".zip") { + let output = if local_path.extension().is_some_and(|ext| ext == "zip") { Command::new("unzip") .arg(&local_path) .arg("-d") diff --git a/tiles/src/repl.rs b/tiles/src/repl.rs index 6b7912f..835f2c1 100644 --- a/tiles/src/repl.rs +++ b/tiles/src/repl.rs @@ -694,6 +694,12 @@ async fn start_repl(modelfile: &Modelfile, _run_args: &RunArgs, db_conn: &Dbconn } } + // This is to prevent the session name being messeup if user starts + // with skills. Pi unfurls skills command to entire skill doc.So we + // cant put that as session name. + if !repl_session.session_started { + repl_session.session_snapshot.name = input.clone(); + }; // Reads the output from Pi and process and responds to the repl let mut reader = BufReader::new(&mut pi_stdout).lines(); @@ -1426,6 +1432,7 @@ fn save_agent_session( model: repl_session.current_modelname.clone(), messages: vec![], }; + //TODO: We need to these at the `message_end` event maybe, need to check for msg in agent_end_event.messages { //TODO: could avoid this cloning here let msg_copy = msg.clone(); @@ -1436,11 +1443,10 @@ fn save_agent_session( create_session( &db_conn.chat, &repl_session.session_id, - &input, + &repl_session.session_snapshot.name, ¤t_user.user_id, )?; repl_session.session_started = true; - repl_session.session_snapshot.name = input.clone(); None } else { repl_session.last_chat_id.clone()