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 19:17:16 +00:00
committed by GitHub
parent b6d4c4ea6b
commit 9e0c191c13
48 changed files with 821 additions and 27 deletions
@@ -142,6 +142,7 @@ fn turn_started_emits_turn_started_event() {
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::InProgress,
error: None,
@@ -1095,6 +1096,7 @@ fn plan_update_emits_started_then_updated_then_completed() {
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,
@@ -1154,6 +1156,7 @@ fn plan_update_after_completion_starts_new_todo_list_with_new_id() {
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,
@@ -1236,6 +1239,7 @@ fn token_usage_update_is_emitted_on_turn_completion() {
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,
@@ -1270,6 +1274,7 @@ fn turn_completion_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(),
@@ -1342,6 +1347,7 @@ fn turn_completion_reconciles_started_items_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::CommandExecution {
id: "cmd-1".to_string(),
command: "ls".to_string(),
@@ -1409,6 +1415,7 @@ fn turn_completion_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(),
@@ -1458,6 +1465,7 @@ fn turn_completion_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,
@@ -1506,6 +1514,7 @@ fn failed_turn_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: Some(TurnError {
@@ -1533,6 +1542,7 @@ fn turn_completion_falls_back_to_final_plan_text() {
thread_id: "thread-1".to_string(),
turn: Turn {
id: "turn-1".to_string(),
items_view: codex_app_server_protocol::TurnItemsView::Full,
items: vec![ThreadItem::Plan {
id: "plan-1".to_string(),
text: "ship the typed adapter".to_string(),
@@ -1587,6 +1597,7 @@ fn turn_failure_prefers_structured_error_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,