Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions codex-rs/analytics/src/analytics_client_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ fn sample_thread_with_metadata(
id: thread_id.to_string(),
session_id: format!("session-{thread_id}"),
forked_from_id: None,
parent_thread_id: None,
preview: "first prompt".to_string(),
ephemeral,
model_provider: "openai".to_string(),
Expand Down Expand Up @@ -2456,7 +2457,7 @@ fn subagent_thread_started_thread_spawn_serializes_parent_thread_id() {
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-spawn".to_string(),
parent_thread_id: None,
parent_thread_id: Some(parent_thread_id.to_string()),
product_client_id: "codex-tui".to_string(),
client_name: "codex-tui".to_string(),
client_version: "1.0.0".to_string(),
Expand Down Expand Up @@ -2534,11 +2535,14 @@ fn subagent_thread_started_other_serializes_expected_shape() {

#[test]
fn subagent_thread_started_other_serializes_explicit_parent_thread_id() {
let parent_thread_id =
codex_protocol::ThreadId::from_string("33333333-3333-4333-8333-333333333333")
.expect("valid thread id");
let event = TrackEventRequest::ThreadInitialized(subagent_thread_started_event_request(
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-guardian".to_string(),
parent_thread_id: Some("parent-thread-guardian".to_string()),
parent_thread_id: Some(parent_thread_id.to_string()),
product_client_id: "codex-tui".to_string(),
client_name: "codex-tui".to_string(),
client_version: "1.0.0".to_string(),
Expand All @@ -2553,7 +2557,7 @@ fn subagent_thread_started_other_serializes_explicit_parent_thread_id() {
assert_eq!(payload["event_params"]["subagent_source"], "guardian");
assert_eq!(
payload["event_params"]["parent_thread_id"],
"parent-thread-guardian"
"33333333-3333-4333-8333-333333333333"
);
}

Expand Down Expand Up @@ -2642,7 +2646,7 @@ async fn subagent_thread_started_inherits_parent_connection_for_new_thread() {
SubAgentThreadStartedInput {
session_id: "session-root".to_string(),
thread_id: "thread-review".to_string(),
parent_thread_id: None,
parent_thread_id: Some(parent_thread_id.to_string()),
product_client_id: "parent-client".to_string(),
client_name: "parent-client".to_string(),
client_version: "1.0.0".to_string(),
Expand Down
1 change: 1 addition & 0 deletions codex-rs/analytics/src/client_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ fn sample_thread(thread_id: &str) -> Thread {
id: thread_id.to_string(),
session_id: format!("session-{thread_id}"),
forked_from_id: None,
parent_thread_id: None,
preview: "first prompt".to_string(),
ephemeral: false,
model_provider: "openai".to_string(),
Expand Down
18 changes: 2 additions & 16 deletions codex-rs/analytics/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1047,9 +1047,7 @@ pub(crate) fn subagent_thread_started_event_request(
thread_source: Some(ThreadSource::Subagent),
initialization_mode: ThreadInitializationMode::New,
subagent_source: Some(subagent_source_name(&input.subagent_source)),
parent_thread_id: input
.parent_thread_id
.or_else(|| subagent_parent_thread_id(&input.subagent_source)),
parent_thread_id: input.parent_thread_id,
created_at: input.created_at,
};
ThreadInitializedEvent {
Expand All @@ -1059,19 +1057,7 @@ pub(crate) fn subagent_thread_started_event_request(
}

pub(crate) fn subagent_source_name(subagent_source: &SubAgentSource) -> String {
match subagent_source {
SubAgentSource::Review => "review".to_string(),
SubAgentSource::Compact => "compact".to_string(),
SubAgentSource::ThreadSpawn { .. } => "thread_spawn".to_string(),
SubAgentSource::MemoryConsolidation => "memory_consolidation".to_string(),
SubAgentSource::Other(other) => other.clone(),
}
}

pub(crate) fn subagent_parent_thread_id(subagent_source: &SubAgentSource) -> Option<String> {
subagent_source
.parent_thread_id()
.map(|parent_thread_id| parent_thread_id.to_string())
subagent_source.kind().to_string()
}

fn analytics_hook_status(status: HookRunStatus) -> HookRunStatus {
Expand Down
18 changes: 7 additions & 11 deletions codex-rs/analytics/src/reducer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ use crate::events::codex_hook_run_metadata;
use crate::events::codex_plugin_metadata;
use crate::events::codex_plugin_used_metadata;
use crate::events::plugin_state_event_type;
use crate::events::subagent_parent_thread_id;
use crate::events::subagent_source_name;
use crate::events::subagent_thread_started_event_request;
use crate::facts::AnalyticsFact;
Expand Down Expand Up @@ -267,20 +266,18 @@ impl ThreadMetadataState {
session_id: String,
session_source: &SessionSource,
thread_source: Option<ThreadSource>,
parent_thread_id: Option<String>,
initialization_mode: ThreadInitializationMode,
) -> Self {
let (subagent_source, parent_thread_id) = match session_source {
SessionSource::SubAgent(subagent_source) => (
Some(subagent_source_name(subagent_source)),
subagent_parent_thread_id(subagent_source),
),
let subagent_source = match session_source {
SessionSource::SubAgent(subagent_source) => Some(subagent_source_name(subagent_source)),
SessionSource::Cli
| SessionSource::VSCode
| SessionSource::Exec
| SessionSource::Mcp
| SessionSource::Custom(_)
| SessionSource::Internal(_)
| SessionSource::Unknown => (None, None),
| SessionSource::Unknown => None,
};
Self {
session_id,
Expand Down Expand Up @@ -516,10 +513,7 @@ impl AnalyticsReducer {
input: SubAgentThreadStartedInput,
out: &mut Vec<TrackEventRequest>,
) {
let parent_thread_id = input
.parent_thread_id
.clone()
.or_else(|| subagent_parent_thread_id(&input.subagent_source));
let parent_thread_id = input.parent_thread_id.clone();
let parent_connection_id = parent_thread_id
.as_ref()
.and_then(|parent_thread_id| self.threads.get(parent_thread_id))
Expand Down Expand Up @@ -1238,13 +1232,15 @@ impl AnalyticsReducer {
let session_source: SessionSource = thread.source.into();
let session_id = thread.session_id;
let thread_id = thread.id;
let parent_thread_id = thread.parent_thread_id;
let Some(connection_state) = self.connections.get(&connection_id) else {
return;
};
let thread_metadata = ThreadMetadataState::from_thread_metadata(
session_id.clone(),
&session_source,
thread.thread_source.map(Into::into),
parent_thread_id,
initialization_mode,
);
self.threads.insert(
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions codex-rs/app-server-protocol/schema/typescript/v2/Thread.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2328,6 +2328,7 @@ mod tests {
id: "67e55044-10b1-426f-9247-bb680e5fe0c8".to_string(),
session_id: "67e55044-10b1-426f-9247-bb680e5fe0c7".to_string(),
forked_from_id: None,
parent_thread_id: None,
preview: "first prompt".to_string(),
ephemeral: true,
model_provider: "openai".to_string(),
Expand Down Expand Up @@ -2370,6 +2371,7 @@ mod tests {
"id": "67e55044-10b1-426f-9247-bb680e5fe0c8",
"sessionId": "67e55044-10b1-426f-9247-bb680e5fe0c7",
"forkedFromId": null,
"parentThreadId": null,
"preview": "first prompt",
"ephemeral": true,
"modelProvider": "openai",
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ fn thread_resume_response_round_trips_initial_turns_page() {
id: "thr_123".to_string(),
session_id: "thr_123".to_string(),
forked_from_id: None,
parent_thread_id: None,
preview: String::new(),
ephemeral: false,
model_provider: "openai".to_string(),
Expand Down Expand Up @@ -3581,6 +3582,7 @@ fn thread_lifecycle_responses_default_missing_optional_fields() {
let fork: ThreadForkResponse = serde_json::from_value(response).expect("thread/fork response");

assert_eq!(start.instruction_sources, Vec::<AbsolutePathBuf>::new());
assert_eq!(start.thread.parent_thread_id, None);
assert_eq!(resume.instruction_sources, Vec::<AbsolutePathBuf>::new());
assert_eq!(fork.instruction_sources, Vec::<AbsolutePathBuf>::new());
assert_eq!(start.active_permission_profile, None);
Expand Down
2 changes: 2 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2/thread_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ pub struct Thread {
pub session_id: String,
/// Source thread id when this thread was created by forking another thread.
pub forked_from_id: Option<String>,
/// The ID of the parent thread. This will only be set if this thread is a subagent.
pub parent_thread_id: Option<String>,
/// Usually the first user message in the thread, if available.
pub preview: String,
/// Whether the thread is ephemeral and should not be materialized on disk.
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server/src/bespoke_event_handling.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2170,6 +2170,7 @@ mod tests {
thread_id,
rollout_path: None,
forked_from_id: None,
parent_thread_id: None,
preview: "fallback preview".to_string(),
name: Some("Rollback thread".to_string()),
model_provider: "openai".to_string(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4012,6 +4012,7 @@ pub(crate) fn thread_from_stored_thread(
id: thread_id.clone(),
session_id: thread_id,
forked_from_id: thread.forked_from_id.map(|id| id.to_string()),
parent_thread_id: thread.parent_thread_id.map(|id| id.to_string()),
preview: thread.preview,
ephemeral: false,
model_provider: if thread.model_provider.is_empty() {
Expand Down Expand Up @@ -4220,6 +4221,7 @@ fn build_thread_from_snapshot(
id: thread_id.to_string(),
session_id,
forked_from_id: None,
parent_thread_id: config_snapshot.parent_thread_id.map(|id| id.to_string()),
preview: String::new(),
ephemeral: config_snapshot.ephemeral,
model_provider: config_snapshot.model_provider_id.clone(),
Expand Down
Loading
Loading