diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 83cc474e3367..96c4cf227067 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -322,15 +322,37 @@ async fn get_or_create_session_id( let session_manager = SessionManager::instance(); - let Some(id) = identifier else { - return if resume { + let resolved_id = if resume { + let Some(id) = identifier else { let sessions = session_manager.list_sessions().await?; let session_id = sessions .first() .map(|s| s.id.clone()) .ok_or_else(|| anyhow::anyhow!("No session found to resume"))?; - Ok(Some(session_id)) + return Ok(Some(session_id)); + }; + + if let Some(session_id) = id.session_id { + session_id + } else if let Some(name) = id.name { + let sessions = session_manager.list_sessions().await?; + sessions + .into_iter() + .find(|s| s.name == name || s.id == name) + .map(|s| s.id) + .ok_or_else(|| anyhow::anyhow!("No session found with name '{}'", name))? + } else if let Some(path) = id.path { + path.file_stem() + .and_then(|s| s.to_str()) + .map(|s| s.to_string()) + .ok_or_else(|| { + anyhow::anyhow!("Could not extract session ID from path: {:?}", path) + })? } else { + return Err(anyhow::anyhow!("Invalid identifier")); + } + } else { + let Some(id) = identifier else { let session = session_manager .create_session( std::env::current_dir()?, @@ -338,58 +360,39 @@ async fn get_or_create_session_id( SessionType::User, ) .await?; - Ok(Some(session.id)) + return Ok(Some(session.id)); }; - }; - if let Some(session_id) = id.session_id { - Ok(Some(session_id)) - } else if let Some(name) = id.name { - if resume { - let sessions = session_manager.list_sessions().await?; - let session_id = sessions - .into_iter() - .find(|s| s.name == name || s.id == name) - .map(|s| s.id) - .ok_or_else(|| anyhow::anyhow!("No session found with name '{}'", name))?; - Ok(Some(session_id)) - } else { - let session = session_manager - .create_session(std::env::current_dir()?, name.clone(), SessionType::User) - .await?; + if id.session_id.is_some() { + return Err(anyhow::anyhow!("Cannot use --session-id without --resume")); + } + + let has_user_provided_name = id.name.is_some(); + let name = id.name.unwrap_or_else(|| "CLI Session".to_string()); + let session = session_manager + .create_session(std::env::current_dir()?, name.clone(), SessionType::User) + .await?; + if has_user_provided_name { session_manager .update(&session.id) .user_provided_name(name) .apply() .await?; - - Ok(Some(session.id)) } - } else if let Some(path) = id.path { - let session_id = path - .file_stem() - .and_then(|s| s.to_str()) - .map(|s| s.to_string()) - .ok_or_else(|| anyhow::anyhow!("Could not extract session ID from path: {:?}", path))?; - Ok(Some(session_id)) - } else { - let session = session_manager - .create_session( - std::env::current_dir()?, - "CLI Session".to_string(), - SessionType::User, - ) - .await?; - Ok(Some(session.id)) - } + + return Ok(Some(session.id)); + }; + + Ok(Some(resolved_id)) } async fn lookup_session_id(identifier: Identifier) -> Result { + let session_manager = SessionManager::instance(); + if let Some(session_id) = identifier.session_id { Ok(session_id) } else if let Some(name) = identifier.name { - let session_manager = SessionManager::instance(); let sessions = session_manager.list_sessions().await?; sessions .into_iter() @@ -722,6 +725,15 @@ enum Command { )] resume: bool, + /// Fork a previous session (creates new session with copied history) + #[arg( + long, + requires = "resume", + help = "Fork a previous session (creates new session with copied history)", + long_help = "Create a new session by copying all messages from a previous session. Must be used with --resume. If --name or --session-id is provided, forks that specific session. Otherwise forks the most recently used session." + )] + fork: bool, + /// Show message history when resuming #[arg( long, @@ -1047,6 +1059,7 @@ async fn handle_session_subcommand(command: SessionCommand) -> Result<()> { async fn handle_interactive_session( identifier: Option, resume: bool, + fork: bool, history: bool, session_opts: SessionOptions, extension_opts: ExtensionOptions, @@ -1056,7 +1069,13 @@ async fn handle_interactive_session( } let session_start = std::time::Instant::now(); - let session_type = if resume { "resumed" } else { "new" }; + let session_type = if fork { + "forked" + } else if resume { + "resumed" + } else { + "new" + }; tracing::info!( counter.goose.session_starts = 1, @@ -1076,11 +1095,21 @@ async fn handle_interactive_session( } } - let session_id = get_or_create_session_id(identifier, resume, false).await?; + let mut session_id = get_or_create_session_id(identifier, resume, false).await?; + + if fork { + if let Some(id) = session_id { + let session_manager = SessionManager::instance(); + let original = session_manager.get_session(&id, false).await?; + let copied = session_manager.copy_session(&id, original.name).await?; + session_id = Some(copied.id); + } + } let mut session: crate::CliSession = build_session(SessionBuilderConfig { session_id, resume, + fork, no_session: false, extensions: extension_opts.extensions, streamable_http_extensions: extension_opts.streamable_http_extensions, @@ -1099,7 +1128,7 @@ async fn handle_interactive_session( }) .await; - if resume && history { + if (resume || fork) && history { session.render_message_history(); } @@ -1283,6 +1312,7 @@ async fn handle_run_command( let mut session = build_session(SessionBuilderConfig { session_id, resume: run_behavior.resume, + fork: false, no_session: run_behavior.no_session, extensions: extension_opts.extensions, streamable_http_extensions: extension_opts.streamable_http_extensions, @@ -1407,6 +1437,7 @@ async fn handle_default_session() -> Result<()> { let mut session = build_session(SessionBuilderConfig { session_id, resume: false, + fork: false, no_session: false, extensions: Vec::new(), streamable_http_extensions: Vec::new(), @@ -1458,12 +1489,20 @@ pub async fn cli() -> anyhow::Result<()> { command: None, identifier, resume, + fork, history, session_opts, extension_opts, }) => { - handle_interactive_session(identifier, resume, history, session_opts, extension_opts) - .await + handle_interactive_session( + identifier, + resume, + fork, + history, + session_opts, + extension_opts, + ) + .await } Some(Command::Project {}) => { handle_project_default()?; diff --git a/crates/goose-cli/src/commands/bench.rs b/crates/goose-cli/src/commands/bench.rs index bac2ef88ed2f..d614c0049bf0 100644 --- a/crates/goose-cli/src/commands/bench.rs +++ b/crates/goose-cli/src/commands/bench.rs @@ -37,6 +37,7 @@ pub async fn agent_generator( let base_session = build_session(SessionBuilderConfig { session_id: Some(session_id), resume: false, + fork: false, no_session: false, extensions: requirements.external, streamable_http_extensions: requirements.streamable_http, diff --git a/crates/goose-cli/src/session/builder.rs b/crates/goose-cli/src/session/builder.rs index 4ecc96ea38f0..788456446afa 100644 --- a/crates/goose-cli/src/session/builder.rs +++ b/crates/goose-cli/src/session/builder.rs @@ -82,6 +82,8 @@ pub struct SessionBuilderConfig { pub session_id: Option, /// Whether to resume an existing session pub resume: bool, + /// Whether to fork an existing session (creates a copy of the original/existing session then resumes the copy) + pub fork: bool, /// Whether to run without a session file pub no_session: bool, /// List of stdio extension commands to add @@ -121,6 +123,7 @@ impl Default for SessionBuilderConfig { SessionBuilderConfig { session_id: None, resume: false, + fork: false, no_session: false, extensions: Vec::new(), streamable_http_extensions: Vec::new(), @@ -659,6 +662,7 @@ mod tests { let config = SessionBuilderConfig { session_id: None, resume: false, + fork: false, no_session: false, extensions: vec!["echo test".to_string()], streamable_http_extensions: vec!["http://localhost:8080/mcp".to_string()], @@ -705,6 +709,7 @@ mod tests { assert!(config.scheduled_job_id.is_none()); assert!(!config.interactive); assert!(!config.quiet); + assert!(!config.fork); } #[tokio::test] diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index be4634b17de2..3cc8cac406e7 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -380,7 +380,7 @@ derive_utoipa!(Icon as IconSchema); super::routes::session::export_session, super::routes::session::import_session, super::routes::session::update_session_user_recipe_values, - super::routes::session::edit_message, + super::routes::session::fork_session, super::routes::session::get_session_extensions, super::routes::schedule::create_schedule, super::routes::schedule::list_schedules, @@ -442,9 +442,8 @@ derive_utoipa!(Icon as IconSchema); super::routes::session::UpdateSessionNameRequest, super::routes::session::UpdateSessionUserRecipeValuesRequest, super::routes::session::UpdateSessionUserRecipeValuesResponse, - super::routes::session::EditType, - super::routes::session::EditMessageRequest, - super::routes::session::EditMessageResponse, + super::routes::session::ForkRequest, + super::routes::session::ForkResponse, super::routes::session::SessionExtensionsResponse, Message, MessageContent, diff --git a/crates/goose-server/src/routes/session.rs b/crates/goose-server/src/routes/session.rs index f9ac4278bec9..c214c86f4c60 100644 --- a/crates/goose-server/src/routes/session.rs +++ b/crates/goose-server/src/routes/session.rs @@ -51,28 +51,17 @@ pub struct ImportSessionRequest { json: String, } -#[derive(Debug, Deserialize, ToSchema)] -#[serde(rename_all = "lowercase")] -pub enum EditType { - Fork, - Edit, -} - #[derive(Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct EditMessageRequest { - timestamp: i64, - #[serde(default = "default_edit_type")] - edit_type: EditType, -} - -fn default_edit_type() -> EditType { - EditType::Fork +pub struct ForkRequest { + timestamp: Option, + truncate: bool, + copy: bool, } #[derive(Serialize, ToSchema)] #[serde(rename_all = "camelCase")] -pub struct EditMessageResponse { +pub struct ForkResponse { session_id: String, } @@ -369,16 +358,16 @@ async fn import_session( #[utoipa::path( post, - path = "/sessions/{session_id}/edit_message", - request_body = EditMessageRequest, + path = "/sessions/{session_id}/fork", + request_body = ForkRequest, params( ("session_id" = String, Path, description = "Unique identifier for the session") ), responses( - (status = 200, description = "Session prepared for editing - frontend should submit the edited message", body = EditMessageResponse), - (status = 400, description = "Bad request - Invalid message timestamp"), + (status = 200, description = "Session forked successfully", body = ForkResponse), + (status = 400, description = "Bad request - truncate=true requires timestamp"), (status = 401, description = "Unauthorized - Invalid or missing API key"), - (status = 404, description = "Session or message not found"), + (status = 404, description = "Session not found"), (status = 500, description = "Internal server error") ), security( @@ -386,51 +375,75 @@ async fn import_session( ), tag = "Session Management" )] -async fn edit_message( +async fn fork_session( State(state): State>, Path(session_id): Path, - Json(request): Json, -) -> Result, StatusCode> { - let manager = state.session_manager(); - match request.edit_type { - EditType::Fork => { - let new_session = manager - .copy_session(&session_id, "(edited)".to_string()) - .await - .map_err(|e| { - tracing::error!("Failed to copy session: {}", e); - goose::posthog::emit_error("session_copy_failed", &e.to_string()); - StatusCode::INTERNAL_SERVER_ERROR - })?; - - manager - .truncate_conversation(&new_session.id, request.timestamp) - .await - .map_err(|e| { - tracing::error!("Failed to truncate conversation: {}", e); - goose::posthog::emit_error("session_truncate_failed", &e.to_string()); - StatusCode::INTERNAL_SERVER_ERROR - })?; - - Ok(Json(EditMessageResponse { - session_id: new_session.id, - })) - } - EditType::Edit => { - manager - .truncate_conversation(&session_id, request.timestamp) - .await - .map_err(|e| { - tracing::error!("Failed to truncate conversation: {}", e); - goose::posthog::emit_error("session_truncate_failed", &e.to_string()); - StatusCode::INTERNAL_SERVER_ERROR - })?; + Json(request): Json, +) -> Result, ErrorResponse> { + if request.truncate && request.timestamp.is_none() { + return Err(ErrorResponse { + message: "truncate=true requires a timestamp".to_string(), + status: StatusCode::BAD_REQUEST, + }); + } - Ok(Json(EditMessageResponse { - session_id: session_id.clone(), - })) - } + let session_manager = state.session_manager(); + + let target_session_id = if request.copy { + let original = session_manager + .get_session(&session_id, false) + .await + .map_err(|e| { + tracing::error!("Failed to get session: {}", e); + goose::posthog::emit_error("session_get_failed", &e.to_string()); + ErrorResponse { + message: if e.to_string().contains("not found") { + format!("Session {} not found", session_id) + } else { + format!("Failed to get session: {}", e) + }, + status: if e.to_string().contains("not found") { + StatusCode::NOT_FOUND + } else { + StatusCode::INTERNAL_SERVER_ERROR + }, + } + })?; + + let copied = session_manager + .copy_session(&session_id, original.name) + .await + .map_err(|e| { + tracing::error!("Failed to copy session: {}", e); + goose::posthog::emit_error("session_copy_failed", &e.to_string()); + ErrorResponse { + message: format!("Failed to copy session: {}", e), + status: StatusCode::INTERNAL_SERVER_ERROR, + } + })?; + + copied.id + } else { + session_id.clone() + }; + + if request.truncate { + session_manager + .truncate_conversation(&target_session_id, request.timestamp.unwrap_or(0)) + .await + .map_err(|e| { + tracing::error!("Failed to truncate conversation: {}", e); + goose::posthog::emit_error("session_truncate_failed", &e.to_string()); + ErrorResponse { + message: format!("Failed to truncate conversation: {}", e), + status: StatusCode::INTERNAL_SERVER_ERROR, + } + })?; } + + Ok(Json(ForkResponse { + session_id: target_session_id, + })) } #[derive(Serialize, ToSchema)] @@ -487,7 +500,7 @@ pub fn routes(state: Arc) -> Router { "/sessions/{session_id}/user_recipe_values", put(update_session_user_recipe_values), ) - .route("/sessions/{session_id}/edit_message", post(edit_message)) + .route("/sessions/{session_id}/fork", post(fork_session)) .route( "/sessions/{session_id}/extensions", get(get_session_extensions), diff --git a/crates/goose/src/session/session_manager.rs b/crates/goose/src/session/session_manager.rs index 7523ccc33720..4b59c0b1a919 100644 --- a/crates/goose/src/session/session_manager.rs +++ b/crates/goose/src/session/session_manager.rs @@ -1285,14 +1285,23 @@ impl SessionStorage { ) .await?; - session_manager + let mut builder = session_manager .update(&new_session.id) .extension_data(original_session.extension_data) .schedule_id(original_session.schedule_id) .recipe(original_session.recipe) - .user_recipe_values(original_session.user_recipe_values) - .apply() - .await?; + .user_recipe_values(original_session.user_recipe_values); + + // Preserve provider and model config from original session + if let Some(provider_name) = original_session.provider_name { + builder = builder.provider_name(provider_name); + } + + if let Some(model_config) = original_session.model_config { + builder = builder.model_config(model_config); + } + + builder.apply().await?; if let Some(conversation) = original_session.conversation { self.replace_conversation(&new_session.id, &conversation) diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 267cdfa8a653..de6e90d72a7c 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -2472,12 +2472,12 @@ ] } }, - "/sessions/{session_id}/edit_message": { - "post": { + "/sessions/{session_id}/export": { + "get": { "tags": [ "Session Management" ], - "operationId": "edit_message", + "operationId": "export_session", "parameters": [ { "name": "session_id", @@ -2489,35 +2489,22 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/EditMessageRequest" - } - } - }, - "required": true - }, "responses": { "200": { - "description": "Session prepared for editing - frontend should submit the edited message", + "description": "Session exported successfully", "content": { - "application/json": { + "text/plain": { "schema": { - "$ref": "#/components/schemas/EditMessageResponse" + "type": "string" } } } }, - "400": { - "description": "Bad request - Invalid message timestamp" - }, "401": { "description": "Unauthorized - Invalid or missing API key" }, "404": { - "description": "Session or message not found" + "description": "Session not found" }, "500": { "description": "Internal server error" @@ -2530,12 +2517,12 @@ ] } }, - "/sessions/{session_id}/export": { + "/sessions/{session_id}/extensions": { "get": { "tags": [ "Session Management" ], - "operationId": "export_session", + "operationId": "get_session_extensions", "parameters": [ { "name": "session_id", @@ -2549,11 +2536,11 @@ ], "responses": { "200": { - "description": "Session exported successfully", + "description": "Session extensions retrieved successfully", "content": { - "text/plain": { + "application/json": { "schema": { - "type": "string" + "$ref": "#/components/schemas/SessionExtensionsResponse" } } } @@ -2575,12 +2562,12 @@ ] } }, - "/sessions/{session_id}/extensions": { - "get": { + "/sessions/{session_id}/fork": { + "post": { "tags": [ "Session Management" ], - "operationId": "get_session_extensions", + "operationId": "fork_session", "parameters": [ { "name": "session_id", @@ -2592,17 +2579,30 @@ } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ForkRequest" + } + } + }, + "required": true + }, "responses": { "200": { - "description": "Session extensions retrieved successfully", + "description": "Session forked successfully", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SessionExtensionsResponse" + "$ref": "#/components/schemas/ForkResponse" } } } }, + "400": { + "description": "Bad request - truncate=true requires timestamp" + }, "401": { "description": "Unauthorized - Invalid or missing API key" }, @@ -3453,39 +3453,6 @@ } } }, - "EditMessageRequest": { - "type": "object", - "required": [ - "timestamp" - ], - "properties": { - "editType": { - "$ref": "#/components/schemas/EditType" - }, - "timestamp": { - "type": "integer", - "format": "int64" - } - } - }, - "EditMessageResponse": { - "type": "object", - "required": [ - "sessionId" - ], - "properties": { - "sessionId": { - "type": "string" - } - } - }, - "EditType": { - "type": "string", - "enum": [ - "fork", - "edit" - ] - }, "EmbeddedResource": { "type": "object", "required": [ @@ -3961,6 +3928,37 @@ } } }, + "ForkRequest": { + "type": "object", + "required": [ + "truncate", + "copy" + ], + "properties": { + "copy": { + "type": "boolean" + }, + "timestamp": { + "type": "integer", + "format": "int64", + "nullable": true + }, + "truncate": { + "type": "boolean" + } + } + }, + "ForkResponse": { + "type": "object", + "required": [ + "sessionId" + ], + "properties": { + "sessionId": { + "type": "string" + } + } + }, "FrontendToolRequest": { "type": "object", "required": [ diff --git a/ui/desktop/src/api/index.ts b/ui/desktop/src/api/index.ts index aa0b2b31fe7f..e1118ac1a3cb 100644 --- a/ui/desktop/src/api/index.ts +++ b/ui/desktop/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { addExtension, agentAddExtension, agentRemoveExtension, backupConfig, callTool, checkProvider, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteRecipe, deleteSchedule, deleteSession, detectProvider, diagnostics, editMessage, encodeRecipe, exportSession, getCustomProvider, getExtensions, getPricing, getPrompt, getPrompts, getProviderModels, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importSession, initConfig, inspectRunningJob, killRunningJob, listApps, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, recoverConfig, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, sendTelemetryEvent, sessionsHandler, setConfigProvider, setRecipeSlashCommand, startAgent, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, systemInfo, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateSchedule, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; -export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, BackupConfigData, BackupConfigErrors, BackupConfigResponse, BackupConfigResponses, CallToolData, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, ChatRequest, CheckProviderData, CheckProviderRequest, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderRequest, DetectProviderResponse, DetectProviderResponse2, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageRequest, EditMessageResponse, EditMessageResponse2, EditMessageResponses, EditType, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, ErrorResponse, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FrontendToolRequest, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetPricingData, GetPricingResponse, GetPricingResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, Icon, ImageContent, ImportSessionData, ImportSessionErrors, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponse, InitConfigResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelConfig, ModelInfo, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, PermissionLevel, PricingData, PricingQuery, PricingResponse, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponse, RecoverConfigResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionDisplayInfo, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TelemetryEventRequest, Template, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WindowProps } from './types.gen'; +export { addExtension, agentAddExtension, agentRemoveExtension, backupConfig, callTool, checkProvider, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteRecipe, deleteSchedule, deleteSession, detectProvider, diagnostics, encodeRecipe, exportSession, forkSession, getCustomProvider, getExtensions, getPricing, getPrompt, getPrompts, getProviderModels, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importSession, initConfig, inspectRunningJob, killRunningJob, listApps, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, recoverConfig, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, sendTelemetryEvent, sessionsHandler, setConfigProvider, setRecipeSlashCommand, startAgent, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, systemInfo, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateSchedule, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; +export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, BackupConfigData, BackupConfigErrors, BackupConfigResponse, BackupConfigResponses, CallToolData, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, ChatRequest, CheckProviderData, CheckProviderRequest, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderRequest, DetectProviderResponse, DetectProviderResponse2, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, ErrorResponse, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetPricingData, GetPricingResponse, GetPricingResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, Icon, ImageContent, ImportSessionData, ImportSessionErrors, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponse, InitConfigResponses, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelConfig, ModelInfo, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, PermissionLevel, PricingData, PricingQuery, PricingResponse, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderDetails, ProviderEngine, ProviderMetadata, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponse, RecoverConfigResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionDisplayInfo, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TelemetryEventRequest, Template, TextContent, ThinkingContent, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WindowProps } from './types.gen'; diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index 2026f2d8f050..db9f8d725097 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EditMessageData, EditMessageErrors, EditMessageResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; +import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, BackupConfigData, BackupConfigErrors, BackupConfigResponses, CallToolData, CallToolErrors, CallToolResponses, CheckProviderData, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DetectProviderData, DetectProviderErrors, DetectProviderResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetPricingData, GetPricingResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportSessionData, ImportSessionErrors, ImportSessionResponses, InitConfigData, InitConfigErrors, InitConfigResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RecoverConfigData, RecoverConfigErrors, RecoverConfigResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SystemInfoData, SystemInfoResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -428,8 +428,12 @@ export const deleteSession = (options: Opt export const getSession = (options: Options) => (options.client ?? client).get({ url: '/sessions/{session_id}', ...options }); -export const editMessage = (options: Options) => (options.client ?? client).post({ - url: '/sessions/{session_id}/edit_message', +export const exportSession = (options: Options) => (options.client ?? client).get({ url: '/sessions/{session_id}/export', ...options }); + +export const getSessionExtensions = (options: Options) => (options.client ?? client).get({ url: '/sessions/{session_id}/extensions', ...options }); + +export const forkSession = (options: Options) => (options.client ?? client).post({ + url: '/sessions/{session_id}/fork', ...options, headers: { 'Content-Type': 'application/json', @@ -437,10 +441,6 @@ export const editMessage = (options: Optio } }); -export const exportSession = (options: Options) => (options.client ?? client).get({ url: '/sessions/{session_id}/export', ...options }); - -export const getSessionExtensions = (options: Options) => (options.client ?? client).get({ url: '/sessions/{session_id}/extensions', ...options }); - export const updateSessionName = (options: Options) => (options.client ?? client).put({ url: '/sessions/{session_id}/name', ...options, diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index 63a51b16aa8b..bcef9d17ec09 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -189,17 +189,6 @@ export type DetectProviderResponse = { provider_name: string; }; -export type EditMessageRequest = { - editType?: EditType; - timestamp: number; -}; - -export type EditMessageResponse = { - sessionId: string; -}; - -export type EditType = 'fork' | 'edit'; - export type EmbeddedResource = { _meta?: { [key: string]: unknown; @@ -352,6 +341,16 @@ export type ExtensionResponse = { warnings?: Array; }; +export type ForkRequest = { + copy: boolean; + timestamp?: number | null; + truncate: boolean; +}; + +export type ForkResponse = { + sessionId: string; +}; + export type FrontendToolRequest = { id: string; toolCall: { @@ -3178,8 +3177,8 @@ export type GetSessionResponses = { export type GetSessionResponse = GetSessionResponses[keyof GetSessionResponses]; -export type EditMessageData = { - body: EditMessageRequest; +export type ExportSessionData = { + body?: never; path: { /** * Unique identifier for the session @@ -3187,20 +3186,16 @@ export type EditMessageData = { session_id: string; }; query?: never; - url: '/sessions/{session_id}/edit_message'; + url: '/sessions/{session_id}/export'; }; -export type EditMessageErrors = { - /** - * Bad request - Invalid message timestamp - */ - 400: unknown; +export type ExportSessionErrors = { /** * Unauthorized - Invalid or missing API key */ 401: unknown; /** - * Session or message not found + * Session not found */ 404: unknown; /** @@ -3209,16 +3204,16 @@ export type EditMessageErrors = { 500: unknown; }; -export type EditMessageResponses = { +export type ExportSessionResponses = { /** - * Session prepared for editing - frontend should submit the edited message + * Session exported successfully */ - 200: EditMessageResponse; + 200: string; }; -export type EditMessageResponse2 = EditMessageResponses[keyof EditMessageResponses]; +export type ExportSessionResponse = ExportSessionResponses[keyof ExportSessionResponses]; -export type ExportSessionData = { +export type GetSessionExtensionsData = { body?: never; path: { /** @@ -3227,10 +3222,10 @@ export type ExportSessionData = { session_id: string; }; query?: never; - url: '/sessions/{session_id}/export'; + url: '/sessions/{session_id}/extensions'; }; -export type ExportSessionErrors = { +export type GetSessionExtensionsErrors = { /** * Unauthorized - Invalid or missing API key */ @@ -3245,17 +3240,17 @@ export type ExportSessionErrors = { 500: unknown; }; -export type ExportSessionResponses = { +export type GetSessionExtensionsResponses = { /** - * Session exported successfully + * Session extensions retrieved successfully */ - 200: string; + 200: SessionExtensionsResponse; }; -export type ExportSessionResponse = ExportSessionResponses[keyof ExportSessionResponses]; +export type GetSessionExtensionsResponse = GetSessionExtensionsResponses[keyof GetSessionExtensionsResponses]; -export type GetSessionExtensionsData = { - body?: never; +export type ForkSessionData = { + body: ForkRequest; path: { /** * Unique identifier for the session @@ -3263,10 +3258,14 @@ export type GetSessionExtensionsData = { session_id: string; }; query?: never; - url: '/sessions/{session_id}/extensions'; + url: '/sessions/{session_id}/fork'; }; -export type GetSessionExtensionsErrors = { +export type ForkSessionErrors = { + /** + * Bad request - truncate=true requires timestamp + */ + 400: unknown; /** * Unauthorized - Invalid or missing API key */ @@ -3281,14 +3280,14 @@ export type GetSessionExtensionsErrors = { 500: unknown; }; -export type GetSessionExtensionsResponses = { +export type ForkSessionResponses = { /** - * Session extensions retrieved successfully + * Session forked successfully */ - 200: SessionExtensionsResponse; + 200: ForkResponse; }; -export type GetSessionExtensionsResponse = GetSessionExtensionsResponses[keyof GetSessionExtensionsResponses]; +export type ForkSessionResponse = ForkSessionResponses[keyof ForkSessionResponses]; export type UpdateSessionNameData = { body: UpdateSessionNameRequest; diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index a91e4507b4a9..659d46940010 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -1,6 +1,6 @@ import { AppEvents } from '../constants/events'; import React, { useRef, useState, useEffect, useMemo, useCallback } from 'react'; -import { Bug, ScrollText, ChefHat } from 'lucide-react'; +import { Bug, ChefHat, ScrollText } from 'lucide-react'; import { Tooltip, TooltipContent, TooltipTrigger } from './ui/Tooltip'; import { Button } from './ui/button'; import type { View } from '../utils/navigationUtils'; diff --git a/ui/desktop/src/components/sessions/SessionListView.tsx b/ui/desktop/src/components/sessions/SessionListView.tsx index c4cebdacd2ea..a2178b16717e 100644 --- a/ui/desktop/src/components/sessions/SessionListView.tsx +++ b/ui/desktop/src/components/sessions/SessionListView.tsx @@ -11,6 +11,7 @@ import { Download, Upload, ExternalLink, + Copy, Puzzle, } from 'lucide-react'; import { Card } from '../ui/card'; @@ -28,6 +29,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../ui/ import { deleteSession, exportSession, + forkSession, importSession, listSessions, Session, @@ -436,6 +438,25 @@ const SessionListView: React.FC = React.memo( setShowDeleteConfirmation(true); }, []); + const handleDuplicateSession = useCallback( + async (session: Session) => { + try { + await forkSession({ + path: { session_id: session.id }, + body: { truncate: false, copy: true }, + throwOnError: true, + }); + toast.success(`Session "${session.name}" duplicated successfully`); + await loadSessions(); + } catch (error) { + console.error('Error duplicating session:', error); + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + toast.error(`Failed to duplicate session: ${errorMessage}`); + } + }, + [loadSessions] + ); + const handleConfirmDelete = useCallback(async () => { if (!sessionToDelete) return; @@ -530,27 +551,37 @@ const SessionListView: React.FC = React.memo( const SessionItem = React.memo(function SessionItem({ session, onEditClick, + onDuplicateClick, onDeleteClick, onExportClick, onOpenInNewWindow, }: { session: Session; onEditClick: (session: Session) => void; + onDuplicateClick: (session: Session) => void; onDeleteClick: (session: Session) => void; onExportClick: (session: Session, e: React.MouseEvent) => void; onOpenInNewWindow: (session: Session, e: React.MouseEvent) => void; }) { const handleEditClick = useCallback( (e: React.MouseEvent) => { - e.stopPropagation(); // Prevent card click + e.stopPropagation(); onEditClick(session); }, [onEditClick, session] ); + const handleDuplicateClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + onDuplicateClick(session); + }, + [onDuplicateClick, session] + ); + const handleDeleteClick = useCallback( (e: React.MouseEvent) => { - e.stopPropagation(); // Prevent card click + e.stopPropagation(); onDeleteClick(session); }, [onDeleteClick, session] @@ -605,6 +636,13 @@ const SessionListView: React.FC = React.memo( > +