[App Server] Add more session metadata to listConversations (#6337)

This unlocks a few new product experience for app server consumers
This commit is contained in:
Gabriel Peal
2025-11-06 14:13:24 -08:00
committed by GitHub
parent 8501b0b768
commit 1b8cc8b625
2 changed files with 81 additions and 16 deletions

View File

@@ -11,6 +11,7 @@ use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::AskForApproval; use codex_protocol::protocol::AskForApproval;
use codex_protocol::protocol::EventMsg; use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::SandboxPolicy; use codex_protocol::protocol::SandboxPolicy;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::TurnAbortReason; use codex_protocol::protocol::TurnAbortReason;
use schemars::JsonSchema; use schemars::JsonSchema;
use serde::Deserialize; use serde::Deserialize;
@@ -113,6 +114,18 @@ pub struct ConversationSummary {
pub preview: String, pub preview: String,
pub timestamp: Option<String>, pub timestamp: Option<String>,
pub model_provider: String, pub model_provider: String,
pub cwd: PathBuf,
pub cli_version: String,
pub source: SessionSource,
pub git_info: Option<ConversationGitInfo>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
#[serde(rename_all = "snake_case")]
pub struct ConversationGitInfo {
pub sha: Option<String>,
pub branch: Option<String>,
pub origin_url: Option<String>,
} }
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]

View File

@@ -20,6 +20,7 @@ use codex_app_server_protocol::CancelLoginAccountParams;
use codex_app_server_protocol::CancelLoginAccountResponse; use codex_app_server_protocol::CancelLoginAccountResponse;
use codex_app_server_protocol::CancelLoginChatGptResponse; use codex_app_server_protocol::CancelLoginChatGptResponse;
use codex_app_server_protocol::ClientRequest; use codex_app_server_protocol::ClientRequest;
use codex_app_server_protocol::ConversationGitInfo;
use codex_app_server_protocol::ConversationSummary; use codex_app_server_protocol::ConversationSummary;
use codex_app_server_protocol::ExecCommandApprovalParams; use codex_app_server_protocol::ExecCommandApprovalParams;
use codex_app_server_protocol::ExecCommandApprovalResponse; use codex_app_server_protocol::ExecCommandApprovalResponse;
@@ -130,8 +131,10 @@ use codex_protocol::ConversationId;
use codex_protocol::config_types::ForcedLoginMethod; use codex_protocol::config_types::ForcedLoginMethod;
use codex_protocol::items::TurnItem; use codex_protocol::items::TurnItem;
use codex_protocol::models::ResponseItem; use codex_protocol::models::ResponseItem;
use codex_protocol::protocol::GitInfo;
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot; use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
use codex_protocol::protocol::RolloutItem; use codex_protocol::protocol::RolloutItem;
use codex_protocol::protocol::SessionMetaLine;
use codex_protocol::protocol::USER_MESSAGE_BEGIN; use codex_protocol::protocol::USER_MESSAGE_BEGIN;
use codex_protocol::user_input::UserInput as CoreInputItem; use codex_protocol::user_input::UserInput as CoreInputItem;
use codex_utils_json_to_toml::json_to_toml; use codex_utils_json_to_toml::json_to_toml;
@@ -1510,7 +1513,18 @@ impl CodexMessageProcessor {
let items = page let items = page
.items .items
.into_iter() .into_iter()
.filter_map(|it| extract_conversation_summary(it.path, &it.head, &fallback_provider)) .filter_map(|it| {
let session_meta_line = it.head.first().and_then(|first| {
serde_json::from_value::<SessionMetaLine>(first.clone()).ok()
})?;
extract_conversation_summary(
it.path,
&it.head,
&session_meta_line.meta,
session_meta_line.git.as_ref(),
fallback_provider.as_str(),
)
})
.collect::<Vec<_>>(); .collect::<Vec<_>>();
// Encode next_cursor as a plain string // Encode next_cursor as a plain string
@@ -2671,16 +2685,25 @@ async fn read_summary_from_rollout(
))); )));
}; };
let session_meta = serde_json::from_value::<SessionMeta>(first.clone()).map_err(|_| { let session_meta_line =
IoError::other(format!( serde_json::from_value::<SessionMetaLine>(first.clone()).map_err(|_| {
"rollout at {} does not start with session metadata", IoError::other(format!(
path.display() "rollout at {} does not start with session metadata",
)) path.display()
})?; ))
})?;
let SessionMetaLine {
meta: session_meta,
git,
} = session_meta_line;
if let Some(summary) = if let Some(summary) = extract_conversation_summary(
extract_conversation_summary(path.to_path_buf(), &head, fallback_provider) path.to_path_buf(),
{ &head,
&session_meta,
git.as_ref(),
fallback_provider,
) {
return Ok(summary); return Ok(summary);
} }
@@ -2691,7 +2714,9 @@ async fn read_summary_from_rollout(
}; };
let model_provider = session_meta let model_provider = session_meta
.model_provider .model_provider
.clone()
.unwrap_or_else(|| fallback_provider.to_string()); .unwrap_or_else(|| fallback_provider.to_string());
let git_info = git.as_ref().map(map_git_info);
Ok(ConversationSummary { Ok(ConversationSummary {
conversation_id: session_meta.id, conversation_id: session_meta.id,
@@ -2699,19 +2724,20 @@ async fn read_summary_from_rollout(
path: path.to_path_buf(), path: path.to_path_buf(),
preview: String::new(), preview: String::new(),
model_provider, model_provider,
cwd: session_meta.cwd,
cli_version: session_meta.cli_version,
source: session_meta.source,
git_info,
}) })
} }
fn extract_conversation_summary( fn extract_conversation_summary(
path: PathBuf, path: PathBuf,
head: &[serde_json::Value], head: &[serde_json::Value],
session_meta: &SessionMeta,
git: Option<&GitInfo>,
fallback_provider: &str, fallback_provider: &str,
) -> Option<ConversationSummary> { ) -> Option<ConversationSummary> {
let session_meta = match head.first() {
Some(first_line) => serde_json::from_value::<SessionMeta>(first_line.clone()).ok()?,
None => return None,
};
let preview = head let preview = head
.iter() .iter()
.filter_map(|value| serde_json::from_value::<ResponseItem>(value.clone()).ok()) .filter_map(|value| serde_json::from_value::<ResponseItem>(value.clone()).ok())
@@ -2733,7 +2759,9 @@ fn extract_conversation_summary(
let conversation_id = session_meta.id; let conversation_id = session_meta.id;
let model_provider = session_meta let model_provider = session_meta
.model_provider .model_provider
.clone()
.unwrap_or_else(|| fallback_provider.to_string()); .unwrap_or_else(|| fallback_provider.to_string());
let git_info = git.map(map_git_info);
Some(ConversationSummary { Some(ConversationSummary {
conversation_id, conversation_id,
@@ -2741,13 +2769,26 @@ fn extract_conversation_summary(
path, path,
preview: preview.to_string(), preview: preview.to_string(),
model_provider, model_provider,
cwd: session_meta.cwd.clone(),
cli_version: session_meta.cli_version.clone(),
source: session_meta.source.clone(),
git_info,
}) })
} }
fn map_git_info(git_info: &GitInfo) -> ConversationGitInfo {
ConversationGitInfo {
sha: git_info.commit_hash.clone(),
branch: git_info.branch.clone(),
origin_url: git_info.repository_url.clone(),
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use anyhow::Result; use anyhow::Result;
use codex_protocol::protocol::SessionSource;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use serde_json::json; use serde_json::json;
use tempfile::TempDir; use tempfile::TempDir;
@@ -2786,8 +2827,11 @@ mod tests {
}), }),
]; ];
let session_meta = serde_json::from_value::<SessionMeta>(head[0].clone())?;
let summary = let summary =
extract_conversation_summary(path.clone(), &head, "test-provider").expect("summary"); extract_conversation_summary(path.clone(), &head, &session_meta, None, "test-provider")
.expect("summary");
let expected = ConversationSummary { let expected = ConversationSummary {
conversation_id, conversation_id,
@@ -2795,6 +2839,10 @@ mod tests {
path, path,
preview: "Count to 5".to_string(), preview: "Count to 5".to_string(),
model_provider: "test-provider".to_string(), model_provider: "test-provider".to_string(),
cwd: PathBuf::from("/"),
cli_version: "0.0.0".to_string(),
source: SessionSource::VSCode,
git_info: None,
}; };
assert_eq!(summary, expected); assert_eq!(summary, expected);
@@ -2839,6 +2887,10 @@ mod tests {
path: path.clone(), path: path.clone(),
preview: String::new(), preview: String::new(),
model_provider: "fallback".to_string(), model_provider: "fallback".to_string(),
cwd: PathBuf::new(),
cli_version: String::new(),
source: SessionSource::VSCode,
git_info: None,
}; };
assert_eq!(summary, expected); assert_eq!(summary, expected);