mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
store and expose parent_thread_id on Threads (#25113)
## Why This PR https://github.com/openai/codex/pull/24161#discussion_r3325692763 revealed a subagent data modeling issue, where we overloaded `forked_from_id` to also mean `parent_thread_id`. That's incorrect since guardian and review subagents can be a subagent and NOT fork the main thread's history. The solution here is to explicitly store a new `parent_thread_id` on `SessionMeta`, alongside `forked_from_id` which already exists. While we're at it, also expose it in the app-server protocol on the `Thread` object. A thread->subagent relationship and a fork of thread history are orthogonal concepts. ## What Changed - Added top-level `parent_thread_id` persistence on `SessionMeta` and runtime/session plumbing through `SessionConfiguredEvent`, `CodexSpawnArgs`, `SessionConfiguration`, `ThreadConfigSnapshot`, `TurnContext`, and `ModelClient`. - Made turn metadata, request headers, analytics, and subagent-start events read the separate runtime/top-level parent field instead of deriving general parent lineage from `SessionSource` or `forked_from_thread_id`. - Passed parent lineage separately at delegated subagent, review, guardian, agent-job, and multi-agent spawn construction sites; copied-history fork lineage remains derived only from `InitialHistory`. - Persisted and exposed parent lineage through rollout/thread-store projections and app-server v2 `Thread.parentThreadId`. - Updated app-server README text and regenerated app-server schema fixtures for the additive `parentThreadId` response field.
This commit is contained in:
committed by
GitHub
Unverified
parent
3b7334d099
commit
cf0911076f
@@ -105,6 +105,7 @@ async fn responses_stream_includes_subagent_header_on_review() {
|
||||
/*installation_id*/ TEST_INSTALLATION_ID.to_string(),
|
||||
provider.clone(),
|
||||
session_source,
|
||||
/*parent_thread_id*/ None,
|
||||
config.model_verbosity,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
@@ -233,6 +234,7 @@ async fn responses_stream_includes_subagent_header_on_other() {
|
||||
/*installation_id*/ TEST_INSTALLATION_ID.to_string(),
|
||||
provider.clone(),
|
||||
session_source,
|
||||
/*parent_thread_id*/ None,
|
||||
config.model_verbosity,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
@@ -350,6 +352,7 @@ async fn responses_respects_model_info_overrides_from_config() {
|
||||
/*installation_id*/ TEST_INSTALLATION_ID.to_string(),
|
||||
provider.clone(),
|
||||
session_source,
|
||||
/*parent_thread_id*/ None,
|
||||
config.model_verbosity,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
|
||||
@@ -488,6 +488,7 @@ async fn resume_replays_legacy_js_repl_image_rollout_shapes() {
|
||||
item: RolloutItem::SessionMeta(SessionMetaLine {
|
||||
meta: SessionMeta {
|
||||
id: ThreadId::default(),
|
||||
parent_thread_id: None,
|
||||
timestamp: "2024-01-01T00:00:00Z".to_string(),
|
||||
cwd: ".".into(),
|
||||
originator: "test_originator".to_string(),
|
||||
@@ -618,6 +619,7 @@ async fn resume_replays_image_tool_outputs_with_detail() {
|
||||
item: RolloutItem::SessionMeta(SessionMetaLine {
|
||||
meta: SessionMeta {
|
||||
id: ThreadId::default(),
|
||||
parent_thread_id: None,
|
||||
timestamp: "2024-01-01T00:00:00Z".to_string(),
|
||||
cwd: ".".into(),
|
||||
originator: "test_originator".to_string(),
|
||||
@@ -902,6 +904,7 @@ async fn send_provider_auth_request(server: &MockServer, auth: ModelProviderAuth
|
||||
/*installation_id*/ "11111111-1111-4111-8111-111111111111".to_string(),
|
||||
provider,
|
||||
SessionSource::Exec,
|
||||
/*parent_thread_id*/ None,
|
||||
config.model_verbosity,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
@@ -2358,6 +2361,7 @@ async fn azure_responses_request_includes_store_and_reasoning_ids() {
|
||||
/*installation_id*/ "11111111-1111-4111-8111-111111111111".to_string(),
|
||||
provider.clone(),
|
||||
SessionSource::Exec,
|
||||
/*parent_thread_id*/ None,
|
||||
config.model_verbosity,
|
||||
/*enable_request_compression*/ false,
|
||||
/*include_timing_metrics*/ false,
|
||||
|
||||
@@ -2155,6 +2155,7 @@ async fn websocket_harness_with_provider_options(
|
||||
/*installation_id*/ TEST_INSTALLATION_ID.to_string(),
|
||||
provider.clone(),
|
||||
SessionSource::Exec,
|
||||
/*parent_thread_id*/ None,
|
||||
config.model_verbosity,
|
||||
/*enable_request_compression*/ false,
|
||||
runtime_metrics_enabled,
|
||||
|
||||
@@ -61,6 +61,7 @@ async fn write_rollout_with_user_event(dir: &Path, thread_id: ThreadId) -> io::R
|
||||
meta: SessionMeta {
|
||||
id: thread_id,
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
timestamp: TEST_TIMESTAMP.to_string(),
|
||||
cwd: std::path::PathBuf::from("."),
|
||||
originator: "test_originator".to_string(),
|
||||
@@ -109,6 +110,7 @@ async fn write_rollout_with_meta_only(dir: &Path, thread_id: ThreadId) -> io::Re
|
||||
meta: SessionMeta {
|
||||
id: thread_id,
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
timestamp: TEST_TIMESTAMP.to_string(),
|
||||
cwd: std::path::PathBuf::from("."),
|
||||
originator: "test_originator".to_string(),
|
||||
|
||||
@@ -130,8 +130,9 @@ async fn responses_api_parent_and_subagent_requests_include_identity_headers() -
|
||||
.header("x-codex-turn-metadata")
|
||||
.ok_or_else(|| anyhow!("child request missing x-codex-turn-metadata"))?,
|
||||
)?;
|
||||
assert!(child_turn_metadata.get("forked_from_thread_id").is_none());
|
||||
assert_eq!(
|
||||
child_turn_metadata["forked_from_thread_id"].as_str(),
|
||||
child_turn_metadata["parent_thread_id"].as_str(),
|
||||
Some(parent_thread_id)
|
||||
);
|
||||
|
||||
|
||||
@@ -136,8 +136,9 @@ async fn review_op_emits_lifecycle_and_review_output() {
|
||||
.expect("review request turn metadata"),
|
||||
)
|
||||
.expect("review request turn metadata json");
|
||||
assert!(turn_metadata.get("forked_from_thread_id").is_none());
|
||||
assert_eq!(
|
||||
turn_metadata["forked_from_thread_id"].as_str(),
|
||||
turn_metadata["parent_thread_id"].as_str(),
|
||||
Some(parent_thread_id.as_str())
|
||||
);
|
||||
|
||||
|
||||
@@ -184,6 +184,7 @@ async fn find_locates_rollout_file_written_by_recorder() -> std::io::Result<()>
|
||||
RolloutRecorderParams::new(
|
||||
thread_id,
|
||||
/*forked_from_id*/ None,
|
||||
/*parent_thread_id*/ None,
|
||||
SessionSource::Exec,
|
||||
/*thread_source*/ None,
|
||||
BaseInstructions::default(),
|
||||
|
||||
@@ -212,6 +212,7 @@ async fn backfill_scans_existing_rollouts() -> Result<()> {
|
||||
meta: SessionMeta {
|
||||
id: thread_id,
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
timestamp: "2026-01-27T12:00:00Z".to_string(),
|
||||
cwd: codex_home.to_path_buf(),
|
||||
originator: "test".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user