feat: associate threads with projects (#8745)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -181,6 +181,15 @@ pub struct RemoveSecretRequest {
|
||||
pub key: String,
|
||||
}
|
||||
|
||||
/// Update the project association for a session.
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(method = "_goose/session/update_project", response = EmptyResponse)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct UpdateSessionProjectRequest {
|
||||
pub session_id: String,
|
||||
pub project_id: Option<String>,
|
||||
}
|
||||
|
||||
/// Archive a session (soft delete).
|
||||
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
|
||||
#[request(method = "_goose/session/archive", response = EmptyResponse)]
|
||||
|
||||
@@ -90,6 +90,11 @@
|
||||
"requestType": "ImportSessionRequest",
|
||||
"responseType": "ImportSessionResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/session/update_project",
|
||||
"requestType": "UpdateSessionProjectRequest",
|
||||
"responseType": "EmptyResponse"
|
||||
},
|
||||
{
|
||||
"method": "_goose/session/archive",
|
||||
"requestType": "ArchiveSessionRequest",
|
||||
|
||||
@@ -669,6 +669,26 @@
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/session/import"
|
||||
},
|
||||
"UpdateSessionProjectRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"sessionId": {
|
||||
"type": "string"
|
||||
},
|
||||
"projectId": {
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"sessionId"
|
||||
],
|
||||
"description": "Update the project association for a session.",
|
||||
"x-side": "agent",
|
||||
"x-method": "_goose/session/update_project"
|
||||
},
|
||||
"ArchiveSessionRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1487,6 +1507,15 @@
|
||||
"description": "Params for _goose/session/import",
|
||||
"title": "ImportSessionRequest"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/$defs/UpdateSessionProjectRequest"
|
||||
}
|
||||
],
|
||||
"description": "Params for _goose/session/update_project",
|
||||
"title": "UpdateSessionProjectRequest"
|
||||
},
|
||||
{
|
||||
"allOf": [
|
||||
{
|
||||
|
||||
@@ -153,6 +153,24 @@ fn sid_short(id: &str) -> String {
|
||||
id.chars().take(8).collect()
|
||||
}
|
||||
|
||||
fn thread_session_meta(
|
||||
message_count: i64,
|
||||
metadata: &crate::session::ThreadMetadata,
|
||||
) -> serde_json::Map<String, serde_json::Value> {
|
||||
let mut meta = serde_json::Map::new();
|
||||
meta.insert(
|
||||
"messageCount".to_string(),
|
||||
serde_json::Value::Number(message_count.into()),
|
||||
);
|
||||
if let Some(ref pid) = metadata.project_id {
|
||||
meta.insert(
|
||||
"projectId".to_string(),
|
||||
serde_json::Value::String(pid.clone()),
|
||||
);
|
||||
}
|
||||
meta
|
||||
}
|
||||
|
||||
fn extract_timeout_from_meta(meta: &Option<Meta>) -> Option<u64> {
|
||||
meta.as_ref()
|
||||
.and_then(|m| m.get("timeout"))
|
||||
@@ -1539,9 +1557,17 @@ impl GooseAcpAgent {
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
let project_id = args
|
||||
.meta
|
||||
.as_ref()
|
||||
.and_then(|m| m.get("projectId"))
|
||||
.and_then(|v| v.as_str())
|
||||
.map(|s| s.to_string());
|
||||
|
||||
// Create the Thread — this IS the ACP session from the client's perspective.
|
||||
let thread_metadata = crate::session::ThreadMetadata {
|
||||
provider_id: requested_provider.clone(),
|
||||
project_id,
|
||||
mode: Some(self.goose_mode.to_string()),
|
||||
..Default::default()
|
||||
};
|
||||
@@ -2544,11 +2570,7 @@ impl GooseAcpAgent {
|
||||
.as_deref()
|
||||
.map(std::path::PathBuf::from)
|
||||
.unwrap_or_default();
|
||||
let mut meta = serde_json::Map::new();
|
||||
meta.insert(
|
||||
"messageCount".to_string(),
|
||||
serde_json::Value::Number(t.message_count.into()),
|
||||
);
|
||||
let meta = thread_session_meta(t.message_count, &t.metadata);
|
||||
SessionInfo::new(SessionId::new(t.id), cwd)
|
||||
.title(t.name)
|
||||
.updated_at(t.updated_at.to_rfc3339())
|
||||
@@ -2613,11 +2635,7 @@ impl GooseAcpAgent {
|
||||
},
|
||||
);
|
||||
|
||||
let mut meta = serde_json::Map::new();
|
||||
meta.insert(
|
||||
"messageCount".to_string(),
|
||||
serde_json::Value::Number(new_thread.message_count.into()),
|
||||
);
|
||||
let meta = thread_session_meta(new_thread.message_count, &new_thread.metadata);
|
||||
|
||||
let mut response = ForkSessionResponse::new(SessionId::new(new_thread_id))
|
||||
.modes(mode_state)
|
||||
@@ -3047,6 +3065,19 @@ impl GooseAcpAgent {
|
||||
})
|
||||
}
|
||||
|
||||
#[custom_method(UpdateSessionProjectRequest)]
|
||||
async fn on_update_session_project(
|
||||
&self,
|
||||
req: UpdateSessionProjectRequest,
|
||||
) -> Result<EmptyResponse, sacp::Error> {
|
||||
let project_id = req.project_id;
|
||||
self.update_thread_metadata(&req.session_id, move |meta| {
|
||||
meta.project_id = project_id;
|
||||
})
|
||||
.await?;
|
||||
Ok(EmptyResponse {})
|
||||
}
|
||||
|
||||
#[custom_method(ArchiveSessionRequest)]
|
||||
async fn on_archive_session(
|
||||
&self,
|
||||
@@ -3189,7 +3220,7 @@ impl GooseAcpAgent {
|
||||
other => {
|
||||
return Err(
|
||||
sacp::Error::invalid_params().data(format!("Unsupported format: {other}"))
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user