Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion crates/goose-sdk-types/src/custom_requests.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use agent_client_protocol::schema::{ContentBlock, McpServer};
use agent_client_protocol::schema::{ContentBlock, McpServer, SessionInfo};
use agent_client_protocol::{JsonRpcRequest, JsonRpcResponse};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -474,6 +474,23 @@ pub struct DictationSecretDeleteRequest {
pub provider: String,
}

/// Return list-style metadata for a single session without loading the conversation.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(
method = "_goose/unstable/session/info",
response = GetSessionInfoResponse
)]
#[serde(rename_all = "camelCase")]
pub struct GetSessionInfoRequest {
pub session_id: String,
}

#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, JsonRpcResponse)]
#[serde(rename_all = "camelCase")]
pub struct GetSessionInfoResponse {
pub session: SessionInfo,
}

/// Update the project association for a session.
#[derive(Debug, Default, Clone, Serialize, Deserialize, JsonSchema, JsonRpcRequest)]
#[request(method = "_goose/unstable/session/project/update", response = EmptyResponse)]
Expand Down
5 changes: 5 additions & 0 deletions crates/goose/acp-meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,11 @@
"requestType": "ImportSessionRequest_unstable",
"responseType": "ImportSessionResponse_unstable"
},
{
"method": "_goose/unstable/session/info",
"requestType": "GetSessionInfoRequest_unstable",
"responseType": "GetSessionInfoResponse_unstable"
},
{
"method": "_goose/unstable/elicitation/respond",
"requestType": "ElicitationRespondRequest_unstable",
Expand Down
95 changes: 95 additions & 0 deletions crates/goose/acp-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -2690,6 +2690,84 @@
"x-side": "agent",
"x-method": "_goose/unstable/session/import"
},
"GetSessionInfoRequest_unstable": {
"type": "object",
"properties": {
"sessionId": {
"type": "string"
}
},
"required": [
"sessionId"
],
"description": "Return list-style metadata for a single session without loading the conversation.",
"x-side": "agent",
"x-method": "_goose/unstable/session/info"
},
"GetSessionInfoResponse_unstable": {
"type": "object",
"properties": {
"session": {
"$ref": "#/$defs/SessionInfo"
}
},
"required": [
"session"
],
"x-side": "agent",
"x-method": "_goose/unstable/session/info"
},
"SessionInfo": {
"type": "object",
"properties": {
"sessionId": {
"$ref": "#/$defs/SessionId",
"description": "Unique identifier for the session"
},
"cwd": {
"type": "string",
"description": "The working directory for this session. Must be an absolute path."
},
"additionalDirectories": {
"type": "array",
"items": {
"type": "string"
},
"description": "**UNSTABLE**\n\nThis capability is not part of the spec yet, and may be removed or changed at any point.\n\nAuthoritative ordered additional workspace roots for this session. Each path must be absolute.\n\nWhen omitted or empty, there are no additional roots for the session."
},
"title": {
"type": [
"string",
"null"
],
"description": "Human-readable title for the session"
},
"updatedAt": {
"type": [
"string",
"null"
],
"description": "ISO 8601 timestamp of last activity"
},
"_meta": {
"type": [
"object",
"null"
],
"additionalProperties": {},
"description": "The _meta property is reserved by ACP to allow clients and agents to attach additional\nmetadata to their interactions. Implementations MUST NOT make assumptions about values at\nthese keys.\n\nSee protocol docs: [Extensibility](https://agentclientprotocol.com/protocol/extensibility)"
}
},
"required": [
"sessionId",
"cwd"
],
"description": "Information about a session returned by session/list"
},
"SessionId": {
"type": "string",
"description": "A unique identifier for a conversation session between a client and agent.\n\nSessions maintain their own context, conversation history, and state,\nallowing multiple independent interactions with the same agent.\n\nSee protocol docs: [Session ID](https://agentclientprotocol.com/protocol/session-setup#session-id)"
},
"ElicitationRespondRequest_unstable": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -4041,6 +4119,15 @@
"description": "Params for _goose/unstable/session/import",
"title": "ImportSessionRequest_unstable"
},
{
"allOf": [
{
"$ref": "#/$defs/GetSessionInfoRequest_unstable"
}
],
"description": "Params for _goose/unstable/session/info",
"title": "GetSessionInfoRequest_unstable"
},
{
"allOf": [
{
Expand Down Expand Up @@ -4476,6 +4563,14 @@
],
"title": "ImportSessionResponse_unstable"
},
{
"allOf": [
{
"$ref": "#/$defs/GetSessionInfoResponse_unstable"
}
],
"title": "GetSessionInfoResponse_unstable"
},
{
"allOf": [
{
Expand Down
62 changes: 60 additions & 2 deletions crates/goose/src/acp/response_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use crate::session::Session;
use agent_client_protocol::schema::{
AvailableCommand, AvailableCommandInput, AvailableCommandsUpdate, ModelId, ModelInfo,
SessionConfigOption, SessionConfigOptionCategory, SessionConfigSelectOption, SessionId,
SessionMode, SessionModeId, SessionModeState, SessionModelState, SessionNotification,
SessionUpdate, UnstructuredCommandInput,
SessionInfo, SessionMode, SessionModeId, SessionModeState, SessionModelState,
SessionNotification, SessionUpdate, UnstructuredCommandInput,
};
use agent_client_protocol::{Client, ConnectionTo};
use goose_providers::thinking::ThinkingEffort;
Expand All @@ -21,6 +21,64 @@ pub(super) fn session_provider_selection(session: &Session) -> &str {
.unwrap_or(DEFAULT_PROVIDER_ID)
}

pub(super) fn session_meta(session: &Session) -> serde_json::Map<String, serde_json::Value> {
let mut meta = serde_json::Map::new();
meta.insert(
"messageCount".to_string(),
serde_json::Value::Number(session.message_count.into()),
);
meta.insert(
"createdAt".to_string(),
serde_json::Value::String(session.created_at.to_rfc3339()),
);
if let Some(ref archived_at) = session.archived_at {
meta.insert(
"archivedAt".to_string(),
serde_json::Value::String(archived_at.to_rfc3339()),
);
}
meta.insert(
"userSetName".to_string(),
serde_json::Value::Bool(session.user_set_name),
);
meta.insert(
"hasRecipe".to_string(),
serde_json::Value::Bool(session.recipe.is_some()),
);

if let Some(ref pid) = session.project_id {
meta.insert(
"projectId".to_string(),
serde_json::Value::String(pid.clone()),
);
}
if let Some(ref provider) = session.provider_name {
meta.insert(
"providerId".to_string(),
serde_json::Value::String(provider.clone()),
);
}
if let Some(ref mc) = session.model_config {
meta.insert(
"modelId".to_string(),
serde_json::Value::String(mc.model_name.clone()),
);
}
meta
}

pub(super) fn build_session_info(session: Session) -> SessionInfo {
let meta = session_meta(&session);
let title = session.display_title();
let mut info = SessionInfo::new(SessionId::new(session.id), session.working_dir)
.updated_at(session.updated_at.to_rfc3339())
.meta(meta);
if let Some(title) = title {
info = info.title(title);
}
info
}

pub(super) fn build_model_state(
current_model: &str,
inventory: &ProviderInventoryEntry,
Expand Down
50 changes: 2 additions & 48 deletions crates/goose/src/acp/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use crate::acp::custom_requests::*;
use crate::acp::fs::AcpTools;
pub(super) use crate::acp::response_builder::{
build_config_options, build_mode_state, build_model_state, build_provider_options,
build_session_setup_config, send_session_setup_notifications, session_provider_selection,
should_refresh_inventory_for_session_init,
build_session_info, build_session_setup_config, send_session_setup_notifications, session_meta,
session_provider_selection, should_refresh_inventory_for_session_init,
};
use crate::acp::tools::AcpAwareToolMeta;
use crate::acp::{PermissionDecision, ACP_CURRENT_MODEL};
Expand Down Expand Up @@ -222,52 +222,6 @@ pub(super) fn sid_short(id: &str) -> String {
id.chars().take(8).collect()
}

pub(super) fn session_meta(session: &Session) -> serde_json::Map<String, serde_json::Value> {
let mut meta = serde_json::Map::new();
meta.insert(
"messageCount".to_string(),
serde_json::Value::Number(session.message_count.into()),
);
meta.insert(
"createdAt".to_string(),
serde_json::Value::String(session.created_at.to_rfc3339()),
);
if let Some(ref archived_at) = session.archived_at {
meta.insert(
"archivedAt".to_string(),
serde_json::Value::String(archived_at.to_rfc3339()),
);
}
meta.insert(
"userSetName".to_string(),
serde_json::Value::Bool(session.user_set_name),
);
meta.insert(
"hasRecipe".to_string(),
serde_json::Value::Bool(session.recipe.is_some()),
);

if let Some(ref pid) = session.project_id {
meta.insert(
"projectId".to_string(),
serde_json::Value::String(pid.clone()),
);
}
if let Some(ref provider) = session.provider_name {
meta.insert(
"providerId".to_string(),
serde_json::Value::String(provider.clone()),
);
}
if let Some(ref mc) = session.model_config {
meta.insert(
"modelId".to_string(),
serde_json::Value::String(mc.model_name.clone()),
);
}
meta
}

fn meta_string(
meta: Option<&Meta>,
key: &str,
Expand Down
8 changes: 8 additions & 0 deletions crates/goose/src/acp/server/custom_dispatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,14 @@ impl GooseAcpAgent {
self.on_import_session(req).await
}

#[custom_method(GetSessionInfoRequest)]
async fn dispatch_get_session_info(
&self,
req: GetSessionInfoRequest,
) -> Result<GetSessionInfoResponse, agent_client_protocol::Error> {
self.on_get_session_info(req).await
}

#[custom_method(ElicitationRespondRequest)]
async fn dispatch_elicitation_respond(
&self,
Expand Down
23 changes: 4 additions & 19 deletions crates/goose/src/acp/server/list_sessions.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
use super::{meta_string, session_meta, GooseAcpAgent, ResultExt};
use super::{build_session_info, meta_string, GooseAcpAgent, ResultExt};
use crate::session::session_manager::{
SessionListCursor, SessionListFilters, SessionListPageQuery, SessionType,
};
use agent_client_protocol::schema::{
ListSessionsRequest, ListSessionsResponse, Meta, SessionId, SessionInfo,
};
use agent_client_protocol::schema::{ListSessionsRequest, ListSessionsResponse, Meta, SessionInfo};
use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine as _};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
Expand Down Expand Up @@ -180,21 +178,8 @@ impl GooseAcpAgent {
})
.await
.internal_err()?;
let session_infos: Vec<SessionInfo> = page
.sessions
.into_iter()
.map(|s| {
let meta = session_meta(&s);
let title = s.display_title();
let mut info = SessionInfo::new(SessionId::new(s.id), s.working_dir)
.updated_at(s.updated_at.to_rfc3339())
.meta(meta);
if let Some(t) = title {
info = info.title(t);
}
info
})
.collect();
let session_infos: Vec<SessionInfo> =
page.sessions.into_iter().map(build_session_info).collect();
let next_cursor = page
.next_cursor
.as_ref()
Expand Down
25 changes: 25 additions & 0 deletions crates/goose/src/acp/server/manage_sessions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,31 @@ impl GooseAcpAgent {
})
}

pub(super) async fn on_get_session_info(
&self,
req: GetSessionInfoRequest,
) -> Result<GetSessionInfoResponse, agent_client_protocol::Error> {
let session_id = req.session_id.trim();
if session_id.is_empty() {
return Err(
agent_client_protocol::Error::invalid_params().data("sessionId cannot be empty")
);
}

let session = self
.session_manager
.get_session(session_id, false)
.await
.map_err(|_| {
agent_client_protocol::Error::resource_not_found(Some(session_id.to_string()))
.data(format!("Session not found: {}", session_id))
})?;

Ok(GetSessionInfoResponse {
session: build_session_info(session),
})
}

pub(super) async fn on_update_session_project(
&self,
req: UpdateSessionProjectRequest,
Expand Down
Loading
Loading