add turn items view to app-server turns (#21063)

## Why

`Turn.items` currently overloads an empty array to mean either that no
items exist or that the server intentionally did not load them for this
response. That ambiguity blocks future lazy-loading work where clients
need to distinguish unloaded, summary, and fully hydrated turn payloads.

## What changed

- add a new `TurnItemsView` enum with `notLoaded`, `summary`, and `full`
variants
- add required `itemsView` metadata to app-server `Turn` payloads
- mark reconstructed persisted history as `full` and live shell-style
turn payloads as `notLoaded`
- keep current `thread/turns/list` behavior unchanged and document that
it still returns `full` turns today
- regenerate the JSON and TypeScript protocol fixtures

## Verification

- `just write-app-server-schema`
- `cargo test -p codex-app-server-protocol`
- `cargo test -p codex-app-server thread_read_can_include_turns`
- `cargo test -p codex-app-server
thread_turns_list_can_page_backward_and_forward`
- `cargo test -p codex-app-server
thread_resume_rejects_history_when_thread_is_running`
- `just fix -p codex-app-server-protocol`
- `just fix -p codex-app-server`
- `just fmt`
This commit is contained in:
rhan-oai
2026-05-05 12:17:16 -07:00
committed by GitHub
Unverified
parent b6d4c4ea6b
commit 9e0c191c13
48 changed files with 821 additions and 27 deletions
@@ -240,6 +240,7 @@ fn turn_completed_recovers_final_message_from_turn_items() {
thread_id: "thread-1".to_string(),
turn: Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: vec![ThreadItem::AgentMessage {
id: "msg-1".to_string(),
text: "final answer".to_string(),
@@ -287,6 +288,7 @@ fn turn_completed_overwrites_stale_final_message_from_turn_items() {
thread_id: "thread-1".to_string(),
turn: Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: vec![ThreadItem::AgentMessage {
id: "msg-1".to_string(),
text: "final answer".to_string(),
@@ -335,6 +337,7 @@ fn turn_completed_preserves_streamed_final_message_when_turn_items_are_empty() {
thread_id: "thread-1".to_string(),
turn: Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: Vec::new(),
status: TurnStatus::Completed,
error: None,
@@ -378,6 +381,7 @@ fn turn_failed_clears_stale_final_message() {
thread_id: "thread-1".to_string(),
turn: Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: Vec::new(),
status: TurnStatus::Failed,
error: None,
@@ -422,6 +426,7 @@ fn turn_interrupted_clears_stale_final_message() {
thread_id: "thread-1".to_string(),
turn: Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: Vec::new(),
status: TurnStatus::Interrupted,
error: None,
@@ -32,6 +32,7 @@ fn failed_turn_does_not_overwrite_output_last_message_file() {
thread_id: "thread-1".to_string(),
turn: codex_app_server_protocol::Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: Vec::new(),
status: TurnStatus::Failed,
error: Some(codex_app_server_protocol::TurnError {
+3
View File
@@ -262,6 +262,7 @@ fn turn_items_for_thread_returns_matching_turn_items() {
turns: vec![
codex_app_server_protocol::Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: vec![AppServerThreadItem::AgentMessage {
id: "msg-1".to_string(),
text: "hello".to_string(),
@@ -276,6 +277,7 @@ fn turn_items_for_thread_returns_matching_turn_items() {
},
codex_app_server_protocol::Turn {
id: "turn-2".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: vec![AppServerThreadItem::Plan {
id: "plan-1".to_string(),
text: "ship it".to_string(),
@@ -308,6 +310,7 @@ fn should_backfill_turn_completed_items_skips_ephemeral_threads() {
thread_id: "thread-1".to_string(),
turn: codex_app_server_protocol::Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: Vec::new(),
status: codex_app_server_protocol::TurnStatus::Completed,
error: None,