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 authored and copyberry committed 2026-09-01 22:56:18 +00:00
1 parent 6b59cefcbb
commit 986ff1cc7c
50 files changed
+633 -33

No files matched your search

+7
View File
@@ -28,6 +28,7 @@ use codex_protocol::RolloutId;
use codex_protocol::SanitizedGitUrl;
use codex_protocol::ThreadId;
use codex_protocol::items::TurnItem;
use codex_protocol::openai_models::ReasoningEffort;
use codex_protocol::protocol::SessionMetaLine;
use codex_protocol::protocol::SessionSource;
use codex_protocol::protocol::ThreadHistoryMode;
@@ -82,6 +83,10 @@ pub struct ThreadItem {
pub agent_role: Option<String>,
/// Model provider from session metadata.
pub model_provider: Option<String>,
/// Latest persisted model in SQLite-owned metadata, when available.
pub model: Option<String>,
/// Latest persisted reasoning effort in SQLite-owned metadata, when available.
pub reasoning_effort: Option<ReasoningEffort>,
/// CLI version from session metadata.
pub cli_version: Option<String>,
/// RFC3339 timestamp string for when the session was created, if available.
@@ -849,6 +854,8 @@ async fn build_thread_item(
agent_nickname,
agent_role,
model_provider,
model: None,
reasoning_effort: None,
cli_version,
created_at,
recency_at: summary_updated_at.clone(),
+6
View File
@@ -1298,6 +1298,8 @@ fn fill_missing_thread_item_metadata(item: &mut ThreadItem, state_item: ThreadIt
agent_nickname,
agent_role,
model_provider,
model,
reasoning_effort,
cli_version,
created_at,
updated_at,
@@ -1312,6 +1314,8 @@ fn fill_missing_thread_item_metadata(item: &mut ThreadItem, state_item: ThreadIt
}
item.section = section;
item.project_id = project_id;
item.model = model;
item.reasoning_effort = reasoning_effort;
if item.cwd.is_none() {
item.cwd = cwd;
}
@@ -2040,6 +2044,8 @@ fn thread_item_from_state_metadata(
agent_nickname: item.agent_nickname,
agent_role: item.agent_role,
model_provider: Some(item.model_provider),
model: item.model,
reasoning_effort: item.reasoning_effort,
cli_version: Some(item.cli_version),
created_at: Some(item.created_at.to_rfc3339_opts(SecondsFormat::Secs, true)),
updated_at: Some(item.updated_at.to_rfc3339_opts(SecondsFormat::Millis, true)),
+4
View File
@@ -1544,6 +1544,8 @@ fn fill_missing_thread_item_metadata_preserves_identity_and_prefers_state_git_fi
agent_nickname: None,
agent_role: None,
model_provider: None,
model: None,
reasoning_effort: None,
cli_version: None,
created_at: None,
recency_at: Some("2025-01-03T15:59:00.000Z".to_string()),
@@ -1573,6 +1575,8 @@ fn fill_missing_thread_item_metadata_preserves_identity_and_prefers_state_git_fi
agent_nickname: Some("state-agent".to_string()),
agent_role: Some("state-role".to_string()),
model_provider: Some("state-provider".to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("state-version".to_string()),
created_at: Some("2025-01-03T16:00:00Z".to_string()),
recency_at: Some("2025-01-03T16:00:30.001Z".to_string()),
+22
View File
@@ -735,6 +735,8 @@ async fn test_list_conversations_latest_first() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-01-03T12-00-00".into()),
recency_at: updated_times.first().cloned().flatten(),
@@ -757,6 +759,8 @@ async fn test_list_conversations_latest_first() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-01-02T12-00-00".into()),
recency_at: updated_times.get(1).cloned().flatten(),
@@ -779,6 +783,8 @@ async fn test_list_conversations_latest_first() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-01-01T12-00-00".into()),
recency_at: updated_times.get(2).cloned().flatten(),
@@ -894,6 +900,8 @@ async fn test_pagination_cursor() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-03-05T09-00-00".into()),
recency_at: updated_page1.first().cloned().flatten(),
@@ -916,6 +924,8 @@ async fn test_pagination_cursor() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-03-04T09-00-00".into()),
recency_at: updated_page1.get(1).cloned().flatten(),
@@ -974,6 +984,8 @@ async fn test_pagination_cursor() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-03-03T09-00-00".into()),
recency_at: updated_page2.first().cloned().flatten(),
@@ -996,6 +1008,8 @@ async fn test_pagination_cursor() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-03-02T09-00-00".into()),
recency_at: updated_page2.get(1).cloned().flatten(),
@@ -1046,6 +1060,8 @@ async fn test_pagination_cursor() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some("2025-03-01T09-00-00".into()),
recency_at: updated_page3.first().cloned().flatten(),
@@ -1221,6 +1237,8 @@ async fn test_get_thread_contents() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some(ts.into()),
recency_at: page.items[0].updated_at.clone(),
@@ -1624,6 +1642,8 @@ async fn test_timestamp_only_cursor_skips_same_second_filesystem_ties() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some(ts.to_string()),
recency_at: updated_page1.first().cloned().flatten(),
@@ -1646,6 +1666,8 @@ async fn test_timestamp_only_cursor_skips_same_second_filesystem_ties() {
agent_nickname: None,
agent_role: None,
model_provider: Some(TEST_PROVIDER.to_string()),
model: None,
reasoning_effort: None,
cli_version: Some("test_version".to_string()),
created_at: Some(ts.to_string()),
recency_at: updated_page1.get(1).cloned().flatten(),