From 173307ef28d897ae8747e68c98b85ea9e1ee47dd Mon Sep 17 00:00:00 2001 From: ekko <152005280+EKKOLearnAI@users.noreply.github.com> Date: Thu, 7 May 2026 13:49:57 +0800 Subject: [PATCH] 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 --- packages/client/src/api/hermes/sessions.ts | 20 ++- .../src/components/hermes/chat/ChatPanel.vue | 45 +++++- .../hermes/chat/SessionListItem.vue | 49 +++++- packages/client/src/i18n/locales/de.ts | 6 + packages/client/src/i18n/locales/en.ts | 6 + packages/client/src/i18n/locales/es.ts | 6 + packages/client/src/i18n/locales/fr.ts | 6 + packages/client/src/i18n/locales/ja.ts | 6 + packages/client/src/i18n/locales/ko.ts | 6 + packages/client/src/i18n/locales/pt.ts | 6 + packages/client/src/i18n/locales/zh.ts | 6 + .../client/src/views/hermes/HistoryView.vue | 45 +++++- .../server/src/controllers/hermes/sessions.ts | 86 ++++++++++ .../context-compressor/export-compressor.ts | 149 ++++++++++++++++++ .../src/lib/context-compressor/index.ts | 18 ++- packages/server/src/routes/hermes/sessions.ts | 1 + tests/server/sessions-controller.test.ts | 93 +++++++++++ tests/server/sessions-routes.test.ts | 14 ++ 18 files changed, 554 insertions(+), 14 deletions(-) create mode 100644 packages/server/src/lib/context-compressor/export-compressor.ts diff --git a/packages/client/src/api/hermes/sessions.ts b/packages/client/src/api/hermes/sessions.ts index ffb27d1..95ef7ce 100644 --- a/packages/client/src/api/hermes/sessions.ts +++ b/packages/client/src/api/hermes/sessions.ts @@ -1,4 +1,4 @@ -import { request } from '../client' +import { request, getApiKey, getBaseUrlValue } from '../client' export interface SessionSummary { id: string @@ -147,6 +147,24 @@ export async function setSessionWorkspace(id: string, workspace: string | null): } } +export async function exportSession(id: string, mode: 'full' | 'compressed' = 'full', ext: 'json' | 'txt' = 'json'): Promise { + const baseUrl = getBaseUrlValue() + const token = getApiKey() + const url = `${baseUrl}/api/hermes/sessions/${id}/export?mode=${mode}&ext=${ext}&token=${encodeURIComponent(token)}` + const res = await fetch(url) + if (!res.ok) throw new Error('Export failed') + const blob = await res.blob() + const contentDisposition = res.headers.get('Content-Disposition') || '' + let filename = `session_${id}.${ext}` + const match = contentDisposition.match(/filename\*?=(?:UTF-8'')?([^;\n]+)/i) + if (match) filename = decodeURIComponent(match[1].replace(/"/g, '')) + const a = document.createElement('a') + a.href = URL.createObjectURL(blob) + a.download = filename + a.click() + URL.revokeObjectURL(a.href) +} + export interface UsageStatsResponse { total_input_tokens: number total_output_tokens: number diff --git a/packages/client/src/components/hermes/chat/ChatPanel.vue b/packages/client/src/components/hermes/chat/ChatPanel.vue index afd71f4..783c3ef 100644 --- a/packages/client/src/components/hermes/chat/ChatPanel.vue +++ b/packages/client/src/components/hermes/chat/ChatPanel.vue @@ -1,5 +1,5 @@