From 8196e49478ec1ddce8b7f116d2d0161176be6774 Mon Sep 17 00:00:00 2001 From: ekko <152005280+EKKOLearnAI@users.noreply.github.com> Date: Fri, 15 May 2026 15:52:16 +0800 Subject: [PATCH] [codex] Add group chat room reset and clone (#756) * Add group chat room reset and clone * Clean npm cache before self update --- bin/hermes-web-ui.mjs | 17 ++- packages/client/src/api/hermes/group-chat.ts | 14 ++ .../hermes/group-chat/GroupChatPanel.vue | 127 +++++++++++++++++- .../hermes/group-chat/GroupMessageList.vue | 20 ++- packages/client/src/i18n/locales/de.ts | 5 + packages/client/src/i18n/locales/en.ts | 5 + packages/client/src/i18n/locales/es.ts | 5 + packages/client/src/i18n/locales/fr.ts | 5 + packages/client/src/i18n/locales/ja.ts | 5 + packages/client/src/i18n/locales/ko.ts | 5 + packages/client/src/i18n/locales/pt.ts | 5 + packages/client/src/i18n/locales/zh-TW.ts | 5 + packages/client/src/i18n/locales/zh.ts | 5 + .../client/src/stores/hermes/group-chat.ts | 41 ++++++ packages/server/src/controllers/update.ts | 6 + .../server/src/routes/hermes/group-chat.ts | 84 ++++++++++++ .../hermes/group-chat/agent-clients.ts | 8 ++ .../src/services/hermes/group-chat/index.ts | 20 +++ 18 files changed, 373 insertions(+), 9 deletions(-) diff --git a/bin/hermes-web-ui.mjs b/bin/hermes-web-ui.mjs index f3f41e6..8f98223 100755 --- a/bin/hermes-web-ui.mjs +++ b/bin/hermes-web-ui.mjs @@ -447,7 +447,22 @@ Options: function doUpdate() { console.log(' ⬆ Updating hermes-web-ui...') - const child = spawnCli(getNpmBin(), ['install', '-g', 'hermes-web-ui@latest'], { + const npm = getNpmBin() + try { + console.log(' 🧹 Cleaning npm cache...') + execFileSync(npm, ['cache', 'clean', '--force'], { + stdio: 'inherit', + env: getCurrentNodeEnv(), + }) + } catch (err) { + console.log(` ⚠ Failed to clean npm cache, continuing update: ${err?.message || err}`) + } + + runUpdateInstall(npm) +} + +function runUpdateInstall(npm) { + const child = spawnCli(npm, ['install', '-g', 'hermes-web-ui@latest'], { stdio: 'inherit', windowsHide: true, env: getCurrentNodeEnv(), diff --git a/packages/client/src/api/hermes/group-chat.ts b/packages/client/src/api/hermes/group-chat.ts index 4ed1c77..20aefae 100644 --- a/packages/client/src/api/hermes/group-chat.ts +++ b/packages/client/src/api/hermes/group-chat.ts @@ -125,6 +125,14 @@ export async function createRoom(data: { }) } +export async function cloneRoom(roomId: string, data?: { name?: string; inviteCode?: string }): Promise<{ room: RoomInfo; agents: RoomAgent[] }> { + return request(`/api/hermes/group-chat/rooms/${roomId}/clone`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data || {}), + }) +} + export async function listRooms(): Promise<{ rooms: RoomInfo[] }> { return request('/api/hermes/group-chat/rooms') } @@ -174,6 +182,12 @@ export async function deleteRoom(roomId: string): Promise { }) } +export async function clearRoomContext(roomId: string): Promise<{ success: boolean; room: RoomInfo }> { + return request(`/api/hermes/group-chat/rooms/${roomId}/clear-context`, { + method: 'POST', + }) +} + export async function updateRoomConfig(roomId: string, config: { triggerTokens?: number; maxHistoryTokens?: number; tailMessageCount?: number }): Promise<{ room: RoomInfo }> { return request(`/api/hermes/group-chat/rooms/${roomId}/config`, { method: 'PUT', diff --git a/packages/client/src/components/hermes/group-chat/GroupChatPanel.vue b/packages/client/src/components/hermes/group-chat/GroupChatPanel.vue index 7d0fb15..2b40308 100644 --- a/packages/client/src/components/hermes/group-chat/GroupChatPanel.vue +++ b/packages/client/src/components/hermes/group-chat/GroupChatPanel.vue @@ -16,6 +16,7 @@ const profilesStore = useProfilesStore() const showSidebar = ref(window.innerWidth > 768) const showCreateModal = ref(false) +const showCloneModal = ref(false) const showAddAgentModal = ref(false) const showCompressionModal = ref(false) const compressionConfig = ref({ triggerTokens: 100000, maxHistoryTokens: 32000, tailMessageCount: 10 }) @@ -23,6 +24,9 @@ const isCompressing = ref(false) const selectedProfile = ref(null) const agentName = ref('') const agentDescription = ref('') +const cloneSourceRoomId = ref(null) +const cloneRoomName = ref('') +const cloneInviteCode = ref('') const profileOptions = computed(() => profilesStore.profiles.map(p => ({ label: p.name, value: p.name })) @@ -48,6 +52,15 @@ function toggleSidebar() { showSidebar.value = !showSidebar.value } +function generateCode(): string { + const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789' + let code = '' + for (let i = 0; i < 6; i++) { + code += chars[Math.floor(Math.random() * chars.length)] + } + return code +} + async function handleCreateRoom(name: string, inviteCode: string, userName: string, description: string, compression: { triggerTokens: number; maxHistoryTokens: number; tailMessageCount: number }) { try { store.setUserInfo(userName, description) @@ -69,6 +82,46 @@ async function handleDeleteRoom(roomId: string) { } } +function handleOpenCloneRoom(roomId: string) { + const room = store.rooms.find(r => r.id === roomId) + cloneSourceRoomId.value = roomId + cloneRoomName.value = room?.name ? `${room.name} Copy` : '' + cloneInviteCode.value = generateCode() + showCloneModal.value = true +} + +async function confirmCloneRoom() { + if (!cloneSourceRoomId.value || !cloneRoomName.value.trim()) return + try { + const res = await store.cloneRoom(cloneSourceRoomId.value, { + name: cloneRoomName.value.trim(), + inviteCode: cloneInviteCode.value.trim() || undefined, + }) + showCloneModal.value = false + cloneSourceRoomId.value = null + cloneRoomName.value = '' + cloneInviteCode.value = '' + await store.joinRoom(res.room.id) + message.success(t('groupChat.roomCloned')) + } catch { + message.error(t('common.saveFailed')) + } +} + +async function handleClearRoomContext() { + if (!store.currentRoomId) return + if (store.contextStatuses.size > 0) { + message.warning(t('groupChat.compressingInProgress')) + return + } + try { + await store.clearCurrentRoomContext() + message.success(t('groupChat.contextCleared')) + } catch { + message.error(t('common.deleteFailed')) + } +} + async function handleSelectRoom(roomId: string) { try { await store.joinRoom(roomId) @@ -204,9 +257,14 @@ watch(() => store.sortedMessages.length, async () => { {{ room.inviteCode }} {{ formatTokens(room.totalTokens || 0) }} + @@ -281,6 +339,16 @@ watch(() => store.sortedMessages.length, async () => { + + + {{ t('groupChat.clearContextConfirm') }} + {{ store.members.length }} {{ t('groupChat.members') }} @@ -370,6 +438,40 @@ watch(() => store.sortedMessages.length, async () => { +