fix(history): resolve session resume profile scoping and history pagination (#1040)

This commit is contained in:
M57 authored and GitHub committed 2026-09-02 00:01:19 +03:00
1 parent 7c00f4407d
commit 2d0b6ae1a9
5 files changed
+64 -10

No files matched your search

@@ -231,7 +231,7 @@ fun ChatScreen(
.collectLatest { firstVisibleIndex ->
if (firstVisibleIndex > 2) {
isOlderPagingArmed = true
} else if (isOlderPagingArmed) {
} else if (isOlderPagingArmed || state.messages.size <= 3) {
val anchorId = state.messages.getOrNull(firstVisibleIndex)?.id
val offset = scrollController.captureAnchorOffset()
pagingAnchor.value = if (anchorId != null) anchorId to offset else null
@@ -647,6 +647,8 @@ class ChatViewModel(
viewModelScope.launch {
ProfileSwitchCoordinator.switched
.collect { _ ->
pendingGoneSessionNotice = false
sessionHasServerPresence = false
resetSessionState(sessionId = null, title = "Hermes", isLoading = true)
}
}
@@ -657,6 +659,8 @@ class ChatViewModel(
viewModelScope.launch {
ProfileSwitchCoordinator.connectionSwitched
.collect { _ ->
pendingGoneSessionNotice = false
sessionHasServerPresence = false
resetSessionState(sessionId = null, title = "Hermes", isLoading = true)
}
}
@@ -2469,6 +2473,27 @@ class ChatViewModel(
}
is NetworkResult.Failure -> {
val errorMsg = result.error.message
val is404 =
errorMsg.contains("404", ignoreCase = true) ||
errorMsg.contains("not found", ignoreCase = true)
if (is404) {
// A 404 from GET /api/sessions/{id}/messages indicates the session has no
// persisted rows in state.db (e.g. newly created/lazy unprompted session, or empty history).
// It is NOT a fatal error: keep the session open with empty transcript, clear loading,
// and mark hydrated cleanly.
_uiState.update {
if (!isCurrentHydration(sessionId, generation, requestSequence)) return@update it
it.copy(
isLoading = false,
isLoadingOlder = false,
hasOlderMessages = false,
)
}
hydratedGeneration = generation
finishResumeWhenHydrated(generation)
return@launch
}
if (hydratedGeneration == generation) hydratedGeneration = -1L
_uiState.update {
if (!isCurrentHydration(sessionId, generation, requestSequence)) return@update it
@@ -2576,10 +2601,19 @@ class ChatViewModel(
) {
val requestSequence = ++resumeRequestSequence
activeResumeRequestSequence = requestSequence
val profile = AuthManager.activeProfileId.value
val params =
mutableMapOf<String, Any>(
"session_id" to sessionId,
"omit_messages" to true,
)
if (!profile.isNullOrBlank()) {
params["profile"] = profile
}
viewModelScope.launch(ioDispatcher) {
wsClient.send(
WsMethods.SESSION_RESUME,
mapOf("session_id" to sessionId, "omit_messages" to true),
params,
onSent = { id ->
trackSessionRequest(
id = id,
@@ -2653,6 +2687,17 @@ class ChatViewModel(
private fun recoverGoneSession(sessionId: String) {
if (_uiState.value.currentSessionId != sessionId) return
if (sessionGoneRecoveryInFlight) return
if (_uiState.value.messages.isNotEmpty()) {
cancelResumeRetry()
_uiState.update {
it.copy(
isLoading = false,
isResumeRetrying = false,
resumeError = "Session not found on server (displaying cached messages)",
)
}
return
}
sessionGoneRecoveryInFlight = true
cancelResumeRetry()
_uiState.update {