mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Persist session IDs across thread resume (#29327)
## Summary
A cold-resumed subagent kept its durable thread ID but could receive a
new session ID, splitting one agent tree across multiple sessions after
a restart.
Persist the root session ID in every rollout `SessionMeta`, carry it
through thread creation, and restore it before initializing the resumed
`Session` and `AgentControl`.
## Behavior
For a nested agent tree:
```text
root session R
parent thread P
child thread C
```
The child rollout stores:
```text
session_id: R
parent_thread_id: P
id: C
```
After a cold resume, the child still belongs to root session `R` while
its immediate parent remains `P`. The integration coverage uses distinct
values for all three IDs so it catches restoring the session from
`parent_thread_id`.
## Legacy rollouts
Previous rollouts have `id` but no `session_id`. `SessionMetaLine`
deserialization treats a missing `session_id` as `id`, keeping those
files readable, listable, and resumable. When a legacy subagent is
resumed through its root, that synthesized child ID no longer overrides
the inherited root-scoped `AgentControl`. New rollouts always persist
the explicit root session ID.
This commit is contained in:
@@ -200,6 +200,7 @@ impl ExternalAgentSessionImporter {
|
||||
};
|
||||
let now = Utc::now();
|
||||
let create_params = CreateThreadParams {
|
||||
session_id: thread_id.into(),
|
||||
thread_id,
|
||||
extra_config: None,
|
||||
forked_from_id: None,
|
||||
|
||||
@@ -1004,6 +1004,7 @@ mod thread_processor_behavior_tests {
|
||||
let timestamp = "2025-09-05T16:53:11.850Z".to_string();
|
||||
|
||||
let session_meta = SessionMeta {
|
||||
session_id: conversation_id.into(),
|
||||
id: conversation_id,
|
||||
timestamp: timestamp.clone(),
|
||||
model_provider: None,
|
||||
@@ -1060,6 +1061,7 @@ mod thread_processor_behavior_tests {
|
||||
let timestamp = "2025-09-05T16:53:11.850Z".to_string();
|
||||
|
||||
let session_meta = SessionMeta {
|
||||
session_id: parent_thread_id.into(),
|
||||
id: conversation_id,
|
||||
timestamp: timestamp.clone(),
|
||||
source: SessionSource::SubAgent(SubAgentSource::ThreadSpawn {
|
||||
@@ -1110,6 +1112,7 @@ mod thread_processor_behavior_tests {
|
||||
let timestamp = "2025-09-05T16:53:11.850Z".to_string();
|
||||
|
||||
let session_meta = SessionMeta {
|
||||
session_id: conversation_id.into(),
|
||||
id: conversation_id,
|
||||
forked_from_id: Some(forked_from_id),
|
||||
timestamp: timestamp.clone(),
|
||||
|
||||
@@ -13,6 +13,7 @@ fn extract_conversation_summary_prefers_plain_user_messages() -> Result<()> {
|
||||
|
||||
let head = vec![
|
||||
json!({
|
||||
"session_id": conversation_id.to_string(),
|
||||
"id": conversation_id.to_string(),
|
||||
"timestamp": timestamp,
|
||||
"cwd": "/",
|
||||
|
||||
Reference in New Issue
Block a user