Repository navigation
Desktop/app-server history replay omits persisted commandExecution items #28162
Copy link
Copy link
Closed as duplicate
Closed as duplicate
Copy link
Labels
appIssues related to the Codex desktop appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesIssues involving app server protocol or interfacesbugSomething isn't workingSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archivingIssues involving session (thread) management, resuming, forking, naming, archiving
Description
Summary
Codex Desktop/app-server history replay appears to omit persisted command execution items after a session is reloaded. The raw rollout JSONL still contains the command tool calls and outputs, but thread/read with includeTurns: true returns no commandExecution items for the same thread.
Environment
- Codex CLI:
codex-cli 0.133.0 - Codex Desktop / app-server protocol:
thread/readwithincludeTurns: true - Platform: macOS
Evidence
- Raw rollout JSONL contains persisted command execution data, including
response_itempayloads withtype: "function_call"andname: "exec_command", matchingfunction_call_outputpayloads, and in some sessionsevent_msgpayloads withtype: "exec_command_end". - Across a local rollout corpus, the raw files include 47,534
exec_commandfunction calls, 56,291 function call outputs, and 5,603exec_command_endevents. - For one sampled thread, raw JSONL contained thousands of tool/function rows. The SQLite thread index pointed to the correct rollout path. However
thread/read includeTurns=truereturned 130 turns with item counts such asfileChange: 250,mcpToolCall: 53,agentMessage: 757, andcommandExecution: 0. - Additional sampled sessions with only legacy
response_item.function_call(name=exec_command)rows and sessions with newerevent_msg.exec_command_endrows also replayed withcommandExecution: 0. - This makes command execution details disappear from the Desktop UI after reload/switch even though the underlying rollout JSONL is still intact.
Expected behavior
History replay should reconstruct command execution UI items, or an equivalent expandable tool-call item, from persisted rollout rows such as:
{"type":"response_item","payload":{"type":"function_call","name":"exec_command","call_id":"...","arguments":"{...}"}}
{"type":"response_item","payload":{"type":"function_call_output","call_id":"...","output":"..."}}
{"type":"event_msg","payload":{"type":"exec_command_end","call_id":"...","command":["/bin/zsh","-lc","..."],"cwd":"...","exit_code":0,"status":"completed"}}Actual behavior
After reload/history replay, command execution details are missing from the returned thread items and from the Desktop UI. Other history items such as assistant messages, file changes, and some MCP/tool items can still appear.
Reproduction outline
- Create or identify a Codex session that has shell command executions.
- Confirm the raw rollout JSONL contains
exec_commandfunction calls and matching outputs, and/orexec_command_endevents. - Start app-server and call
thread/readwithincludeTurns: truefor that thread. - Count returned turn items. The raw rollout has command execution data, but the replayed turns contain zero
commandExecutionitems.
Notes
- This report intentionally avoids including local paths, credentials, command output, or private session content.
- The problem looks like a history replay/normalization issue rather than physical JSONL loss: the raw rollout files remain intact and the SQLite
threads.rollout_pathvalues point to the correct files.
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
appIssues related to the Codex desktop appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesIssues involving app server protocol or interfacesbugSomething isn't workingSomething isn't workingsessionIssues involving session (thread) management, resuming, forking, naming, archivingIssues involving session (thread) management, resuming, forking, naming, archiving
Activity
wgu9 commentedon Jun 15, 2026
I took a look at the current replay reducer. The missing-command-history behavior seems to split into two cases.
For newer rollout rows that contain
event_msgvalues, currentmainalready has a replay path:thread/read includeTurnsloads rollout history and callsbuild_api_turns_from_rollout_items(...)ThreadHistoryBuilder::handle_event(...)handlesEventMsg::ExecCommandBeginandEventMsg::ExecCommandEndhandle_exec_command_end(...)callsbuild_command_execution_end_item(...), which maps the core event intoThreadItem::CommandExecutionwithaggregated_output,exit_code, andduration_msapp-server-protocol/src/protocol/thread_history.rsasserting that anExecCommandEndEventreplays into a completedCommandExecutionitemThe gap I do see is the legacy-only shape mentioned in the report: rollout rows that have only
response_item.function_call(name = "exec_command")plus matchingresponse_item.function_call_output, without the newerEventMsg::ExecCommandBegin/Endrecords. In currentThreadHistoryBuilder::handle_response_item(...), response-item replay only handles userMessagerows that parse as hook prompts. Non-message response items, includingfunction_callandfunction_call_output, return early and do not contribute anyThreadItem.That explains why older sessions with only persisted Responses API tool-call rows can replay with
commandExecution: 0even though the raw JSONL still contains command calls and outputs.A minimal fix path would probably be:
EventMsg::ExecCommand*replay as the canonical path for newer rollouts.ThreadHistoryBuilderthat pairsResponseItem::FunctionCall { name: "exec_command", call_id, arguments, ... }withResponseItem::FunctionCallOutput { call_id, output }.ThreadItem::CommandExecutionfrom those paired rows, using the parsed command/cwd from the arguments when available and the function-call output asaggregated_output.ExecCommandEndevent rows exist for the samecall_id; the event row should win because it carries richer status, cwd, parsed command actions, exit code, and duration.A focused regression test could build rollout items containing only the legacy
response_itempair for anexec_commandcall and assertbuild_turns_from_rollout_items(...)returns oneThreadItem::CommandExecution. A second test should include both the legacy pair and anExecCommandEndEventfor the samecall_idand assert only the event-derived command item survives.Per
docs/contributing.md, I am not opening an unsolicited PR, but this looks like a contained replay-normalization fix if maintainers want it.VincentAdamNemessisX commentedon Jun 17, 2026
Okay, thanks for your reply.