fix(tui): restore remote resume and fork history#14930
fix(tui): restore remote resume and fork history#14930etraut-openai merged 12 commits intoopenai:mainfrom
Conversation
Preserve the app-server `Thread` snapshot returned during start, resume, and fork, then seed the primary thread store from its `turns` before replaying the rebuilt transcript. This makes `tui_app_server` restore remote session history without relying on historical websocket notifications being replayed after `thread/resume` or `thread/fork`.
Build the first replay snapshot for resumed and forked app-server threads from a temporary lossless `ThreadEventStore` before updating the real bounded channel store. This keeps early history visible on first load even when the restored thread is larger than the live event buffer. Add a regression test for over-capacity restores and document why the `TurnStatus::InProgress` snapshot arm intentionally emits no completion event during replay.
Share terminal turn event mapping between the live app-server bridge and snapshot replay so interrupted and failed turns hydrate with the same `EventMsg` sequence in both paths. Add coverage for interrupted and failed `TurnCompleted` notifications to lock in parity for reconnect and restore flows.
Add rustdoc to the new types and functions introduced by the remote resume/fork history fix so reviewers can understand intent without reading the full diff.
Borrow app-server thread items while bridging replay events and move `response.thread` out of start, resume, and fork responses instead of cloning potentially large restored histories. This keeps the replay behavior unchanged while removing avoidable allocation churn on restore paths and response mapping.
Keep `WebSearchAction::Other` when bridging app-server thread items so restore replay matches the session mapping path instead of dropping the entire web search item. This preserves history parity for restored threads without changing the handling of supported web search actions.
Keep failed-turn error synthesis for snapshot replay, but suppress it when bridging live `TurnCompleted` notifications because the app-server already emits a separate `ServerNotification::Error` for failed turns. Add coverage so live failed turns emit a single completion event while restored failed turns still replay their error state.
Stop resume and fork startup mapping from populating `SessionConfiguredEvent.initial_messages` now that restored thread history is replayed through the snapshot restore path. Remove the unused initial-message conversion helpers and update the session test to lock in snapshot-only replay for restored history.
Replay restored non-message turn items using their legacy event shapes so resume and fork snapshots keep reasoning, web search, image generation, and context compaction cells visible in the transcript. Keep `ItemCompleted` replay for user, plan, and agent-message items, which still depend on committed-turn semantics in the TUI. Add adapter tests to pin the mixed replay contract.
Carry `show_raw_agent_reasoning` through the app-server start/resume state so snapshot replay keeps raw reasoning history when users enable that transcript mode. Update snapshot replay tests in the adapter and app restore path to pin that resumed and forked threads retain the raw reasoning events instead of dropping them during hydration.
|
@codex review |
There was a problem hiding this comment.
Pull request overview
This PR fixes missing transcript history when the TUI connects to a remote app-server and the user resumes or forks a thread. It preserves and replays the server-provided Thread snapshot (including historical turns/items) through the TUI’s existing event pipeline, and also clears stale session state on reset.
Changes:
- Extend
AppServerStartedThreadto carry the fullThreadsnapshot (plusshow_raw_agent_reasoning) for start/resume/fork. - Add snapshot-to-event replay (
thread_snapshot_events/restore_started_app_server_thread) so remote resumed/forked sessions render prior turns. - Clear
primary_session_configuredduring reset to prevent stale session state after reconnection.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
codex-rs/tui_app_server/src/app_server_session.rs |
Preserves the full Thread in start/resume/fork results; removes legacy “initial_messages from turns” path. |
codex-rs/tui_app_server/src/app/app_server_adapter.rs |
Adds thread_snapshot_events + turn terminal-event bridging to generate replayable Event sequences from a Thread snapshot. |
codex-rs/tui_app_server/src/app.rs |
Introduces restore_started_app_server_thread to seed the event store and replay history; clears primary_session_configured on reset; updates startup/resume/fork flow to use the new restore path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| /// 2. Builds a **lossless** replay snapshot from a temporary store so that | ||
| /// the initial render sees all history even when the thread has more | ||
| /// turns than the bounded channel capacity. |
| /// produce identical `EventMsg` sequences for the same turn status. | ||
| /// | ||
| /// - `Completed` → `TurnComplete` | ||
| /// - `Interrupted` → `TurnAborted { reason: Interrupted }` | ||
| /// - `Failed` → `Error` (if present) then `TurnComplete` | ||
| /// - `InProgress` → no events (the turn is still running) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cfe71650e9
ℹ️ 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".
Defer `initial_user_message` submission while a restored thread snapshot is replayed so resume and fork startup prompts stay after hydrated history in the transcript. Add a regression test that restores remote history with a startup prompt and asserts the restored transcript renders before the prompt submission.
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6151d8a9ea
ℹ️ 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".
|
@codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
Problem
When the TUI connects to a remote app-server (via WebSocket), resume and fork operations lost all conversation history.
AppServerStartedThreadcarried only theSessionConfiguredevent, not the fullThreadsnapshot. After resume or fork, the chat transcript was empty — prior turns were silently discarded.A secondary issue:
primary_session_configuredwas not cleared on reset, causing stale session state after reconnection.Approach: TUI-side only, zero app-server changes
The app-server already returns the full
Threadobject (with populatedturns: Vec<Turn>) in itsThreadStartResponse,ThreadResumeResponse, andThreadForkResponse. The data was always there — the TUI was simply throwing it away. The oldAppServerStartedThreadstruct only kept theSessionConfiguredEvent, discarding the rich turn history that the server had already provided.This PR fixes the problem entirely within
tui_app_server(3 files changed, 0 changes toapp-server,app-server-protocol, or any other crate). Rather than modifying the server to send history in a different format or adding a new endpoint, the fix preserves the existingThreadsnapshot and replays it through the TUI's standard event pipeline — making restored sessions indistinguishable from live ones.Solution
Add a thread snapshot replay path. When the server hands back a
Threadobject (on start, resume, or fork),restore_started_app_server_threadconverts its historical turns into the same coreEventsequence the TUI already processes for live interactions, then replays them into the event store so the chat widget renders them.Key changes:
AppServerStartedThreadnow carries the fullThread—started_thread_from_{start,resume,fork}_responseclone the thread into the struct alongside the existingSessionConfiguredEvent.thread_snapshot_events()walks the thread's turns and items, producingTurnStarted→ItemCompleted* →TurnComplete/TurnAbortedevent sequences that the TUI already knows how to render.restore_started_app_server_thread()pushes the session event + history events into the thread channel's store, activates the channel, and replays the snapshot — used for initial startup, resume, and fork.primary_session_configuredcleared on reset to prevent stale session state after reconnection.Tradeoffs
Threadis cloned intoAppServerStartedThread: The full thread snapshot (including all historical turns) is cloned at startup. For long-lived threads this could be large, but it's a one-time cost and avoids lifetime gymnastics with the response.Tests
restore_started_app_server_thread_replays_remote_history— end-to-end: constructs aThreadwith one completed turn, restores it, and asserts user/agent messages appear in the transcript.bridges_thread_snapshot_turns_for_resume_restore— unit: verifiesthread_snapshot_eventsproduces the correct event sequence for completed and interrupted turns.Test plan
cargo check -p codex-tui-app-serverpassescargo test -p codex-tui-app-serverpasses