[codex] Add group chat room reset and clone (#756)
* Add group chat room reset and clone * Clean npm cache before self update
This commit is contained in:
@@ -81,6 +81,12 @@ function getGlobalCliScript() {
|
||||
}
|
||||
|
||||
function runUpdateInstall() {
|
||||
try {
|
||||
runNpm(['cache', 'clean', '--force'], { timeout: 2 * 60 * 1000 })
|
||||
} catch (err) {
|
||||
console.warn('[update] failed to clean npm cache, continuing update:', err)
|
||||
}
|
||||
|
||||
return runNpm(['install', '-g', 'hermes-web-ui@latest'], { timeout: 10 * 60 * 1000 })
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,15 @@ function generateId(): string {
|
||||
return Date.now().toString(36) + Math.random().toString(36).slice(2, 8)
|
||||
}
|
||||
|
||||
function generateInviteCode(): string {
|
||||
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
|
||||
let code = ''
|
||||
for (let i = 0; i < 6; i++) {
|
||||
code += chars[Math.floor(Math.random() * chars.length)]
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// Create room
|
||||
groupChatRoutes.post('/api/hermes/group-chat/rooms', async (ctx) => {
|
||||
if (!chatServer) {
|
||||
@@ -65,6 +74,61 @@ groupChatRoutes.post('/api/hermes/group-chat/rooms', async (ctx) => {
|
||||
ctx.body = { room, agents: addedAgents }
|
||||
})
|
||||
|
||||
// Clone room roles/config without copying the conversation context.
|
||||
groupChatRoutes.post('/api/hermes/group-chat/rooms/:roomId/clone', async (ctx) => {
|
||||
if (!chatServer) {
|
||||
ctx.status = 503
|
||||
ctx.body = { error: 'Group chat not initialized' }
|
||||
return
|
||||
}
|
||||
|
||||
const sourceRoom = chatServer.getStorage().getRoom(ctx.params.roomId)
|
||||
if (!sourceRoom) {
|
||||
ctx.status = 404
|
||||
ctx.body = { error: 'Room not found' }
|
||||
return
|
||||
}
|
||||
|
||||
const { name, inviteCode } = ctx.request.body as { name?: string; inviteCode?: string }
|
||||
const roomId = generateId()
|
||||
const storage = chatServer.getStorage()
|
||||
const code = inviteCode?.trim() || generateInviteCode()
|
||||
storage.saveRoom(roomId, name?.trim() || `${sourceRoom.name} Copy`, code, {
|
||||
triggerTokens: sourceRoom.triggerTokens,
|
||||
maxHistoryTokens: sourceRoom.maxHistoryTokens,
|
||||
tailMessageCount: sourceRoom.tailMessageCount,
|
||||
})
|
||||
|
||||
const addedAgents = []
|
||||
for (const sourceAgent of storage.getRoomAgents(sourceRoom.id)) {
|
||||
const agentId = generateId()
|
||||
const agent = storage.addRoomAgent(
|
||||
roomId,
|
||||
agentId,
|
||||
sourceAgent.profile,
|
||||
sourceAgent.name,
|
||||
sourceAgent.description,
|
||||
sourceAgent.invited,
|
||||
)
|
||||
addedAgents.push(agent)
|
||||
|
||||
try {
|
||||
const client = await chatServer.agentClients.createAgent({
|
||||
profile: agent.profile,
|
||||
name: agent.name,
|
||||
description: agent.description,
|
||||
invited: agent.invited,
|
||||
})
|
||||
await chatServer.agentClients.addAgentToRoom(roomId, client)
|
||||
} catch (err: any) {
|
||||
console.error(`[GroupChat] Failed to connect cloned agent ${agent.profile} to room ${roomId}: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
const room = storage.getRoom(roomId)
|
||||
ctx.body = { room, agents: addedAgents }
|
||||
})
|
||||
|
||||
// Get room detail and messages
|
||||
groupChatRoutes.get('/api/hermes/group-chat/rooms/:roomId', async (ctx) => {
|
||||
if (!chatServer) {
|
||||
@@ -218,6 +282,26 @@ groupChatRoutes.delete('/api/hermes/group-chat/rooms/:roomId', async (ctx) => {
|
||||
ctx.body = { success: true }
|
||||
})
|
||||
|
||||
// Clear current room context while keeping members, agents, and room config.
|
||||
groupChatRoutes.post('/api/hermes/group-chat/rooms/:roomId/clear-context', async (ctx) => {
|
||||
if (!chatServer) {
|
||||
ctx.status = 503
|
||||
ctx.body = { error: 'Group chat not initialized' }
|
||||
return
|
||||
}
|
||||
|
||||
const roomId = ctx.params.roomId
|
||||
if (!chatServer.getStorage().getRoom(roomId)) {
|
||||
ctx.status = 404
|
||||
ctx.body = { error: 'Room not found' }
|
||||
return
|
||||
}
|
||||
|
||||
chatServer.getStorage().clearRoomContext(roomId)
|
||||
chatServer.clearRoomRuntimeState(roomId)
|
||||
ctx.body = { success: true, room: chatServer.getStorage().getRoom(roomId) }
|
||||
})
|
||||
|
||||
// Update room compression config
|
||||
groupChatRoutes.put('/api/hermes/group-chat/rooms/:roomId/config', async (ctx) => {
|
||||
if (!chatServer) {
|
||||
|
||||
@@ -574,6 +574,14 @@ export class AgentClients {
|
||||
}
|
||||
}
|
||||
|
||||
resetRoomContext(roomId: string): void {
|
||||
this._mentionQueue.delete(roomId)
|
||||
this._processingRooms.delete(roomId)
|
||||
if (this._contextEngine) {
|
||||
try { this._contextEngine.invalidateRoom(roomId) } catch { /* ignore */ }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect all agents in all rooms.
|
||||
*/
|
||||
|
||||
@@ -233,6 +233,14 @@ class ChatStorage {
|
||||
).run(msg.id, msg.roomId, msg.senderId, msg.senderName, msg.content, msg.timestamp)
|
||||
}
|
||||
|
||||
clearRoomContext(roomId: string): void {
|
||||
const db = this.db()
|
||||
if (!db) return
|
||||
db.prepare('DELETE FROM gc_messages WHERE roomId = ?').run(roomId)
|
||||
db.prepare('DELETE FROM gc_context_snapshots WHERE roomId = ?').run(roomId)
|
||||
db.prepare('UPDATE gc_rooms SET totalTokens = 0 WHERE id = ?').run(roomId)
|
||||
}
|
||||
|
||||
pruneMessages(roomId: string, keep = 500): void {
|
||||
const db = this.db()
|
||||
if (!db) return
|
||||
@@ -483,6 +491,18 @@ export class GroupChatServer {
|
||||
return Array.from(this.rooms.keys())
|
||||
}
|
||||
|
||||
clearRoomRuntimeState(roomId: string): void {
|
||||
const roomTyping = this.typingState.get(roomId)
|
||||
if (roomTyping) {
|
||||
for (const entry of roomTyping.values()) clearTimeout(entry.timer)
|
||||
this.typingState.delete(roomId)
|
||||
}
|
||||
this.contextStatusState.delete(roomId)
|
||||
this.agentClients.resetRoomContext(roomId)
|
||||
this.nsp.to(roomId).emit('room_cleared', { roomId, totalTokens: 0 })
|
||||
this.nsp.to(roomId).emit('room_updated', { roomId, totalTokens: 0 })
|
||||
}
|
||||
|
||||
// ─── Restore Agents ─────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user