feat: add session export with full and compressed modes (#507)

Add export functionality that allows users to download session data
as JSON or plain text, with optional LLM-based context compression
for long conversations. Includes UI controls in chat panel, session
list, and history view, plus i18n strings for all 8 locales.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
ekko
2026-05-07 13:49:57 +08:00
committed by GitHub
parent c0ad8c907b
commit 173307ef28
18 changed files with 554 additions and 14 deletions
+14
View File
@@ -17,6 +17,7 @@ const usageSingleMock = vi.fn(async (ctx: any) => { ctx.body = { input_tokens: 0
const usageStatsMock = vi.fn(async (ctx: any) => { ctx.body = { total_input_tokens: 0, total_output_tokens: 0 } })
const contextLengthMock = vi.fn(async (ctx: any) => { ctx.body = { context_length: 200000 } })
const batchRemoveMock = vi.fn(async (ctx: any) => { ctx.body = { deleted: 1, failed: 0, errors: [] } })
const exportSessionMock = vi.fn(async (ctx: any) => { ctx.body = JSON.stringify({ id: ctx.params.id }) })
vi.mock('../../packages/server/src/controllers/hermes/sessions', () => ({
listConversations: listConversationsMock,
@@ -36,6 +37,7 @@ vi.mock('../../packages/server/src/controllers/hermes/sessions', () => ({
usageSingle: usageSingleMock,
usageStats: usageStatsMock,
contextLength: contextLengthMock,
exportSession: exportSessionMock,
}))
describe('session routes', () => {
@@ -66,6 +68,7 @@ describe('session routes', () => {
'/api/hermes/usage/stats',
'/api/hermes/sessions/context-length',
'/api/hermes/sessions/:id',
'/api/hermes/sessions/:id/export',
'/api/hermes/sessions/:id/usage',
'/api/hermes/sessions/:id/rename',
]))
@@ -110,4 +113,15 @@ describe('session routes', () => {
expect(getConversationMessagesMock).toHaveBeenCalledWith(detailCtx)
expect(detailCtx.body).toEqual({ session_id: 'child-session', messages: [] })
})
it('delegates session export to the controller', async () => {
const { sessionRoutes } = await import('../../packages/server/src/routes/hermes/sessions')
const layer = sessionRoutes.stack.find((entry: any) => entry.path === '/api/hermes/sessions/:id/export')
const handler = layer.stack[0]
const ctx: any = { params: { id: 'session-abc' }, query: {}, body: null, set: vi.fn() }
await handler(ctx)
expect(exportSessionMock).toHaveBeenCalledWith(ctx)
})
})