Preserve parent cache affinity for ephemeral forks (#44862)

## Why

ChatGPT derives Responses cache affinity from the `session-id` header. Ephemeral forks need to reuse their parent's cache routing while retaining their own session and thread identities.

## What changed

- Inherit the parent session ID as the `prompt_cache_key` for ephemeral root forks.
- Use the prompt cache key for root-agent Responses `session-id` headers, including WebSocket handshakes. Preserve the actual session identity in turn metadata and leave non-root-agent routing unchanged.
- Keep enabled goal tool definitions visible on ephemeral threads, but reject execution with `Goal tools require a persistent thread.` and disable automatic goal continuation without persistent state.

## Testing

Add regression coverage for inherited cache routing with distinct fork identities, matching parent and fork tool definitions, WebSocket session headers, and rejection of ephemeral goal tool execution.

GitOrigin-RevId: d235e97630068b27f7ebd562dd23ac6266ebc1b9
This commit is contained in:
jif authored and copyberry committed 2026-09-11 16:41:56 +00:00
1 parent 0818b6550b
commit bc5957eac9
8 files changed
+167 -22

No files matched your search

+18 -1
View File
@@ -799,6 +799,22 @@ impl Session {
));
}
};
// Ephemeral forks reuse cache routing, without sharing storage or lifecycle identity.
let fork_cache_key = match &initial_history {
InitialHistory::Forked(items)
if config.ephemeral
&& !session_configuration.session_source.is_non_root_agent() =>
{
items.iter().find_map(|item| match item {
RolloutItem::SessionMeta(meta) => Some(meta.meta.session_id.to_string()),
_ => None,
})
}
InitialHistory::New
| InitialHistory::Cleared
| InitialHistory::Resumed(_)
| InitialHistory::Forked(_) => None,
};
let resumed_session_id = match &initial_history {
InitialHistory::Resumed(resumed) => {
resumed.history.iter().find_map(|item| match item {
@@ -1580,7 +1596,8 @@ impl Session {
crate::guardian::prompt_cache_key_override_for_review_session(
&session_configuration.session_source,
session_configuration.parent_thread_id,
),
)
.or(fork_cache_key),
tx_event.clone(),
),
executed_tool_calls: executed_tool_calls.clone(),