Codex conversations never appeared in the session list, and the resume path
skipped codex, so picking one back up meant finding its thread id by hand and
POSTing codexConfig.resumeSessionId to /api/sessions.
Two gaps caused it:
- The unified list is built from ~/.claude/projects plus omp's own store.
Codex writes to neither: its rollouts live in ~/.codex/sessions/<y>/<m>/<d>.
- terminal-ui.js sends a continuation only for the CLIs with a
"continue most recent" flag. Codex has no such flag — it names a thread by an
exact id — and nothing supplied one.
Add codex-transcript.ts, the codex analog of omp-transcript.ts, and wire it into
gatherUnifiedInputs() beside the omp scan. A rollout row carries `resumeId`, the
thread id `codex resume` takes, and the resume path sends it as
codexConfig.resumeSessionId.
`resumeId` is what keeps the two kinds of row apart: only a transcript scanner
sets it, so a LIVE codex row — whose sessionId is Codeman's own uuid — can never
ask codex for a thread that does not exist.
Three things measured against a real store of 519 rollouts rather than assumed:
- Rollouts are far too large to read whole (median 407 KiB, p90 1.3 MiB, max
25 MiB, 381 MiB total), so this reads a 128 KiB head for the identity and the
opening prompt and a bounded tail for the most recent one. session_meta is
written once and never rewritten, so per-path identity is cached; a warm
rescan of that store costs ~75ms against ~470ms cold.
- codex 0.152.1 emits no event_msg/user_message rows at all. It writes
event_msg/item_completed carrying an item.type of UserMessage. Both shapes are
read, plus response_item as a last resort.
- That last resort sees injected context, and the first such row is the repo's
AGENTS.md every time, so injections are dropped rather than used as titles.
Sub-agent threads (thread_source: 'subagent') are left out; codex spawns them
for itself and on a real store they outnumber the resumable threads.