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:
jif
2026-06-22 08:36:08 +01:00
committed by GitHub
Unverified
parent 98845e4840
commit 6d15bb3d17
38 changed files with 193 additions and 34 deletions
@@ -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
+1
View File
@@ -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,