mirror of
https://github.com/instructkr/claw-code.git
synced 2026-04-10 18:14:50 +08:00
feat(session): persist model in session metadata — ROADMAP #59
Add 'model: Option<String>' 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.
This commit is contained in:
@@ -96,6 +96,9 @@ pub struct Session {
|
|||||||
pub fork: Option<SessionFork>,
|
pub fork: Option<SessionFork>,
|
||||||
pub workspace_root: Option<PathBuf>,
|
pub workspace_root: Option<PathBuf>,
|
||||||
pub prompt_history: Vec<SessionPromptEntry>,
|
pub prompt_history: Vec<SessionPromptEntry>,
|
||||||
|
/// The model used in this session, persisted so resumed sessions can
|
||||||
|
/// report which model was originally used.
|
||||||
|
pub model: Option<String>,
|
||||||
persistence: Option<SessionPersistence>,
|
persistence: Option<SessionPersistence>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,6 +164,7 @@ impl Session {
|
|||||||
fork: None,
|
fork: None,
|
||||||
workspace_root: None,
|
workspace_root: None,
|
||||||
prompt_history: Vec::new(),
|
prompt_history: Vec::new(),
|
||||||
|
model: None,
|
||||||
persistence: None,
|
persistence: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,6 +267,7 @@ impl Session {
|
|||||||
}),
|
}),
|
||||||
workspace_root: self.workspace_root.clone(),
|
workspace_root: self.workspace_root.clone(),
|
||||||
prompt_history: self.prompt_history.clone(),
|
prompt_history: self.prompt_history.clone(),
|
||||||
|
model: self.model.clone(),
|
||||||
persistence: None,
|
persistence: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -371,6 +376,10 @@ impl Session {
|
|||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
|
let model = object
|
||||||
|
.get("model")
|
||||||
|
.and_then(JsonValue::as_str)
|
||||||
|
.map(String::from);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
version,
|
version,
|
||||||
session_id,
|
session_id,
|
||||||
@@ -381,6 +390,7 @@ impl Session {
|
|||||||
fork,
|
fork,
|
||||||
workspace_root,
|
workspace_root,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
model,
|
||||||
persistence: None,
|
persistence: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -394,6 +404,7 @@ impl Session {
|
|||||||
let mut compaction = None;
|
let mut compaction = None;
|
||||||
let mut fork = None;
|
let mut fork = None;
|
||||||
let mut workspace_root = None;
|
let mut workspace_root = None;
|
||||||
|
let mut model = None;
|
||||||
let mut prompt_history = Vec::new();
|
let mut prompt_history = Vec::new();
|
||||||
|
|
||||||
for (line_number, raw_line) in contents.lines().enumerate() {
|
for (line_number, raw_line) in contents.lines().enumerate() {
|
||||||
@@ -433,6 +444,10 @@ impl Session {
|
|||||||
.get("workspace_root")
|
.get("workspace_root")
|
||||||
.and_then(JsonValue::as_str)
|
.and_then(JsonValue::as_str)
|
||||||
.map(PathBuf::from);
|
.map(PathBuf::from);
|
||||||
|
model = object
|
||||||
|
.get("model")
|
||||||
|
.and_then(JsonValue::as_str)
|
||||||
|
.map(String::from);
|
||||||
}
|
}
|
||||||
"message" => {
|
"message" => {
|
||||||
let message_value = object.get("message").ok_or_else(|| {
|
let message_value = object.get("message").ok_or_else(|| {
|
||||||
@@ -475,6 +490,7 @@ impl Session {
|
|||||||
fork,
|
fork,
|
||||||
workspace_root,
|
workspace_root,
|
||||||
prompt_history,
|
prompt_history,
|
||||||
|
model,
|
||||||
persistence: None,
|
persistence: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -580,6 +596,9 @@ impl Session {
|
|||||||
JsonValue::String(workspace_root_to_string(workspace_root)?),
|
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))
|
Ok(JsonValue::Object(object))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2789,7 +2789,7 @@ fn run_resume_command(
|
|||||||
Ok(ResumeCommandOutcome {
|
Ok(ResumeCommandOutcome {
|
||||||
session: session.clone(),
|
session: session.clone(),
|
||||||
message: Some(format_status_report(
|
message: Some(format_status_report(
|
||||||
"restored-session",
|
session.model.as_deref().unwrap_or("restored-session"),
|
||||||
StatusUsage {
|
StatusUsage {
|
||||||
message_count: session.messages.len(),
|
message_count: session.messages.len(),
|
||||||
turns: tracker.turns(),
|
turns: tracker.turns(),
|
||||||
@@ -2801,7 +2801,7 @@ fn run_resume_command(
|
|||||||
&context,
|
&context,
|
||||||
)),
|
)),
|
||||||
json: Some(status_json_value(
|
json: Some(status_json_value(
|
||||||
None,
|
session.model.as_deref(),
|
||||||
StatusUsage {
|
StatusUsage {
|
||||||
message_count: session.messages.len(),
|
message_count: session.messages.len(),
|
||||||
turns: tracker.turns(),
|
turns: tracker.turns(),
|
||||||
@@ -6752,7 +6752,7 @@ fn build_runtime(
|
|||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn build_runtime_with_plugin_state(
|
fn build_runtime_with_plugin_state(
|
||||||
session: Session,
|
mut session: Session,
|
||||||
session_id: &str,
|
session_id: &str,
|
||||||
model: String,
|
model: String,
|
||||||
system_prompt: Vec<String>,
|
system_prompt: Vec<String>,
|
||||||
@@ -6763,6 +6763,10 @@ fn build_runtime_with_plugin_state(
|
|||||||
progress_reporter: Option<InternalPromptProgressReporter>,
|
progress_reporter: Option<InternalPromptProgressReporter>,
|
||||||
runtime_plugin_state: RuntimePluginState,
|
runtime_plugin_state: RuntimePluginState,
|
||||||
) -> Result<BuiltRuntime, Box<dyn std::error::Error>> {
|
) -> Result<BuiltRuntime, Box<dyn std::error::Error>> {
|
||||||
|
// Persist the model in session metadata so resumed sessions can report it.
|
||||||
|
if session.model.is_none() {
|
||||||
|
session.model = Some(model.clone());
|
||||||
|
}
|
||||||
let RuntimePluginState {
|
let RuntimePluginState {
|
||||||
feature_config,
|
feature_config,
|
||||||
tool_registry,
|
tool_registry,
|
||||||
|
|||||||
Reference in New Issue
Block a user