You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Bug]: Session import turns every Codex subagent into its own top-level thread #16222
I searched existing issues and did not find a duplicate.
I included enough detail to reproduce or investigate the problem.
Area
apps/server
Steps to reproduce
In a project, run Codex threads that spawn subagents (spawn_agent). Running them through T3 Code is enough.
On another T3 Code server (or a fresh base dir) that shares the same ~/.codex, add the same project and import its recent sessions.
Expected behavior
Only the conversations the user had are imported. Subagent sessions are skipped, or attached to their parent thread the way Claude subagents already are.
Actual behavior
Every Codex subagent session becomes its own top-level import:codex:<id> thread. Codex stores each subagent as a separate rollout, and the scanner treats every rollout as a conversation. The titles come from injected setup text, not a user prompt, so the sidebar fills with threads named # AGENTS.md instructions, <recommended_plugins> or <thread_title_updates>…. On one server this produced 119 subagent threads next to 53 real ones.
Codex records the parent in the first line of each child rollout:
In apps/server/src/project/AgentSessionScanner.ts (upstream main @ 9e5229b), the session_meta branch reads only payload.id and ignores payload.source. Top-level sessions have a string source ("cli", "exec", "vscode", …), so checking source.subagent.thread_spawn there would be enough to skip spawned sessions or link them to parent_thread_id.
Impact
Minor bug or occasional failure
Version or commit
Seen on a nightly build (0.0.46-nightly.20261004); the scanner code is unchanged on main @ 9e5229b.
Environment
Linux, Codex CLI 0.160.0, two T3 Code servers on one host sharing ~/.codex.
Workaround
Delete the imported subagent threads one by one in the UI. For many of them: I hit this while moving projects between two T3 Code servers on the same host, and wrote t3rry for those moves. As part of a move it soft-deletes imports of the moved threads' sessions and of the subagents they spawned, using the parent_thread_id above. Its delete-imports command removes any other leftover imports from a stopped server. Both write the same thread.deleted events as T3 Code's own delete, and take a backup first.
Thanks for the detailed report and the thread counts, @alcxyz! That made this easy to trace.
What I found
Codex history import treats every rollout-*.jsonl under ~/.codex/sessions as its own conversation. A spawn_agent child is written as a separate rollout with the same cwd as its parent, so project discovery counts it and import publishes it as a top-level thread (parentThreadId: null).
The title comes from the first line of the first user message, which on child rollouts is injected setup text (# AGENTS.md instructions, <recommended_plugins>, and so on), which explains the odd titles.
parseAgentSessionRecords only reads payload.id / payload.session_id from session_meta. TranscriptRecord doesn't declare payload.source, and the decode drops unknown fields, so the parent link is lost before the import decision.
Top-level Codex sessions use a string source (cli, exec, vscode, appServer, unknown) or {custom}. Child rollouts use an object key: subagent on disk, subAgent in the app-server session schema. Its value isn't only thread_spawn; it also covers review, compact, memory_consolidation, and other.
AgentSessionScanner.ts / parseAgentSessionRecords: one option is to decode payload.source and exclude a rollout when the subagent/subAgent key is present, rather than only when thread_spawn is set.
Where the check runs matters. Reporting these as Skipped would bump skippedCount, which onboarding shows as "could not be imported" and which keeps the project out of the fully-imported set. Returning null from the parser may also be too late, since each file is reserved against the 100-transcript import budget before parsing, so child rollouts could crowd out real sessions.
The same check likely belongs in the project-candidate transcript count, since child rollouts share the parent's cwd.
For comparison, Claude import also doesn't attach subagents to a parent thread (it drops sidechain/meta records), so linking Codex children into their parent during import would be a separate change. A fix here wouldn't remove threads that were already imported.
Activity
juliusmarminge commentedon Oct 5, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the detailed report and the thread counts, @alcxyz! That made this easy to trace.
What I found
rollout-*.jsonlunder~/.codex/sessionsas its own conversation. Aspawn_agentchild is written as a separate rollout with the samecwdas its parent, so project discovery counts it and import publishes it as a top-level thread (parentThreadId: null).# AGENTS.md instructions,<recommended_plugins>, and so on), which explains the odd titles.parseAgentSessionRecordsonly readspayload.id/payload.session_idfromsession_meta.TranscriptRecorddoesn't declarepayload.source, and the decode drops unknown fields, so the parent link is lost before the import decision.source(cli,exec,vscode,appServer,unknown) or{custom}. Child rollouts use an object key:subagenton disk,subAgentin the app-server session schema. Its value isn't onlythread_spawn; it also coversreview,compact,memory_consolidation, andother.Likely fix area
AgentSessionScanner.ts/parseAgentSessionRecords: one option is to decodepayload.sourceand exclude a rollout when thesubagent/subAgentkey is present, rather than only whenthread_spawnis set.Skippedwould bumpskippedCount, which onboarding shows as "could not be imported" and which keeps the project out of the fully-imported set. Returning null from the parser may also be too late, since each file is reserved against the 100-transcript import budget before parsing, so child rollouts could crowd out real sessions.cwd.A maintainer will decide on the fix direction.