Expose model settings in app-server thread metadata (#42151)

## What changed

- Add nullable `model` and `reasoningEffort` fields to the shared app-server `Thread` object and generated schemas.
- Report current settings for loaded threads and the latest persisted settings for unloaded threads across thread read, list, start, resume, rollback, metadata update, and notification paths.
- Keep unavailable legacy or filesystem-only settings nullable, and allow metadata reads without loading a thread or dispatching queued work.

## Testing

- Cover live-setting precedence, persisted settings after restart, null legacy values, and model metadata returned by thread lifecycle operations.

GitOrigin-RevId: 22ebe8a8d1910a820d90a97730d812a00d8c1372
This commit is contained in:
Brent Traut
2026-09-01 22:56:18 +00:00
committed by copyberry
parent 6b59cefcbb
commit 986ff1cc7c
50 changed files with 633 additions and 33 deletions
@@ -3130,6 +3130,8 @@ mod tests {
project_id: None,
history_mode: Default::default(),
model_provider: "openai".to_string(),
model: None,
reasoning_effort: None,
created_at: 1,
updated_at: 2,
recency_at: Some(3),
@@ -3185,6 +3187,8 @@ mod tests {
"projectId": null,
"historyMode": "legacy",
"modelProvider": "openai",
"model": null,
"reasoningEffort": null,
"createdAt": 1,
"updatedAt": 2,
"recencyAt": 3,
@@ -288,6 +288,8 @@ fn thread_resume_response_round_trips_initial_turns_page() {
project_id: None,
history_mode: Default::default(),
model_provider: "openai".to_string(),
model: None,
reasoning_effort: None,
created_at: 1,
updated_at: 1,
recency_at: Some(1),
@@ -5,6 +5,7 @@ use super::TurnStatus;
use crate::JsonSchema;
use crate::TS;
use codex_experimental_api_macros::ExperimentalApi;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::MisalignmentErrorDetails as CoreMisalignmentErrorDetails;
use codex_protocol::protocol::MisalignmentSteer as CoreMisalignmentSteer;
use codex_protocol::protocol::SessionSource as CoreSessionSource;
@@ -233,6 +234,12 @@ pub struct Thread {
pub history_mode: ThreadHistoryMode,
/// Model provider used for this thread (for example, 'openai').
pub model_provider: String,
/// Current configured model when loaded, otherwise the latest persisted model.
/// Null when unavailable. This is not per-turn execution telemetry.
pub model: Option<String>,
/// Current configured reasoning effort when loaded, otherwise the latest persisted effort.
/// Null when unset or unavailable. This is not per-turn execution telemetry.
pub reasoning_effort: Option<ReasoningEffort>,
/// Unix timestamp (in seconds) when the thread was created.
#[ts(type = "number")]
pub created_at: i64,
@@ -294,6 +301,8 @@ struct ThreadCompatibility {
#[serde(default)]
history_mode: ThreadHistoryMode,
model_provider: String,
model: Option<String>,
reasoning_effort: Option<ReasoningEffort>,
created_at: i64,
updated_at: i64,
recency_at: Option<i64>,
@@ -330,6 +339,8 @@ impl<'de> Deserialize<'de> for Thread {
project_id: thread.project_id,
history_mode: thread.history_mode,
model_provider: thread.model_provider,
model: thread.model,
reasoning_effort: thread.reasoning_effort,
created_at: thread.created_at,
updated_at: thread.updated_at,
recency_at: thread.recency_at,