fix(desktop): session persistence — refresh/login/context/empty-content 4-bug fix
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled

1. App.tsx: add restoreSession() call on startup to prevent redirect
   to login page after refresh (isRestoring guard + BootstrapScreen)
2. CloneManager: call syncAgents() after loadClones() to restore
   currentAgent and conversation history on app load
3. zclaw-memory: add get_or_create_session() so frontend session UUID
   is persisted directly — kernel no longer creates mismatched IDs
4. openai.rs: assistant message content must be non-empty for
   Kimi/Qwen APIs — replace empty content with meaningful placeholders

Also includes admin-v2 ModelServices unified page (merge providers +
models + API keys into expandable row layout)
This commit is contained in:
iven
2026-03-31 13:38:59 +08:00
parent 3e5d64484e
commit 6cae768401
29 changed files with 1982 additions and 933 deletions

View File

@@ -173,6 +173,49 @@ impl MemoryStore {
Ok(session_id)
}
/// Get an existing session or create it with the given ID.
///
/// This is the critical bridge between frontend session IDs and the database.
/// The frontend generates a UUID (`sessionKey`) and sends it with each message.
/// Without this method, the kernel would create a *different* session ID on
/// every call, so conversation history would never be found.
pub async fn get_or_create_session(
&self,
session_id: &SessionId,
agent_id: &AgentId,
) -> Result<SessionId> {
let session_str = session_id.to_string();
let agent_str = agent_id.to_string();
// Check if session already exists
let exists: bool = sqlx::query_scalar(
"SELECT COUNT(*) > 0 FROM sessions WHERE id = ?",
)
.bind(&session_str)
.fetch_one(&self.pool)
.await
.unwrap_or(false);
if exists {
return Ok(session_id.clone());
}
// Create session with the frontend-provided ID
sqlx::query(
r#"
INSERT INTO sessions (id, agent_id, created_at, updated_at)
VALUES (?, ?, datetime('now'), datetime('now'))
"#,
)
.bind(&session_str)
.bind(&agent_str)
.execute(&self.pool)
.await
.map_err(|e| ZclawError::StorageError(e.to_string()))?;
Ok(session_id.clone())
}
/// Append a message to a session
pub async fn append_message(&self, session_id: &SessionId, message: &Message) -> Result<()> {
let session_str = session_id.to_string();