feat(goose-acp): enable parallel sessions with isolated agent state (#6392)

Signed-off-by: Adrian Cole <adrian@tetrate.io>
This commit is contained in:
Adrian Cole
2026-01-15 06:19:17 +08:00
committed by GitHub
parent fb0eca2c36
commit 7d4a6bd8ff
86 changed files with 2594 additions and 1938 deletions
+60 -19
View File
@@ -13,7 +13,7 @@ use goose::agents::ExtensionConfig;
use goose::recipe::Recipe;
use goose::session::extension_data::ExtensionState;
use goose::session::session_manager::SessionInsights;
use goose::session::{EnabledExtensionsState, Session, SessionManager};
use goose::session::{EnabledExtensionsState, Session};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
@@ -91,8 +91,12 @@ const MAX_NAME_LENGTH: usize = 200;
),
tag = "Session Management"
)]
async fn list_sessions() -> Result<Json<SessionListResponse>, StatusCode> {
let sessions = SessionManager::list_sessions()
async fn list_sessions(
State(state): State<Arc<AppState>>,
) -> Result<Json<SessionListResponse>, StatusCode> {
let sessions = state
.session_manager()
.list_sessions()
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
@@ -116,8 +120,13 @@ async fn list_sessions() -> Result<Json<SessionListResponse>, StatusCode> {
),
tag = "Session Management"
)]
async fn get_session(Path(session_id): Path<String>) -> Result<Json<Session>, StatusCode> {
let session = SessionManager::get_session(&session_id, true)
async fn get_session(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
) -> Result<Json<Session>, StatusCode> {
let session = state
.session_manager()
.get_session(&session_id, true)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;
@@ -136,8 +145,12 @@ async fn get_session(Path(session_id): Path<String>) -> Result<Json<Session>, St
),
tag = "Session Management"
)]
async fn get_session_insights() -> Result<Json<SessionInsights>, StatusCode> {
let insights = SessionManager::get_insights()
async fn get_session_insights(
State(state): State<Arc<AppState>>,
) -> Result<Json<SessionInsights>, StatusCode> {
let insights = state
.session_manager()
.get_insights()
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(insights))
@@ -163,6 +176,7 @@ async fn get_session_insights() -> Result<Json<SessionInsights>, StatusCode> {
tag = "Session Management"
)]
async fn update_session_name(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
Json(request): Json<UpdateSessionNameRequest>,
) -> Result<StatusCode, StatusCode> {
@@ -174,7 +188,9 @@ async fn update_session_name(
return Err(StatusCode::BAD_REQUEST);
}
SessionManager::update_session(&session_id)
state
.session_manager()
.update(&session_id)
.user_provided_name(name.to_string())
.apply()
.await
@@ -207,7 +223,9 @@ async fn update_session_user_recipe_values(
Path(session_id): Path<String>,
Json(request): Json<UpdateSessionUserRecipeValuesRequest>,
) -> Result<Json<UpdateSessionUserRecipeValuesResponse>, ErrorResponse> {
SessionManager::update_session(&session_id)
state
.session_manager()
.update(&session_id)
.user_recipe_values(Some(request.user_recipe_values))
.apply()
.await
@@ -216,7 +234,9 @@ async fn update_session_user_recipe_values(
status: StatusCode::INTERNAL_SERVER_ERROR,
})?;
let session = SessionManager::get_session(&session_id, false)
let session = state
.session_manager()
.get_session(&session_id, false)
.await
.map_err(|err| ErrorResponse {
message: err.to_string(),
@@ -270,8 +290,13 @@ async fn update_session_user_recipe_values(
),
tag = "Session Management"
)]
async fn delete_session(Path(session_id): Path<String>) -> Result<StatusCode, StatusCode> {
SessionManager::delete_session(&session_id)
async fn delete_session(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
) -> Result<StatusCode, StatusCode> {
state
.session_manager()
.delete_session(&session_id)
.await
.map_err(|e| {
if e.to_string().contains("not found") {
@@ -301,8 +326,13 @@ async fn delete_session(Path(session_id): Path<String>) -> Result<StatusCode, St
),
tag = "Session Management"
)]
async fn export_session(Path(session_id): Path<String>) -> Result<Json<String>, StatusCode> {
let exported = SessionManager::export_session(&session_id)
async fn export_session(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
) -> Result<Json<String>, StatusCode> {
let exported = state
.session_manager()
.export_session(&session_id)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;
@@ -325,9 +355,12 @@ async fn export_session(Path(session_id): Path<String>) -> Result<Json<String>,
tag = "Session Management"
)]
async fn import_session(
State(state): State<Arc<AppState>>,
Json(request): Json<ImportSessionRequest>,
) -> Result<Json<Session>, StatusCode> {
let session = SessionManager::import_session(&request.json)
let session = state
.session_manager()
.import_session(&request.json)
.await
.map_err(|_| StatusCode::BAD_REQUEST)?;
@@ -354,12 +387,15 @@ async fn import_session(
tag = "Session Management"
)]
async fn edit_message(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
Json(request): Json<EditMessageRequest>,
) -> Result<Json<EditMessageResponse>, StatusCode> {
let manager = state.session_manager();
match request.edit_type {
EditType::Fork => {
let new_session = SessionManager::copy_session(&session_id, "(edited)".to_string())
let new_session = manager
.copy_session(&session_id, "(edited)".to_string())
.await
.map_err(|e| {
tracing::error!("Failed to copy session: {}", e);
@@ -367,7 +403,8 @@ async fn edit_message(
StatusCode::INTERNAL_SERVER_ERROR
})?;
SessionManager::truncate_conversation(&new_session.id, request.timestamp)
manager
.truncate_conversation(&new_session.id, request.timestamp)
.await
.map_err(|e| {
tracing::error!("Failed to truncate conversation: {}", e);
@@ -380,7 +417,8 @@ async fn edit_message(
}))
}
EditType::Edit => {
SessionManager::truncate_conversation(&session_id, request.timestamp)
manager
.truncate_conversation(&session_id, request.timestamp)
.await
.map_err(|e| {
tracing::error!("Failed to truncate conversation: {}", e);
@@ -419,9 +457,12 @@ pub struct SessionExtensionsResponse {
tag = "Session Management"
)]
async fn get_session_extensions(
State(state): State<Arc<AppState>>,
Path(session_id): Path<String>,
) -> Result<Json<SessionExtensionsResponse>, StatusCode> {
let session = SessionManager::get_session(&session_id, false)
let session = state
.session_manager()
.get_session(&session_id, false)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;