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:
@@ -110,6 +110,7 @@ mod tests {
|
||||
] {
|
||||
store
|
||||
.create_thread(CreateThreadParams {
|
||||
session_id: thread_id.into(),
|
||||
thread_id,
|
||||
extra_config: None,
|
||||
forked_from_id: None,
|
||||
@@ -231,6 +232,7 @@ impl InMemoryThreadStore {
|
||||
let mut state = self.state.lock().await;
|
||||
state.calls.create_thread += 1;
|
||||
let session_meta = SessionMeta {
|
||||
session_id: params.session_id,
|
||||
id: params.thread_id,
|
||||
forked_from_id: params.forked_from_id,
|
||||
parent_thread_id: params.parent_thread_id,
|
||||
|
||||
@@ -36,6 +36,7 @@ pub(super) async fn create_thread(
|
||||
params.base_instructions,
|
||||
params.dynamic_tools,
|
||||
)
|
||||
.with_session_id(params.session_id)
|
||||
.with_multi_agent_version(params.multi_agent_version),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -1123,6 +1123,7 @@ mod tests {
|
||||
|
||||
fn create_thread_params(thread_id: ThreadId) -> CreateThreadParams {
|
||||
CreateThreadParams {
|
||||
session_id: thread_id.into(),
|
||||
thread_id,
|
||||
extra_config: None,
|
||||
forked_from_id: None,
|
||||
|
||||
@@ -821,6 +821,7 @@ mod tests {
|
||||
"timestamp": "2025-01-03T12:00:00Z",
|
||||
"type": "session_meta",
|
||||
"payload": {
|
||||
"session_id": uuid,
|
||||
"id": uuid,
|
||||
"timestamp": "2025-01-03T12:00:00Z",
|
||||
"cwd": rollout_cwd,
|
||||
@@ -926,6 +927,7 @@ mod tests {
|
||||
"timestamp": "2025-01-03T12-00-00",
|
||||
"type": "session_meta",
|
||||
"payload": {
|
||||
"session_id": uuid,
|
||||
"id": uuid,
|
||||
"timestamp": "2025-01-03T12-00-00",
|
||||
"cwd": home.path(),
|
||||
@@ -1091,6 +1093,7 @@ mod tests {
|
||||
"timestamp": "2025-01-03T12:00:00Z",
|
||||
"type": "session_meta",
|
||||
"payload": {
|
||||
"session_id": uuid,
|
||||
"id": uuid,
|
||||
"timestamp": "2025-01-03T12:00:00Z",
|
||||
"cwd": home.path(),
|
||||
|
||||
@@ -77,6 +77,7 @@ pub(super) fn write_session_file_with_fork(
|
||||
"timestamp": ts,
|
||||
"type": "session_meta",
|
||||
"payload": {
|
||||
"session_id": uuid,
|
||||
"id": uuid,
|
||||
"forked_from_id": forked_from_id,
|
||||
"timestamp": ts,
|
||||
|
||||
@@ -579,6 +579,7 @@ mod tests {
|
||||
fn session_meta(thread_id: ThreadId) -> SessionMetaLine {
|
||||
SessionMetaLine {
|
||||
meta: SessionMeta {
|
||||
session_id: thread_id.into(),
|
||||
id: thread_id,
|
||||
timestamp: "2025-01-03T12:00:00Z".to_string(),
|
||||
source: SessionSource::Exec,
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::path::PathBuf;
|
||||
|
||||
use chrono::DateTime;
|
||||
use chrono::Utc;
|
||||
use codex_protocol::SessionId;
|
||||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::dynamic_tools::DynamicToolSpec;
|
||||
use codex_protocol::models::BaseInstructions;
|
||||
@@ -63,6 +64,8 @@ pub struct ExtraConfig {}
|
||||
/// Parameters required to create a persisted thread.
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct CreateThreadParams {
|
||||
/// Session id shared by the root thread and all of its subagents.
|
||||
pub session_id: SessionId,
|
||||
/// Thread id generated by Codex before opening persistence.
|
||||
pub thread_id: ThreadId,
|
||||
/// Optional extra configuration fields for the thread.
|
||||
|
||||
Reference in New Issue
Block a user