feat: add History page for browsing Hermes sessions (v0.5.5) (#370)

Features:
- Add dedicated History page for browsing Hermes session history
- Independent session state (does not interfere with active chat)
- Auto-select first CLI session on page load
- Filter out api_server and cron sources

Components:
- New HistoryView.vue with isolated state management
- New HistoryMessageList.vue with session prop support
- Filters empty content and tool messages without toolName

Backend:
- Add GET /api/hermes/sessions/hermes endpoint (excludes api_server)
- Add GET /api/hermes/sessions/hermes/:id endpoint (404s for api_server)
- Add fetchHermesSessions() and fetchHermesSession() API functions

Cleanup:
- Remove localStorage session caching
- Simplify profile switching cache management
- Clean up废弃 cache cleanup calls

i18n:
- Add "History" translation to all 8 locales
- Add v0.5.5 changelog entries in all languages
- 🎉 Happy Labor Day!

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ekko
2026-05-01 11:27:43 +08:00
committed by GitHub
parent e2f35d3caf
commit 3ba76ad19b
19 changed files with 1473 additions and 47 deletions
+20 -6
View File
@@ -215,14 +215,13 @@ function isQuotaExceededError(error: unknown): boolean {
function recoverStorageQuota() {
try {
// 清理所有会话相关的旧缓存(已完全废弃)
const prefixes = [
`hermes_session_msgs_v1_${getProfileName()}_`,
`hermes_in_flight_v1_${getProfileName()}_`,
'hermes_sessions_cache_v1_',
'hermes_session_msgs_v1_',
'hermes_session_pins_v1_',
'hermes_human_only_v1_',
]
if (getProfileName() === 'default') {
prefixes.push('hermes_session_msgs_v1_')
prefixes.push('hermes_in_flight_v1_')
}
const keysToRemove: string[] = []
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
@@ -233,6 +232,9 @@ function recoverStorageQuota() {
}
}
keysToRemove.forEach(key => removeItem(key))
if (keysToRemove.length > 0) {
console.log(`Recovered storage: cleared ${keysToRemove.length} old session cache entries`)
}
} catch {
// ignore
}
@@ -538,6 +540,17 @@ export const useChatStore = defineStore('chat', () => {
if (s) s.messages.push(msg)
}
function addOrUpdateSession(session: Session) {
const existingIndex = sessions.value.findIndex(s => s.id === session.id)
if (existingIndex !== -1) {
// Update existing session
sessions.value[existingIndex] = session
} else {
// Add new session
sessions.value.push(session)
}
}
function updateMessage(sessionId: string, id: string, update: Partial<Message>) {
const s = sessions.value.find(s => s.id === sessionId)
if (!s) return
@@ -1346,6 +1359,7 @@ export const useChatStore = defineStore('chat', () => {
newChat,
switchSession,
switchSessionModel,
addOrUpdateSession,
clearProviderFromSessions,
deleteSession,
sendMessage,
+7 -20
View File
@@ -24,6 +24,8 @@ export const useProfilesStore = defineStore('profiles', () => {
activeProfileName.value = activeProfile.value.name
localStorage.setItem(ACTIVE_PROFILE_STORAGE_KEY, activeProfile.value.name)
}
// 清理所有会话缓存(不再使用 localStorage 缓存)
clearAllSessionCaches()
} catch (err) {
console.error('Failed to fetch profiles:', err)
} finally {
@@ -52,31 +54,15 @@ export const useProfilesStore = defineStore('profiles', () => {
const ok = await profilesApi.deleteProfile(name)
if (ok) {
delete detailMap.value[name]
// 清理该 profile 的 localStorage 缓存
clearProfileCache(name)
await fetchProfiles()
}
return ok
}
// 清理指定 profile 的所有 localStorage 缓存(精确匹配缓存 key 前缀)
function clearProfileCache(profileName: string) {
const prefixes = [
`hermes_sessions_cache_v1_${profileName}`,
`hermes_session_msgs_v1_${profileName}_`,
`hermes_in_flight_v1_${profileName}_`,
`hermes_active_session_${profileName}`,
`hermes_session_pins_v1_${profileName}`,
`hermes_human_only_v1_${profileName}`,
]
const keysToRemove: string[] = []
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i)
if (key && prefixes.some(p => key.startsWith(p))) {
keysToRemove.push(key)
}
}
keysToRemove.forEach(key => localStorage.removeItem(key))
// 清理所有 profile 的会话缓存
function clearAllSessionCaches() {
// 注意:不再清理任何缓存,因为已经不再使用 localStorage 缓存会话数据
// 所有会话数据都从服务器实时获取
}
async function renameProfile(name: string, newName: string) {
@@ -124,5 +110,6 @@ export const useProfilesStore = defineStore('profiles', () => {
switchProfile,
exportProfile,
importProfile,
clearAllSessionCaches,
}
})