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
@@ -328,6 +328,7 @@ fn stored_thread_from_state(
|
||||
.and_then(|metadata| metadata.rollout_path.clone())
|
||||
.or(rollout_path),
|
||||
forked_from_id: created.forked_from_id,
|
||||
parent_thread_id: created.parent_thread_id,
|
||||
preview: metadata
|
||||
.and_then(|metadata| metadata.preview.clone())
|
||||
.unwrap_or_default(),
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(super) async fn create_thread(
|
||||
RolloutRecorderParams::new(
|
||||
params.thread_id,
|
||||
params.forked_from_id,
|
||||
params.parent_thread_id,
|
||||
params.source,
|
||||
params.thread_source,
|
||||
params.base_instructions,
|
||||
|
||||
@@ -122,6 +122,7 @@ pub(super) fn stored_thread_from_rollout_item(
|
||||
thread_id,
|
||||
rollout_path: Some(item.path),
|
||||
forked_from_id: None,
|
||||
parent_thread_id: item.parent_thread_id,
|
||||
preview,
|
||||
name: None,
|
||||
model_provider: item
|
||||
|
||||
@@ -1023,6 +1023,7 @@ mod tests {
|
||||
CreateThreadParams {
|
||||
thread_id,
|
||||
forked_from_id: None,
|
||||
parent_thread_id: None,
|
||||
source: SessionSource::Exec,
|
||||
thread_source: None,
|
||||
base_instructions: BaseInstructions::default(),
|
||||
|
||||
@@ -235,6 +235,7 @@ async fn read_thread_from_rollout_path(
|
||||
})?;
|
||||
if let Ok(meta_line) = read_session_meta_line(path.as_path()).await {
|
||||
thread.forked_from_id = meta_line.meta.forked_from_id;
|
||||
thread.parent_thread_id = meta_line.meta.parent_thread_id;
|
||||
if let Some(model_provider) = meta_line
|
||||
.meta
|
||||
.model_provider
|
||||
@@ -287,6 +288,7 @@ async fn stored_thread_from_sqlite_metadata(
|
||||
.ok()
|
||||
.map(|meta_line| meta_line.meta);
|
||||
let forked_from_id = session_meta.as_ref().and_then(|meta| meta.forked_from_id);
|
||||
let parent_thread_id = session_meta.as_ref().and_then(|meta| meta.parent_thread_id);
|
||||
let preview = metadata
|
||||
.preview
|
||||
.clone()
|
||||
@@ -298,6 +300,7 @@ async fn stored_thread_from_sqlite_metadata(
|
||||
thread_id: metadata.id,
|
||||
rollout_path: Some(metadata.rollout_path),
|
||||
forked_from_id,
|
||||
parent_thread_id,
|
||||
preview,
|
||||
name,
|
||||
model_provider: if metadata.model_provider.is_empty() {
|
||||
@@ -361,6 +364,7 @@ fn stored_thread_from_meta_line(
|
||||
thread_id: meta_line.meta.id,
|
||||
rollout_path: Some(path),
|
||||
forked_from_id: meta_line.meta.forked_from_id,
|
||||
parent_thread_id: meta_line.meta.parent_thread_id,
|
||||
preview: String::new(),
|
||||
name: None,
|
||||
model_provider: meta_line
|
||||
|
||||
@@ -72,6 +72,8 @@ pub struct CreateThreadParams {
|
||||
pub thread_id: ThreadId,
|
||||
/// Source thread id when this thread is created as a fork.
|
||||
pub forked_from_id: Option<ThreadId>,
|
||||
/// The ID of the parent thread. This will only be set if this thread is a subagent.
|
||||
pub parent_thread_id: Option<ThreadId>,
|
||||
/// Runtime source for the thread.
|
||||
pub source: SessionSource,
|
||||
/// Optional analytics source classification for this thread.
|
||||
@@ -362,6 +364,8 @@ pub struct StoredThread {
|
||||
pub rollout_path: Option<PathBuf>,
|
||||
/// Source thread id when this thread was forked from another thread.
|
||||
pub forked_from_id: Option<ThreadId>,
|
||||
/// The ID of the parent thread. This will only be set if this thread is a subagent.
|
||||
pub parent_thread_id: Option<ThreadId>,
|
||||
/// Best available user-facing preview, usually the first user message.
|
||||
pub preview: String,
|
||||
/// Optional user-facing thread name/title.
|
||||
|
||||
Reference in New Issue
Block a user