Add /reload command to re-sync session state in place
Logging in mid-session shouldn't require recreating the session. /reload re-applies the active backend from current credentials (picking up a fresh /login token), retries a failed startup model load, and pushes refreshed config options + available commands so the editor's UI reflects current state — all without a new session. Also: - Correct the /login message: it no longer claims sign-in "applies to your next session" (the backend swaps in-session); it now points to /models. - /logout drops back to the local backend when leaving a cloud tier. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Seto Elkahfi committed
Jun 24, 2026 at 17:22 UTC
6bf9997f89dd9ddde22b0811e175ae8d9920ea5d
1 file changed
+83
-2
src/main.rs
+83
-2
@@ -46,7 +46,8 @@ use onde::inference::SamplingConfig;
46
47
use agent_client_protocol::schema::{
48
AgentCapabilities, AuthMethod, AuthMethodAgent, AuthenticateRequest, AuthenticateResponse,
49
- AvailableCommand, AvailableCommandInput, AvailableCommandsUpdate, UnstructuredCommandInput,
49
+ AvailableCommand, AvailableCommandInput, AvailableCommandsUpdate, ConfigOptionUpdate,
50
+ UnstructuredCommandInput,
51
CancelNotification, ContentBlock, ContentChunk, EmbeddedResourceResource, ForkSessionRequest,
52
ForkSessionResponse, Implementation, InitializeRequest, InitializeResponse, LoadSessionRequest,
53
LoadSessionResponse, Meta, NewSessionRequest, NewSessionResponse, PromptRequest,
@@ -596,6 +597,7 @@ impl SiGitAgent {
597
with_hint("login", "Sign in to siGit Code Cloud", "<email> <password>"),
598
AvailableCommand::new("logout", "Sign out of siGit Code Cloud"),
599
AvailableCommand::new("whoami", "Show the signed-in account"),
600
+ AvailableCommand::new("reload", "Re-sync sign-in and model state"),
601
AvailableCommand::new("clear", "Wipe the conversation history"),
602
AvailableCommand::new("status", "Show engine status"),
603
];
@@ -1214,6 +1216,70 @@ impl SiGitAgent {
1216
*self.backend.lock().await = local_backend;
1217
}
1218
1219
+ /// Re-attempt the lazy startup model load if the previous attempt failed.
1220
+ /// Clears the one-shot guard so the next load runs; a healthy load is left
1221
+ /// untouched so `/reload` doesn't needlessly reload a working model.
1222
+ fn retry_startup_model_load_if_failed(&self) {
1223
+ let had_error = self
1224
+ .model_load_error
1225
+ .lock()
1226
+ .map(|guard| guard.is_some())
1227
+ .unwrap_or(false);
1228
+ if had_error {
1229
+ self.startup_model_load_started
1230
+ .store(false, Ordering::Release);
1231
+ self.start_startup_model_load_if_needed();
1232
+ }
1233
+ }
1234
+
1235
+ /// Re-sync session state in place — no new session needed. Re-applies the
1236
+ /// active backend from current credentials (so a fresh `/login` token is
1237
+ /// picked up), retries a failed model load, and pushes refreshed commands +
1238
+ /// picker so the editor's UI reflects the current state.
1239
+ async fn handle_reload(&self, cx: &ConnectionTo<Client>, session_id: SessionId) {
1240
+ let signed_in = account::status_line().await;
1241
+
1242
+ let on_cloud_tier = {
1243
+ let guard = self.current_model.lock().unwrap();
1244
+ guard
1245
+ .model_id
1246
+ .strip_prefix("sigit-cloud:")
1247
+ .map(str::to_string)
1248
+ };
1249
+
1250
+ let backend_note = match on_cloud_tier {
1251
+ Some(tier) => match self.switch_to_cloud_tier(&tier).await {
1252
+ Some(name) => format!("Active: {name}."),
1253
+ None => {
1254
+ self.reset_to_local_backend().await;
1255
+ "Signed out — back to on-device. Pick a model with /models.".to_string()
1256
+ }
1257
+ },
1258
+ None => {
1259
+ self.reset_to_local_backend().await;
1260
+ self.retry_startup_model_load_if_failed();
1261
+ let guard = self.current_model.lock().unwrap();
1262
+ format!("Active: {}.", guard.display_name)
1263
+ }
1264
+ };
1265
+
1266
+ // Push refreshed picker + commands so the editor reflects current state.
1267
+ let config_options = {
1268
+ let guard = self.current_model.lock().unwrap();
1269
+ build_model_config_options(&guard)
1270
+ };
1271
+ self.send_tool_call_update(
1272
+ cx,
1273
+ session_id.clone(),
1274
+ SessionUpdate::ConfigOptionUpdate(ConfigOptionUpdate::new(config_options)),
1275
+ )
1276
+ .ok();
1277
+ self.advertise_commands(cx, session_id.clone());
1278
+
1279
+ self.send_assistant_message(cx, session_id, format!("Reloaded. {signed_in} {backend_note}"))
1280
+ .ok();
1281
+ }
1282
+
1283
async fn handle_set_session_config_option(
1284
&self,
1285
cx: &ConnectionTo<Client>,
@@ -1609,6 +1675,8 @@ enum SlashCommand {
1675
Login(Option<String>),
1676
Logout,
1677
Whoami,
1678
+ /// Re-sync session state (auth, backend, picker) without a new session.
1679
+ Reload,
1680
Exit,
1681
Unknown(String),
1682
}
@@ -1629,6 +1697,7 @@ fn parse_slash(input: &str) -> Option<SlashCommand> {
1697
"/login" => SlashCommand::Login(argument.map(str::to_string)),
1698
"/logout" => SlashCommand::Logout,
1699
"/whoami" => SlashCommand::Whoami,
1700
+ "/reload" => SlashCommand::Reload,
1701
"/exit" | "/quit" | "/q" => SlashCommand::Exit,
1702
other => SlashCommand::Unknown(other.to_string()),
1703
})
@@ -1722,6 +1791,7 @@ async fn exec_slash_acp(
1791
/login E P - sign in to siGit Code Cloud\n\
1792
/logout - sign out\n\
1793
/whoami - show the signed-in account\n\
1794
+ /reload - re-sync sign-in and model state\n\
1795
/clear - wipe conversation history\n\
1796
/status - show engine status\n\
1797
/exit - end this turn",
@@ -1858,7 +1928,7 @@ async fn exec_slash_acp(
1928
let message = match argument.as_deref().and_then(account::parse_login_args) {
1929
Some((email, password)) => match account::authenticate(&email, &password).await {
1930
Ok(email) => format!(
1861
- "Signed in as {email}. siGit Code Cloud applies to your next session."
1931
+ "Signed in as {email}. Pick a siGit Code Cloud tier in /models to use it."
1932
),
1933
Err(error) => format!("Login failed: {error}"),
1934
},
@@ -1867,13 +1937,24 @@ async fn exec_slash_acp(
1937
agent.send_assistant_message(cx, session_id, message).ok();
1938
}
1939
SlashCommand::Logout => {
1940
+ // If we're on a cloud tier, drop back to local — the token is gone.
1941
+ let on_cloud = {
1942
+ let guard = agent.current_model.lock().unwrap();
1943
+ guard.model_id.starts_with("sigit-cloud:")
1944
+ };
1945
let message = account::end_session().await;
1946
+ if on_cloud {
1947
+ agent.reset_to_local_backend().await;
1948
+ }
1949
agent.send_assistant_message(cx, session_id, message).ok();
1950
}
1951
SlashCommand::Whoami => {
1952
let message = account::status_line().await;
1953
agent.send_assistant_message(cx, session_id, message).ok();
1954
}
1955
+ SlashCommand::Reload => {
1956
+ agent.handle_reload(cx, session_id).await;
1957
+ }
1958
SlashCommand::Exit => {
1959
agent
1960
.send_assistant_message(