diff --git a/core/src/types.rs b/core/src/types.rs index a81cf45..1462df1 100644 --- a/core/src/types.rs +++ b/core/src/types.rs @@ -133,6 +133,15 @@ pub struct Session { /// Isolated git worktree of this session: {repo, path, branch, base}. #[serde(default, skip_serializing_if = "Option::is_none")] pub worktree: Option, + /// Shown at the top of the session list. + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + pub pinned: bool, + /// Hidden from the session list (still searchable). + #[serde(default, skip_serializing_if = "std::ops::Not::not")] + pub archived: bool, + /// User-defined group in the session list. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub folder: Option, } impl Session { diff --git a/server/src/api.rs b/server/src/api.rs index 604df6a..fd3151e 100644 --- a/server/src/api.rs +++ b/server/src/api.rs @@ -129,6 +129,8 @@ pub fn router(app: AppState) -> Router { .route("/account/git", get(get_git_settings).put(put_git_settings)) .route("/account/prefs", get(crate::cli_api::get_prefs).put(crate::cli_api::put_prefs)) .route("/account/memory", get(crate::cli_api::get_memory).put(crate::cli_api::put_memory)) + .route("/templates", get(crate::cli_api::get_templates)) + .route("/templates/{scope}", put(crate::cli_api::put_templates)) .route("/cli/config", get(crate::cli_api::config)) .route("/cli/skill/{name}", get(crate::cli_api::skill)) .route("/cli/tool", post(crate::cli_api::tool)) @@ -471,6 +473,9 @@ async fn create_session(Extension(user): Extension, State(app): State, State(app): State, permission_mode: Option, clean_room: Option, + pinned: Option, + archived: Option, + /// "" removes the folder. + folder: Option, } async fn update_session(Extension(user): Extension, State(app): State, Path(id): Path, Json(body): Json) -> ApiResult { let mut s = writable(&app, &user, &id)?; + // Organising the list must not move the session to "today". + let organise_only = body.title.is_none() && body.model.is_none() && body.agent.is_none() && body.clean_room.is_none() && body.permission_mode.is_none(); if let Some(t) = body.title { s.title = t; } @@ -690,6 +703,24 @@ async fn update_session(Extension(user): Extension, State(app): State(); + s.folder = (!f.is_empty()).then_some(f); + } + if organise_only { + app.store.save_session(&s)?; + app.emit(Event::SessionUpdated { session: s.clone() }); + return Ok(Json(json!(s))); + } if let Some(p) = body.permission_mode { if !crate::permission::MODES.contains(&p.as_str()) { return Err(ApiError(StatusCode::BAD_REQUEST, "invalid permission mode".into())); diff --git a/server/src/cli_api.rs b/server/src/cli_api.rs index 35bf099..d6985e6 100644 --- a/server/src/cli_api.rs +++ b/server/src/cli_api.rs @@ -248,3 +248,47 @@ pub async fn put_memory(Extension(user): Extension, State(app): State, State(app): State) -> ApiResult { + let personal: Vec