codex
Identity
-
Native format:
codex-rollout. Session logs, called “rollouts”, are stored as JSONL under~/.codex/sessions/YYYY/MM/DD/rollout-<timestamp>-<thread id>[_<rollout id>].jsonl. A thread can span several rollout files: a revert writes a new rollout (a segment) whose file name adds a rollout ID. Rollouts that are no longer active may be recompressed to.jsonl.zstor moved toarchived_sessions/; the path is not identity (ASR-053). The SQLite stores beside them are derived indexes, except for titles, pins, sections, projects, pull request attachments, goals and the queue of input not yet delivered. -
Session ID: the
session_metaid(the thread ID), the same across segments. The spawn tree’s rootsession_idis kept in_meta. Resolve file aliases to their targets before identifying segments. A broken alias means the file is unavailable (unreadableorunknownin a manifest), neverdeleted_by_retention. When the header is only partly written (torn), recover the thread ID from the canonical native basename only when the native thread index corroborates it; keepcom.openai.codex/identityRecovery, emit gaps and leave lineage unknown. Otherwise report an unidentified source. This native-source recovery rule does not relax ASR-020 for ASR bundles. A child rollout can contain its parent’ssession_metaas well as its own; the child’s own metadata is the entry whoseididentifies the child thread. -
Entity keys:
callIdiscall_id;inferenceIdistoken_usage_record.response_id. Legacy per-turn usage has no inference ID and usesusage_summary.turnIdis the recordedturn_id. The native position a record’s identity comes from is(native segment ID, ordinal or line, sub-slot), kept atcom.openai.codex/position; it is not an ASRseqtuple. Allocate increasing safe-integerseqin documented segment creation order and native item order, then sub-slot order. Revert segments reuse the ordinal numbering they inherit. Do not invent creation order when native evidence leaves it ambiguous; preserve the evidence and declareorder. -
Model: usage records carry no model.
model.requestedcomes from the turn’sturn_context.modelwithrequestedSource: turn_config(ASR-325);model.idstays absent. -
Messages: each part’s author and origin come from
content_item_kinds(aligned one to one withcontent):user.textanduser.image→human,prompt;agents_md.instructions,environments.environment_context,host_skills.instructions,permissions.instructions,collaboration_mode.instructionsand the other*.instructionskinds →harness,injected;hooks.additional_context→hook,injected;multi_agent.subagent_notification→agent,subagent_report;generic.developer_instructions→injectedwithauthorabsent. Other kinds leaveauthorabsent. These are part-level values (ASR-224). Assistantphasecommentary→commentary,final_answer→final.message.encrypted_contentand encrypted parts of agent messages →opaqueparts. -
Messages between agents:
agent_messageitems andinter_agent_communication→ amessagewithauthor: agent,origin: inter_session,from: {session: <root thread>, address: <author path>},recipients(recipientasprimary,other_recipientsasadditional, each scoped by the root thread), andtriggersTurnfromtrigger_turn. -
Tools:
function_call,custom_tool_callandlocal_shell_call→tool_callwithrequestedBy: model. Executions nested in a code-mode cell (CommandExecution,McpToolCall,FileChangeitems whose IDs are not model call IDs) → atool_callwithrequestedBy: codeand the item ID ascallId, and itstool_result;parentCallIdonly from an explicit initiating model-call ID.cell_ididentifies a host cell shared by execution and waiting operations. It does not identify a model call; keep it incom.openai.codex/cellId. Read raw execution fields as well as the typed dispatch data: deserializing data for replay can omit IDs and information about which executions occurred.CommandExecution.source: user_shell→requestedBy: user.McpToolCallserverandtool→provider {kind: mcp, server, tool, identity: exact}; calls through an app connector (connectorId,appName) →provider.kind: connectorwithname.process_id→exec.processId, preserving its native string or safe-integer type. This is provenance, never a terminal reference. Approvals, hook runs and terminal lifecycle events are not written to rollouts: a converter writes nopermissionorhookrecords and no inventedterminalrecords for them. A process ID alone does not establish a terminal lifecycle. -
Questions:
request_user_inputcalls and asynchronous questions (AgentMessagewithdelivery: asyncandquestions) →input_requestwithmode: questions,callId,blocking: falsefor asynchronous ones, and questionsecretflags from the call; answers from the call’s output or the retained verified answers. Secret answers are never recorded (ASR-283). -
Turns:
task_started→staterunning;task_complete→idle(stopReason: end_turn, orerrorwitherror);turn_aborted→idlewithstopReason: cancelled(unmapped reasons retained incom.openai.codex/abortReason).turn_attribution.turn_trigger→trigger.native;initiating_agent_path→trigger.source{session: <root thread>, address}.root_turn_id→state.rootTurn {session: <root thread>, turnId}.parent_turn_id→state.parentTurnwithsessionset to the parent thread when the turn was started by the parent; when the source does not say which thread it is in,parentTurnis not written. -
Configuration: each
turn_context→ aconfig_changesnapshot (snapshot: true,turnId) withmodel,effort,permissionModefromapproval_policy,sandboxfromsandbox_policy(mode,network,writableRoots), and the other members inoptions.thread_settings_applied→ aconfig_changethat is a change;service_tiergoes inoptions. Header:originator→client.name;thread_source→ headertrigger.native. -
Compaction:
compacted→compaction, withcompactionIdfromwindow_idandreplaces.throughIdfrom the native checkpoint predecessor when available.replacement_history[].retained_source.id.message_idmaps toretainsonly when it identifies an earlier local active-path record unambiguously;retained_source.complete: falsesetscapture: truncated. Keep the entirereplacement_history, including shortened bytes, fresh instructions and order, atcom.openai.codex/compaction. Declare_com.openai.codex/compaction_structurewhen ASR cannot express that replacement. External retained refs are not supported in this revision.messagesupplies the summary; encrypted checkpoint state useskind: provider_nativeandopaque.CompactedItemrecords neither a trigger nor a link to the call that started it: a nearbynew_contextis not evidence. Settriggeronly from an explicit native trigger or a qualified structural contract.compaction_response_ididentifies the compaction inference when present.thread_rolled_back.num_turnsis a rollback boundary only under the source’s native retained-turn contract; do not count UI messages as turns. -
Instructions: AGENTS.md, skills and permission text reach the model as developer or user message parts; those parts are what the context view shows.
instructionsrecords come only fromworld_staterecords withfull: true. -
Memory: loaded memories reach the model as
memories.instructionsparts. Memory writes happen in a memory-consolidation thread (sourceinternal_memory_consolidation, or subagentmemory_consolidation): afile_changeunder the Codex memories directory (~/.codex/memories/) in such a thread also gets amemory_changewithstore: "codex_memories",scope: "user",op: "consolidate",target.pathandcause.callId. -
Other:
update_plan→planwithplanType: items;Planitems →planType: document; goals (thread_goal_updated) →planType: goalwithstatus,budget.tokensfromtoken_budget, andusedfromtokens_usedandtime_used_seconds. Pull request attachments →vcspr_linkwithevidence: reported. The generatedtitle→titleSource: model; the user-setname→titleSource: user; pins and archive →flags; projects →session_info.project. Realtime voice transcript segments → messages with text parts that carryderivation {method: transcription}. Reasoning items carryencrypted_content(visibility: encrypted,opaque.format: openai.encrypted_content) and a summary that is often empty (preserve[]). In legacy history mode, command exit codes and output are not kept outside the model-facing output text: declareexec.exit_code. -
Goal identity: prefer an authoritatively associated native
goal_idif available. The protocol goal has no ID: deriveplanIdfrom(threadId, createdAt), agoaltype tag and derivation version, withidSource: derived, only when that tuple is stable and unique in the thread. If two goals produce the same derived ID, do not merge them. Preserve them as native goal evidence until a documented method of establishing identity distinguishes them. Do not match a database goal ID to a goal because the objective text or timestamps look alike. -
Alternate representations: join response and UI items only by unique, same-scope IDs under their documented correspondence. Keep user UI items separate with
displayContentonly. UI{type: image, image_url}maps toresource_linkwithuriequal toimage_urland a native display name when present, otherwise the URI asname; never claim inline image bytes from a URI.DynamicToolCallItem.errorstrings map to{message: <native string>}. DecodePathUricwdunder the source’s URI semantics (ASR-477). -
Current state: titles, flags, projects, goals and read-state rows read at export time use snapshot assembly in the capture tail. Rows with a stable identity do not need
idScope: capture.
Position basis
Use only positions established by the stated format and mapping evidence. Current-state rows without transcript positions form the snapshot tail. A protocol commit boundary takes precedence over physical EOF; uncommitted bytes are not a gap.
Destinations
Codex: native facts without a standard field
Links
-
Lineage: subagent threads carry
source.subagent.thread_spawn.parent_thread_id→spawnedBy(native_field), withrolefromagent_role.forked_from_id→forkedFrom; the fork boundary isforked_from_ordinal_exclusive:forkedFrom.ordinalis that value minus 1.history_base {thread_id, end_ordinal_exclusive}names a rollout (a segment), not a thread, despite its field name: resolve it to its thread before writing a session reference. When it names an earlier segment of the same thread, the new segment is a revert of that thread. Write the new segment’s records in the same stream after arewindwithreason: rollback. Settoto the last record that participates in context and has an ordinal belowend_ordinal_exclusivein the identified segment’s active prefix. When it names another thread’s rollout, writeinherits {mode: referenced, from, throughOrdinal: end_ordinal_exclusive − 1}. Legacy forks copy the parent’s items into the child:inherits {mode: copied, evidence: native_structure}only under the legacy native copied-prefix contract;subagent_history_start_ordinalmarks where inherited records end (localThroughId). A subagent created from copied context is bothspawnedByandforkedFrom.rootmay be claimed whensourceis not a subagent source and there is noforked_from_id, and nohistory_basenaming another thread. -
Lineage ordinals after reverts:
history_base.thread_ididentifies a native segment, andend_ordinal_exclusiveselects its retained prefix. Follow that segment’s own history-base chain and apply each recorded boundary. Then select the last item below the exclusive bound on that active prefix, using the item’s final context sub-slot. Prefer a record ref once mapped. A bare inclusive ordinal without an identified segment or prefix is ambiguous after a revert; leave the boundary unknown, retain native lineage metadata and declarelineage. Never select the most recent segment by timestamp or the last matching ordinal. Segment creation order comes only from native metadata or the recorded history-base relationship.
Codex link contracts
These contracts apply to sourcec587d6e; the observed 0.144.3 to 0.163.0 range does not imply that every field exists in every release.
Usage
- Usage:
token_usage_record→inferencewithinput.total,includesCache: true,cacheRead,cacheWrite,output.total,output.reasoningandtotal; its turn and thread totals →usage_summarywithcumulative: true.token_countrepeats these totals (ASR-311).token_count.rate_limits→usage_summary.quota:planfromplan_type, and one window each forprimaryandsecondary(usedPercentfromused_percent,resetsAtfromresets_at). No cost is recorded.
Lane and unassigned facts
Facts without established session ownership use manifest_meta["com.openai.codex/unassigned"] with the common shape. Selection may retain these facts with any session subset, including an empty subset, but must not imply session ownership. State the selection policy in the export report.
Losses
Declare every applicable loss under ASR-157. Missing model rendering, accounting joins, lineage or exact context boundaries remain losses when this entry does not qualify them. Unknown ownership is not itself lost data. Omitted unassigned facts use a documented vendor loss or report, not a new core category.Evidence
Basis: source (openai/codex at c587d6e), and observed (local rollouts of versions 0.144.3 to 0.163.0).
Evidence applies only to the named formats and explicit mappings above. A real capture covers only exercised behavior. A writer probe run without a model covers persistence, not model or tool execution. Authored tests do not qualify an agent version. Unlisted formats and link mappings remain unqualified.