mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Reject forks of paginated threads (#33109)
## What changed - Check the source thread's `history_mode` before loading its history for `thread/fork`. - Return the existing `paginated_threads is not supported yet` method-not-found error for paginated source threads. ## Testing - Add an app-server integration test covering rejection of a paginated source thread. GitOrigin-RevId: 49b4b8aef7c9488eec93c24e831cea6382f8c995
This commit is contained in:
@@ -3473,6 +3473,16 @@ impl ThreadRequestProcessor {
|
||||
"`permissions` cannot be combined with `sandbox`",
|
||||
));
|
||||
}
|
||||
let source_thread = self
|
||||
.read_stored_thread_for_resume(
|
||||
&thread_id,
|
||||
path.as_ref(),
|
||||
/*include_history*/ false,
|
||||
)
|
||||
.await?;
|
||||
if matches!(source_thread.history_mode, ThreadHistoryMode::Paginated) {
|
||||
return Err(method_not_found("paginated_threads is not supported yet"));
|
||||
}
|
||||
let mut source_thread = self
|
||||
.read_stored_thread_for_resume(&thread_id, path.as_ref(), /*include_history*/ true)
|
||||
.await?;
|
||||
|
||||
@@ -691,6 +691,68 @@ async fn thread_fork_rejects_unmaterialized_thread() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_fork_rejects_paginated_thread() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
let codex_home = TempDir::new()?;
|
||||
create_config_toml(codex_home.path(), &server.uri())?;
|
||||
|
||||
let conversation_id = create_fake_rollout(
|
||||
codex_home.path(),
|
||||
"2025-01-05T12-00-00",
|
||||
"2025-01-05T12:00:00Z",
|
||||
"Saved user message",
|
||||
Some("mock_provider"),
|
||||
/*git_info*/ None,
|
||||
)?;
|
||||
let rollout_path = codex_home
|
||||
.path()
|
||||
.join("sessions")
|
||||
.join("2025")
|
||||
.join("01")
|
||||
.join("05")
|
||||
.join(format!(
|
||||
"rollout-2025-01-05T12-00-00-{conversation_id}.jsonl"
|
||||
));
|
||||
let mut lines = std::fs::read_to_string(&rollout_path)?
|
||||
.lines()
|
||||
.map(serde_json::from_str::<Value>)
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
lines[0]["payload"]["history_mode"] = json!("paginated");
|
||||
let contents = lines
|
||||
.into_iter()
|
||||
.map(|line| line.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n");
|
||||
std::fs::write(&rollout_path, format!("{contents}\n"))?;
|
||||
|
||||
let mut mcp = TestAppServer::builder()
|
||||
.with_codex_home(codex_home.path())
|
||||
.without_auto_env()
|
||||
.build()
|
||||
.await?;
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.initialize()).await??;
|
||||
|
||||
let fork_id = mcp
|
||||
.send_thread_fork_request(ThreadForkParams {
|
||||
thread_id: conversation_id,
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
let fork_err: JSONRPCError = timeout(
|
||||
DEFAULT_READ_TIMEOUT,
|
||||
mcp.read_stream_until_error_message(RequestId::Integer(fork_id)),
|
||||
)
|
||||
.await??;
|
||||
|
||||
assert_eq!(fork_err.error.code, -32601);
|
||||
assert_eq!(
|
||||
fork_err.error.message,
|
||||
"paginated_threads is not supported yet"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn thread_fork_with_empty_path_uses_thread_id() -> Result<()> {
|
||||
let server = create_mock_responses_server_repeating_assistant("Done").await;
|
||||
|
||||
Reference in New Issue
Block a user