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
@@ -160,6 +160,26 @@ export async function list(ctx: any) {
ctx.body = { sessions: filterPendingDeletedSessions(sessions) }
}
/**
* List Hermes sessions only (exclude api_server source)
* GET /api/hermes/sessions/hermes?source=&limit=
*/
export async function listHermesSessions(ctx: any) {
const source = (ctx.query.source as string) || undefined
const limit = ctx.query.limit ? parseInt(ctx.query.limit as string, 10) : undefined
try {
const sessions = await listSessionSummaries(source, limit && limit > 0 ? limit : 2000)
ctx.body = { sessions: filterPendingDeletedSessions(sessions.filter(s => s.source !== 'api_server' && s.source !== 'cron')) }
return
} catch (err) {
logger.warn(err, 'Hermes Session DB: summary query failed, falling back to CLI')
}
const sessions = await hermesCli.listSessions(source, limit)
ctx.body = { sessions: filterPendingDeletedSessions(sessions.filter(s => s.source !== 'api_server')) }
}
export async function search(ctx: any) {
if (useLocalSessionStore()) {
const q = typeof ctx.query.q === 'string' ? ctx.query.q : ''
@@ -207,6 +227,27 @@ export async function get(ctx: any) {
ctx.body = { session }
}
/**
* Get Hermes session detail only (exclude api_server source)
* GET /api/hermes/sessions/hermes/:id
*/
export async function getHermesSession(ctx: any) {
const session = await hermesCli.getSession(ctx.params.id)
if (!session) {
ctx.status = 404
ctx.body = { error: 'Session not found' }
return
}
// Filter out api_server sessions
if (session.source === 'api_server') {
ctx.status = 404
ctx.body = { error: 'Session not found' }
return
}
ctx.body = { session }
}
export async function remove(ctx: any) {
if (useLocalSessionStore()) {
const sessionId = ctx.params.id
@@ -7,6 +7,8 @@ sessionRoutes.get('/api/hermes/sessions/conversations', ctrl.listConversations)
sessionRoutes.get('/api/hermes/sessions/conversations/:id/messages', ctrl.getConversationMessages)
sessionRoutes.get('/api/hermes/sessions/conversations/:id/messages/paginated', ctrl.getConversationMessagesPaginated)
sessionRoutes.get('/api/hermes/sessions', ctrl.list)
sessionRoutes.get('/api/hermes/sessions/hermes', ctrl.listHermesSessions)
sessionRoutes.get('/api/hermes/sessions/hermes/:id', ctrl.getHermesSession)
sessionRoutes.get('/api/hermes/search/sessions', ctrl.search)
sessionRoutes.get('/api/hermes/sessions/search', ctrl.search)
sessionRoutes.get('/api/hermes/sessions/usage', ctrl.usageBatch)