mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
Import only the continuation of backgrounded Claude sessions (#55365)
## Why Backgrounding a Claude session creates a continuation that includes its history. Offering both sessions for import duplicates that history. ## What changed Skip sessions containing a `continued-in` record with a nonblank `continuedInSessionId` during import detection, leaving the continuation available for import. ## Testing Add detection and app-server regression tests covering continuation-only detection, importing the full history into one thread, and no remaining import candidates afterward. GitOrigin-RevId: bf8383faadce984ae3c41a0b8e8b3072680fd4b2
This commit is contained in:
@@ -246,6 +246,31 @@ fn assert_history(response: &ThreadReadResponse, expected: &[(&str, &str)]) {
|
||||
assert_eq!(markers, 1, "the import marker must appear exactly once");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn backgrounded_session_imports_only_its_continuation() -> Result<()> {
|
||||
let mut fixture = ImportFixture::new().await?;
|
||||
let continuation = fixture.session_path.with_file_name("background.jsonl");
|
||||
std::fs::copy(&fixture.session_path, &continuation)?;
|
||||
fixture.append_to(&continuation, &[("assistant", LATE_ASSISTANT)])?;
|
||||
fixture.append_raw(&format!(
|
||||
"{}\n",
|
||||
json!({
|
||||
"type": "continued-in",
|
||||
"sessionId": "session",
|
||||
"continuedInSessionId": "background",
|
||||
})
|
||||
))?;
|
||||
|
||||
let imported = fixture.import_one().await?;
|
||||
assert_eq!(fixture.thread_count().await?, 1);
|
||||
assert_history(
|
||||
&fixture.read(&imported).await?,
|
||||
&[("user", FIRST_USER), ("assistant", LATE_ASSISTANT)],
|
||||
);
|
||||
assert!(fixture.detect().await?.items.is_empty());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn cold_exact_prefix_appends_suffix_to_same_task_and_checkpoints() -> Result<()> {
|
||||
let mut fixture = ImportFixture::new().await?;
|
||||
|
||||
@@ -65,6 +65,7 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::sessions::ledger::record_imported_session;
|
||||
use codex_protocol::ThreadId;
|
||||
use pretty_assertions::assert_eq;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::fs::FileTimes;
|
||||
use std::fs::OpenOptions;
|
||||
@@ -101,6 +102,45 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn detects_only_the_continuation_of_a_backgrounded_session() {
|
||||
let root = TempDir::new().expect("tempdir");
|
||||
let external_agent_home = root.path().join(".external");
|
||||
let project_root = root.path().join("repo");
|
||||
let original = record("user", "plan a vacation", &project_root);
|
||||
write_session(
|
||||
&external_agent_home,
|
||||
&project_root,
|
||||
"original.jsonl",
|
||||
&[
|
||||
original.clone(),
|
||||
serde_json::json!({
|
||||
"type": "continued-in",
|
||||
"sessionId": "original",
|
||||
"continuedInSessionId": "background",
|
||||
}),
|
||||
],
|
||||
);
|
||||
let continuation = write_session(
|
||||
&external_agent_home,
|
||||
&project_root,
|
||||
"background.jsonl",
|
||||
&[
|
||||
original,
|
||||
record("assistant", "Here is your itinerary", &project_root),
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
detect_recent_cla_sessions(&external_agent_home, root.path()).expect("detect"),
|
||||
vec![ExternalAgentSessionMigration {
|
||||
path: continuation,
|
||||
cwd: project_root,
|
||||
title: Some("plan a vacation".to_string()),
|
||||
}]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prefers_latest_custom_title_over_first_user_message() {
|
||||
let root = TempDir::new().expect("tempdir");
|
||||
|
||||
@@ -40,6 +40,15 @@ pub fn summarize_session(path: &Path) -> io::Result<Option<SessionSummary>> {
|
||||
let Ok(mut record) = serde_json::from_str::<JsonValue>(trimmed) else {
|
||||
continue;
|
||||
};
|
||||
// Backgrounding replaces this session with a continuation that includes its history.
|
||||
if record.get("type").and_then(JsonValue::as_str) == Some("continued-in")
|
||||
&& record
|
||||
.get("continuedInSessionId")
|
||||
.and_then(JsonValue::as_str)
|
||||
.is_some_and(|id| !id.trim().is_empty())
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
if cwd.is_none() {
|
||||
cwd = record
|
||||
.get("cwd")
|
||||
|
||||
Reference in New Issue
Block a user