Skip to content

Add subagent lineage metadata for responsesapi#24161

Merged
owenlin0 merged 5 commits into
mainfrom
owen/subagent_lineage
May 29, 2026
Merged

Add subagent lineage metadata for responsesapi#24161
owenlin0 merged 5 commits into
mainfrom
owen/subagent_lineage

Conversation

@owenlin0
Copy link
Copy Markdown
Collaborator

@owenlin0 owenlin0 commented May 22, 2026

Why

We recently added forked_from_thread_id which lets us trace where a thread's context comes from, but we also want to understand subagent lineage (e.g. which parent thread spawned this subagent? what kind of subagent is it?) which is orthogonal.

This PR adds parent_thread_id and subagent_kind to the x-codex-turn-metadata header sent to ResponsesAPI.

What changed

  • Adds parent_thread_id and subagent_kind to core-owned x-codex-turn-metadata.
  • Restores persisted SessionSource and ThreadSource from resumed session metadata so cold-resumed subagent threads keep their lineage on later Responses API requests.
  • Centralizes parent-thread extraction on SessionSource / SubAgentSource and reuses it in the Responses client, analytics, agent control, and state parsing paths.
  • Extends reserved-key, git-enrichment, thread-spawn, and app-server v2 metadata coverage for the new lineage fields.

Verification

  • Not run locally per request.
  • Added focused coverage in core/src/turn_metadata_tests.rs and app-server/tests/suite/v2/client_metadata.rs.

@owenlin0 owenlin0 force-pushed the owen/subagent_lineage branch from 3e24bb9 to ade82a4 Compare May 27, 2026 19:15
@owenlin0 owenlin0 requested a review from jif-oai May 28, 2026 23:12
@owenlin0 owenlin0 marked this pull request as ready for review May 28, 2026 23:12
@owenlin0 owenlin0 requested a review from a team as a code owner May 28, 2026 23:12
@owenlin0 owenlin0 changed the title Add subagent lineage turn metadata Add subagent lineage metadata for responsesapi May 28, 2026
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 48b7b4049d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread codex-rs/protocol/src/protocol.rs
Comment thread codex-rs/core/src/turn_metadata.rs Outdated
session_id: Some(session_id),
thread_id: Some(thread_id),
forked_from_thread_id,
parent_thread_id: session_source.parent_thread_id(),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this carry the known parent for non-thread-spawn subagents too?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done! now it's populated for guardian and review subagents as well

Comment thread codex-rs/protocol/src/protocol.rs Outdated
SubAgentSource::Compact => "compact",
SubAgentSource::ThreadSpawn { .. } => "thread_spawn",
SubAgentSource::MemoryConsolidation => "memory_consolidation",
SubAgentSource::Other(_) => "other",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we preserve the Other label here instead of serializing every custom subagent as other?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good call, yes let's do that

SessionSource::SubAgent(subagent_source) => Some(subagent_source.kind()),
let (parent_thread_id, subagent_kind) = match session_source {
SessionSource::SubAgent(subagent_source) => (
subagent_source.parent_thread_id().or(forked_from_thread_id),
Copy link
Copy Markdown
Collaborator Author

@owenlin0 owenlin0 May 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jif-oai this was the fix so that we can properly set parent_id for guardian and review subagents. forked_from_thread_id happens to work but it's actually misleading, since in the case of guardian (and probably review? not as sure) we don't actually fork the parent thread's history.

I realized that this exposes a data modeling problem - we should be storing parent_thread_id directly on these subagent sources. I'll put up a followup PR to clean that up

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense for guardian/review as a short-term bridge
But what about agent_job? (that arguably we should refactor but it still represent a high volume for the security team)

That means they’ll now send subagent_kind="agent_job:$$$" but still no parent_thread_id

But I'm in to find an elegant solution that prevents tons of wiring

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jif-oai FYI - concrete evidence of the followup 😆

#25113

SessionSource::SubAgent(subagent_source) => Some(subagent_source.kind()),
let (parent_thread_id, subagent_kind) = match session_source {
SessionSource::SubAgent(subagent_source) => (
subagent_source.parent_thread_id().or(forked_from_thread_id),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense for guardian/review as a short-term bridge
But what about agent_job? (that arguably we should refactor but it still represent a high volume for the security team)

That means they’ll now send subagent_kind="agent_job:$$$" but still no parent_thread_id

But I'm in to find an elegant solution that prevents tons of wiring

@owenlin0 owenlin0 merged commit fc9cf62 into main May 29, 2026
62 checks passed
@owenlin0 owenlin0 deleted the owen/subagent_lineage branch May 29, 2026 18:28
@github-actions github-actions Bot locked and limited conversation to collaborators May 29, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants