From 0f34c66acdc301c2645f056cfc791319aa06bb18 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 10 Apr 2026 10:05:42 +0900 Subject: [PATCH] =?UTF-8?q?feat(session):=20persist=20model=20in=20session?= =?UTF-8?q?=20metadata=20=E2=80=94=20ROADMAP=20#59?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add 'model: Option' to Session struct. The model used is now saved in the session_meta JSONL record and surfaced in resumed /status: - JSON mode: {model: 'claude-sonnet-4-6'} instead of null - Text mode: shows actual model instead of 'restored-session' Model is set in build_runtime_with_plugin_state() before the runtime is constructed, and only when not already set (preserves model through fork/resume cycles). Backward compatible: old sessions without a model field load cleanly with model: None (shown as null in JSON, 'restored-session' in text). All workspace tests pass. --- rust/crates/runtime/src/session.rs | 19 +++++++++++++++++++ rust/crates/rusty-claude-cli/src/main.rs | 10 +++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/rust/crates/runtime/src/session.rs b/rust/crates/runtime/src/session.rs index 689b65a..0a0a61a 100644 --- a/rust/crates/runtime/src/session.rs +++ b/rust/crates/runtime/src/session.rs @@ -96,6 +96,9 @@ pub struct Session { pub fork: Option, pub workspace_root: Option, pub prompt_history: Vec, + /// The model used in this session, persisted so resumed sessions can + /// report which model was originally used. + pub model: Option, persistence: Option, } @@ -161,6 +164,7 @@ impl Session { fork: None, workspace_root: None, prompt_history: Vec::new(), + model: None, persistence: None, } } @@ -263,6 +267,7 @@ impl Session { }), workspace_root: self.workspace_root.clone(), prompt_history: self.prompt_history.clone(), + model: self.model.clone(), persistence: None, } } @@ -371,6 +376,10 @@ impl Session { .collect() }) .unwrap_or_default(); + let model = object + .get("model") + .and_then(JsonValue::as_str) + .map(String::from); Ok(Self { version, session_id, @@ -381,6 +390,7 @@ impl Session { fork, workspace_root, prompt_history, + model, persistence: None, }) } @@ -394,6 +404,7 @@ impl Session { let mut compaction = None; let mut fork = None; let mut workspace_root = None; + let mut model = None; let mut prompt_history = Vec::new(); for (line_number, raw_line) in contents.lines().enumerate() { @@ -433,6 +444,10 @@ impl Session { .get("workspace_root") .and_then(JsonValue::as_str) .map(PathBuf::from); + model = object + .get("model") + .and_then(JsonValue::as_str) + .map(String::from); } "message" => { let message_value = object.get("message").ok_or_else(|| { @@ -475,6 +490,7 @@ impl Session { fork, workspace_root, prompt_history, + model, persistence: None, }) } @@ -580,6 +596,9 @@ impl Session { JsonValue::String(workspace_root_to_string(workspace_root)?), ); } + if let Some(model) = &self.model { + object.insert("model".to_string(), JsonValue::String(model.clone())); + } Ok(JsonValue::Object(object)) } diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index 95c2cc6..7caef9d 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -2789,7 +2789,7 @@ fn run_resume_command( Ok(ResumeCommandOutcome { session: session.clone(), message: Some(format_status_report( - "restored-session", + session.model.as_deref().unwrap_or("restored-session"), StatusUsage { message_count: session.messages.len(), turns: tracker.turns(), @@ -2801,7 +2801,7 @@ fn run_resume_command( &context, )), json: Some(status_json_value( - None, + session.model.as_deref(), StatusUsage { message_count: session.messages.len(), turns: tracker.turns(), @@ -6752,7 +6752,7 @@ fn build_runtime( #[allow(clippy::needless_pass_by_value)] #[allow(clippy::too_many_arguments)] fn build_runtime_with_plugin_state( - session: Session, + mut session: Session, session_id: &str, model: String, system_prompt: Vec, @@ -6763,6 +6763,10 @@ fn build_runtime_with_plugin_state( progress_reporter: Option, runtime_plugin_state: RuntimePluginState, ) -> Result> { + // Persist the model in session metadata so resumed sessions can report it. + if session.model.is_none() { + session.model = Some(model.clone()); + } let RuntimePluginState { feature_config, tool_registry,