hermes-web-ui 0.3.6 → 0.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +21 -2
  2. package/bin/hermes-web-ui.mjs +15 -1
  3. package/dist/client/assets/{Add-CKf6ViXR.js → Add-s316k4Av.js} +1 -1
  4. package/dist/client/assets/{Button-CrrCCorI.js → Button-BkA_RI8a.js} +1 -1
  5. package/dist/client/assets/{ChannelsView-D4I7hhZO.js → ChannelsView-DPmPn8DW.js} +1 -1
  6. package/dist/client/assets/ChatView-DEtziOPB.js +127 -0
  7. package/dist/client/assets/{ChatView-Vfi_jEpI.css → ChatView-DI3XN8vz.css} +1 -1
  8. package/dist/client/assets/{Close-C9xwy-pW.js → Close-CrLD0IXG.js} +1 -1
  9. package/dist/client/assets/{FormItem-BgJdrTW0.js → FormItem-CQLdFrl9.js} +1 -1
  10. package/dist/client/assets/{GatewaysView-Cib2JydO.js → GatewaysView-CC1Y0tZZ.js} +1 -1
  11. package/dist/client/assets/{Input-ChENEW-Z.js → Input-nXKlujwJ.js} +1 -1
  12. package/dist/client/assets/{InputNumber-Xd6HWSdp.js → InputNumber-DLZwwIyX.js} +1 -1
  13. package/dist/client/assets/{JobsView-SnToCbDd.js → JobsView-BC0bBrJO.js} +2 -2
  14. package/dist/client/assets/{LoginView-BZdmMnsf.js → LoginView-PqpFR9bV.js} +1 -1
  15. package/dist/client/assets/{LogsView-DblvOJIg.js → LogsView-DtR88N0b.js} +1 -1
  16. package/dist/client/assets/{MarkdownRenderer-DJLVk7ei.js → MarkdownRenderer-BSLfTurm.js} +1 -1
  17. package/dist/client/assets/{MemoryView-exXvRwCc.js → MemoryView-CJRWnePL.js} +1 -1
  18. package/dist/client/assets/{Modal-B2zvXTrk.js → Modal-Bp9RK8LZ.js} +1 -1
  19. package/dist/client/assets/ModelsView-7Obe34Cz.js +1 -0
  20. package/dist/client/assets/{ModelsView-jbgZP3YF.css → ModelsView-VM-oMq5M.css} +1 -1
  21. package/dist/client/assets/{Popconfirm-BoZc0kKk.js → Popconfirm-DTdUi7r_.js} +1 -1
  22. package/dist/client/assets/{Popover-Cu52vG3D.js → Popover-_M3o0B7L.js} +1 -1
  23. package/dist/client/assets/{ProfilesView-D0FY7Jwe.js → ProfilesView-1_GmRx-S.js} +1 -1
  24. package/dist/client/assets/{Select-BHc7u-Yf.js → Select-aHPR3urY.js} +2 -2
  25. package/dist/client/assets/{SettingRow-i-UXlco7.js → SettingRow-DKasLuS5.js} +1 -1
  26. package/dist/client/assets/{SettingsView-Dhr2wzAB.css → SettingsView-C3sd8K0e.css} +1 -1
  27. package/dist/client/assets/SettingsView-DZCA7_CM.js +352 -0
  28. package/dist/client/assets/{SkillsView-B5QBaAzi.js → SkillsView-Dk7O05cK.js} +1 -1
  29. package/dist/client/assets/{Spin-DsNCRPk9.js → Spin-Bt_9cTiO.js} +1 -1
  30. package/dist/client/assets/{Suffix-3xK0KZGt.js → Suffix-XaH8SDbR.js} +1 -1
  31. package/dist/client/assets/{Switch-Bf63XXgA.js → Switch-D1_psmjT.js} +1 -1
  32. package/dist/client/assets/{Tag-Dmbj68Ki.js → Tag-3FaOhoJN.js} +1 -1
  33. package/dist/client/assets/{TerminalView-DrJHZ0qI.js → TerminalView-DNU7oQxK.js} +1 -1
  34. package/dist/client/assets/{Tooltip-CRbZNhG0.js → Tooltip-YHrHWGPa.js} +1 -1
  35. package/dist/client/assets/{UsageView-DQ43JasX.js → UsageView-COCrOiiV.js} +1 -1
  36. package/dist/client/assets/{Warning-kBbRMAif.js → Warning-B6CM9aBl.js} +1 -1
  37. package/dist/client/assets/{_plugin-vue_export-helper-CnosYBkx.js → _plugin-vue_export-helper-BGG8ORDx.js} +1 -1
  38. package/dist/client/assets/app-B7ktf7Fh.js +1 -0
  39. package/dist/client/assets/app-BPvTl2-V.js +1 -0
  40. package/dist/client/assets/{browser-Djp4tkp3.js → browser-f5W8abIG.js} +1 -1
  41. package/dist/client/assets/chat-6q6pkzEW.js +6 -0
  42. package/dist/client/assets/composables-xV7dhNpf.js +1 -0
  43. package/dist/client/assets/{fade-in.cssr-CIVyTG6A.js → fade-in.cssr-ifHK7yH1.js} +1 -1
  44. package/dist/client/assets/index-CSCYx7ux.js +284 -0
  45. package/dist/client/assets/{jobs-CcVaCGMJ.js → jobs-DObWfhbO.js} +1 -1
  46. package/dist/client/assets/{light-D9G2GshF.js → light-CxjyoF0s.js} +1 -1
  47. package/dist/client/assets/{light-KCEDTUGE.js → light-D1yfed_s.js} +1 -1
  48. package/dist/client/assets/{light-BPqyaxve.js → light-DWy-mwyK.js} +1 -1
  49. package/dist/client/assets/{light-CSp9-LhE.js → light-D_3MwJj1.js} +1 -1
  50. package/dist/client/assets/{light-BF6E9z0k.js → light-DgLcPjgU.js} +1 -1
  51. package/dist/client/assets/{light-BJ96fCLC.js → light-TGFKT-UB.js} +1 -1
  52. package/dist/client/assets/models-DQ4CT-vv.js +1 -0
  53. package/dist/client/assets/{pinia-iHE5_ZXa.js → pinia-DcAkZ8vx.js} +1 -1
  54. package/dist/client/assets/{profiles-CJCR84uQ.js → profiles-DzkigJwq.js} +1 -1
  55. package/dist/client/assets/{router-C-NNJUuf.js → router-D8sJ39Io.js} +2 -2
  56. package/dist/client/assets/{sessions-C4bnNvzS.js → sessions-Dg8n9PBo.js} +1 -1
  57. package/dist/client/assets/{skills-B4slZfeZ.js → skills-BehzdECn.js} +1 -1
  58. package/dist/client/assets/{use-message-BIpqgDet.js → use-message-DBz2JSTt.js} +1 -1
  59. package/dist/client/assets/{useTheme-B78N9tyz.js → useTheme-UdVT814n.js} +1 -1
  60. package/dist/client/index.html +27 -27
  61. package/dist/server/index.js +8 -3
  62. package/dist/server/routes/hermes/filesystem.js +231 -199
  63. package/dist/server/routes/hermes/group-chat.d.ts +2 -0
  64. package/dist/server/routes/hermes/group-chat.js +135 -0
  65. package/dist/server/routes/hermes/logs.js +50 -11
  66. package/dist/server/routes/hermes/proxy-handler.js +16 -6
  67. package/dist/server/routes/upload.js +41 -11
  68. package/dist/server/services/group-chat/coordinator.d.ts +14 -0
  69. package/dist/server/services/group-chat/coordinator.js +230 -0
  70. package/dist/server/services/group-chat/index.d.ts +5 -0
  71. package/dist/server/services/group-chat/index.js +115 -0
  72. package/dist/server/services/group-chat/rooms-db.d.ts +56 -0
  73. package/dist/server/services/group-chat/rooms-db.js +199 -0
  74. package/dist/server/services/hermes/gateway-manager.d.ts +2 -0
  75. package/dist/server/services/hermes/gateway-manager.js +15 -0
  76. package/dist/server/services/hermes/hermes-profile.d.ts +6 -0
  77. package/dist/server/services/hermes/hermes-profile.js +12 -0
  78. package/dist/server/shared/providers.js +1 -13
  79. package/package.json +1 -1
  80. package/dist/client/assets/ChatView-DxyBUK57.js +0 -127
  81. package/dist/client/assets/ModelsView-DGs47Cj4.js +0 -1
  82. package/dist/client/assets/SettingsView-BW6ctYG5.js +0 -352
  83. package/dist/client/assets/app-BT9yU6N6.js +0 -1
  84. package/dist/client/assets/app-CjNVVG5x.js +0 -1
  85. package/dist/client/assets/chat-DlC9S9DK.js +0 -6
  86. package/dist/client/assets/composables-DCA4Yga5.js +0 -1
  87. package/dist/client/assets/index-D12ukDT7.js +0 -284
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.setupGroupChatSocketIO = setupGroupChatSocketIO;
37
+ exports.getGroupChatIO = getGroupChatIO;
38
+ exports.disconnectGroupChatIO = disconnectGroupChatIO;
39
+ const socket_io_1 = require("socket.io");
40
+ const auth_1 = require("../auth");
41
+ const roomsDb = __importStar(require("./rooms-db"));
42
+ const coordinator = __importStar(require("./coordinator"));
43
+ let io = null;
44
+ function setupGroupChatSocketIO(httpServer) {
45
+ io = new socket_io_1.Server(httpServer, {
46
+ path: '/socket.io/group-chat',
47
+ cors: { origin: '*' },
48
+ });
49
+ // Auth middleware
50
+ io.use(async (socket, next) => {
51
+ const authToken = await (0, auth_1.getToken)();
52
+ if (authToken) {
53
+ const token = socket.handshake.auth.token || socket.handshake.query.token;
54
+ if (token !== authToken) {
55
+ return next(new Error('Unauthorized'));
56
+ }
57
+ }
58
+ next();
59
+ });
60
+ io.on('connection', (socket) => {
61
+ let currentRoom = null;
62
+ socket.on('room:join', async (roomId) => {
63
+ if (currentRoom) {
64
+ socket.leave(currentRoom);
65
+ }
66
+ currentRoom = roomId;
67
+ socket.join(roomId);
68
+ const messages = roomsDb.getRoomMessages(roomId, 100);
69
+ const agents = roomsDb.getRoomAgents(roomId);
70
+ socket.emit('room:history', { roomId, messages, agents });
71
+ });
72
+ socket.on('room:leave', (roomId) => {
73
+ socket.leave(roomId);
74
+ if (currentRoom === roomId)
75
+ currentRoom = null;
76
+ });
77
+ socket.on('room:message', async (data) => {
78
+ if (!currentRoom || currentRoom !== data.roomId)
79
+ return;
80
+ if (!data.content?.trim())
81
+ return;
82
+ const { roomId, content } = data;
83
+ // Process via coordinator (stores user message internally)
84
+ await coordinator.processMessage(roomId, content, {
85
+ onDelta(rId, agentName, text) {
86
+ io.to(rId).emit('message:delta', { roomId: rId, agentName, text });
87
+ },
88
+ onAgentStatus(rId, agentName, status) {
89
+ io.to(rId).emit('agent:status', { roomId: rId, agentName, status });
90
+ },
91
+ onAgentComplete(rId, agentName, message) {
92
+ io.to(rId).emit('room:message', { roomId: rId, message });
93
+ },
94
+ onAgentError(rId, agentName, error) {
95
+ io.to(rId).emit('agent:status', { roomId: rId, agentName, status: 'error', error });
96
+ },
97
+ });
98
+ });
99
+ socket.on('disconnect', () => {
100
+ if (currentRoom) {
101
+ socket.leave(currentRoom);
102
+ }
103
+ });
104
+ });
105
+ return io;
106
+ }
107
+ function getGroupChatIO() {
108
+ return io;
109
+ }
110
+ function disconnectGroupChatIO() {
111
+ if (io) {
112
+ io.disconnectSockets();
113
+ io = null;
114
+ }
115
+ }
@@ -0,0 +1,56 @@
1
+ export interface Room {
2
+ id: string;
3
+ name: string;
4
+ settings: string;
5
+ created_at: number;
6
+ updated_at: number;
7
+ }
8
+ export interface RoomAgent {
9
+ room_id: string;
10
+ agent_name: string;
11
+ profile: string;
12
+ session_id: string;
13
+ role_prompt: string;
14
+ }
15
+ export interface RoomMessage {
16
+ id: string;
17
+ room_id: string;
18
+ role: 'user' | 'assistant' | 'system';
19
+ agent_name: string;
20
+ content: string;
21
+ mentions: string;
22
+ round: number;
23
+ created_at: number;
24
+ }
25
+ export interface NewRoomMessage {
26
+ room_id: string;
27
+ role: string;
28
+ agent_name?: string;
29
+ content: string;
30
+ mentions?: string[];
31
+ round?: number;
32
+ }
33
+ export declare function createRoom(name: string): Room;
34
+ export declare function listRooms(): Room[];
35
+ export declare function getRoom(roomId: string): Room | null;
36
+ export declare function updateRoom(roomId: string, updates: Partial<Pick<Room, 'name' | 'settings'>>): boolean;
37
+ export declare function deleteRoom(roomId: string): boolean;
38
+ export declare function addRoomAgent(roomId: string, agent: {
39
+ agent_name: string;
40
+ profile: string;
41
+ role_prompt?: string;
42
+ }): RoomAgent | null;
43
+ export declare function removeRoomAgent(roomId: string, agentName: string): boolean;
44
+ export declare function getRoomAgents(roomId: string): RoomAgent[];
45
+ export declare function updateRoomAgentSession(roomId: string, agentName: string, sessionId: string): void;
46
+ export declare function addMessage(msg: NewRoomMessage): RoomMessage;
47
+ export declare function getRoomMessages(roomId: string, limit?: number, offset?: number): RoomMessage[];
48
+ export declare function getLatestRound(roomId: string): number;
49
+ /**
50
+ * Get recent context messages relevant to a specific agent.
51
+ * Returns messages where:
52
+ * - mentions is empty (public message, no @mentions)
53
+ * - mentions includes agentName
54
+ * - message is FROM this agent
55
+ */
56
+ export declare function getRecentContext(roomId: string, agentName: string, limit?: number): RoomMessage[];
@@ -0,0 +1,199 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRoom = createRoom;
4
+ exports.listRooms = listRooms;
5
+ exports.getRoom = getRoom;
6
+ exports.updateRoom = updateRoom;
7
+ exports.deleteRoom = deleteRoom;
8
+ exports.addRoomAgent = addRoomAgent;
9
+ exports.removeRoomAgent = removeRoomAgent;
10
+ exports.getRoomAgents = getRoomAgents;
11
+ exports.updateRoomAgentSession = updateRoomAgentSession;
12
+ exports.addMessage = addMessage;
13
+ exports.getRoomMessages = getRoomMessages;
14
+ exports.getLatestRound = getLatestRound;
15
+ exports.getRecentContext = getRecentContext;
16
+ const os_1 = require("os");
17
+ const path_1 = require("path");
18
+ // ---------------------------------------------------------------------------
19
+ // Database singleton
20
+ // ---------------------------------------------------------------------------
21
+ const SQLITE_AVAILABLE = (() => {
22
+ const [major, minor] = process.versions.node.split('.').map(Number);
23
+ return major > 22 || (major === 22 && minor >= 5);
24
+ })();
25
+ function dbPath() {
26
+ return (0, path_1.join)((0, os_1.homedir)(), '.hermes-web-ui', 'rooms.db');
27
+ }
28
+ let _db = null;
29
+ function getDb() {
30
+ if (_db)
31
+ return _db;
32
+ if (!SQLITE_AVAILABLE) {
33
+ throw new Error('Group chat requires Node.js >= 22.5 for SQLite support');
34
+ }
35
+ const { DatabaseSync } = require('node:sqlite');
36
+ const dir = (0, path_1.join)((0, os_1.homedir)(), '.hermes-web-ui');
37
+ const { mkdirSync } = require('fs');
38
+ mkdirSync(dir, { recursive: true });
39
+ _db = new DatabaseSync(dbPath(), { open: true });
40
+ _db.exec('PRAGMA journal_mode=WAL');
41
+ _db.exec('PRAGMA foreign_keys=ON');
42
+ initSchema(_db);
43
+ return _db;
44
+ }
45
+ function initSchema(db) {
46
+ db.exec(`
47
+ CREATE TABLE IF NOT EXISTS rooms (
48
+ id TEXT PRIMARY KEY,
49
+ name TEXT NOT NULL,
50
+ settings TEXT DEFAULT '{}',
51
+ created_at INTEGER NOT NULL,
52
+ updated_at INTEGER NOT NULL
53
+ );
54
+
55
+ CREATE TABLE IF NOT EXISTS room_agents (
56
+ room_id TEXT NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
57
+ agent_name TEXT NOT NULL,
58
+ profile TEXT NOT NULL,
59
+ session_id TEXT DEFAULT '',
60
+ role_prompt TEXT DEFAULT '',
61
+ PRIMARY KEY (room_id, agent_name)
62
+ );
63
+
64
+ CREATE TABLE IF NOT EXISTS room_messages (
65
+ id TEXT PRIMARY KEY,
66
+ room_id TEXT NOT NULL REFERENCES rooms(id) ON DELETE CASCADE,
67
+ role TEXT NOT NULL,
68
+ agent_name TEXT DEFAULT '',
69
+ content TEXT NOT NULL,
70
+ mentions TEXT DEFAULT '[]',
71
+ round INTEGER DEFAULT 0,
72
+ created_at INTEGER NOT NULL
73
+ );
74
+
75
+ CREATE INDEX IF NOT EXISTS idx_room_messages_room
76
+ ON room_messages(room_id, created_at);
77
+ `);
78
+ }
79
+ // ---------------------------------------------------------------------------
80
+ // Room CRUD
81
+ // ---------------------------------------------------------------------------
82
+ function createRoom(name) {
83
+ const db = getDb();
84
+ const now = Date.now();
85
+ const id = crypto.randomUUID();
86
+ db.prepare('INSERT INTO rooms (id, name, settings, created_at, updated_at) VALUES (?, ?, ?, ?, ?)')
87
+ .run(id, name, '{}', now, now);
88
+ return { id, name, settings: '{}', created_at: now, updated_at: now };
89
+ }
90
+ function listRooms() {
91
+ const db = getDb();
92
+ return db.prepare('SELECT * FROM rooms ORDER BY updated_at DESC').all();
93
+ }
94
+ function getRoom(roomId) {
95
+ const db = getDb();
96
+ return db.prepare('SELECT * FROM rooms WHERE id = ?').get(roomId);
97
+ }
98
+ function updateRoom(roomId, updates) {
99
+ const db = getDb();
100
+ const sets = [];
101
+ const values = [];
102
+ if (updates.name !== undefined) {
103
+ sets.push('name = ?');
104
+ values.push(updates.name);
105
+ }
106
+ if (updates.settings !== undefined) {
107
+ sets.push('settings = ?');
108
+ values.push(updates.settings);
109
+ }
110
+ if (sets.length === 0)
111
+ return false;
112
+ sets.push('updated_at = ?');
113
+ values.push(Date.now());
114
+ values.push(roomId);
115
+ const result = db.prepare(`UPDATE rooms SET ${sets.join(', ')} WHERE id = ?`).run(...values);
116
+ return result.changes > 0;
117
+ }
118
+ function deleteRoom(roomId) {
119
+ const db = getDb();
120
+ db.prepare('DELETE FROM room_messages WHERE room_id = ?').run(roomId);
121
+ db.prepare('DELETE FROM room_agents WHERE room_id = ?').run(roomId);
122
+ const result = db.prepare('DELETE FROM rooms WHERE id = ?').run(roomId);
123
+ return result.changes > 0;
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Room Agents
127
+ // ---------------------------------------------------------------------------
128
+ function addRoomAgent(roomId, agent) {
129
+ const db = getDb();
130
+ const row = db.prepare('INSERT OR IGNORE INTO room_agents (room_id, agent_name, profile, session_id, role_prompt) VALUES (?, ?, ?, ?, ?)').run(roomId, agent.agent_name, agent.profile, '', agent.role_prompt || '');
131
+ if (row.changes === 0)
132
+ return null;
133
+ return getRoomAgent(roomId, agent.agent_name);
134
+ }
135
+ function removeRoomAgent(roomId, agentName) {
136
+ const db = getDb();
137
+ const result = db.prepare('DELETE FROM room_agents WHERE room_id = ? AND agent_name = ?')
138
+ .run(roomId, agentName);
139
+ return result.changes > 0;
140
+ }
141
+ function getRoomAgents(roomId) {
142
+ const db = getDb();
143
+ return db.prepare('SELECT * FROM room_agents WHERE room_id = ?').all(roomId);
144
+ }
145
+ function getRoomAgent(roomId, agentName) {
146
+ const db = getDb();
147
+ return db.prepare('SELECT * FROM room_agents WHERE room_id = ? AND agent_name = ?')
148
+ .get(roomId, agentName);
149
+ }
150
+ function updateRoomAgentSession(roomId, agentName, sessionId) {
151
+ const db = getDb();
152
+ db.prepare('UPDATE room_agents SET session_id = ? WHERE room_id = ? AND agent_name = ?')
153
+ .run(sessionId, roomId, agentName);
154
+ }
155
+ // ---------------------------------------------------------------------------
156
+ // Messages
157
+ // ---------------------------------------------------------------------------
158
+ function addMessage(msg) {
159
+ const db = getDb();
160
+ const id = crypto.randomUUID();
161
+ const now = Date.now();
162
+ const mentions = msg.mentions ? JSON.stringify(msg.mentions) : '[]';
163
+ const round = msg.round ?? getLatestRound(msg.room_id);
164
+ db.prepare('INSERT INTO room_messages (id, room_id, role, agent_name, content, mentions, round, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)').run(id, msg.room_id, msg.role, msg.agent_name || '', msg.content, mentions, round, now);
165
+ // Update room timestamp
166
+ db.prepare('UPDATE rooms SET updated_at = ? WHERE id = ?').run(now, msg.room_id);
167
+ return { id, room_id: msg.room_id, role: msg.role, agent_name: msg.agent_name || '', content: msg.content, mentions, round, created_at: now };
168
+ }
169
+ function getRoomMessages(roomId, limit = 100, offset = 0) {
170
+ const db = getDb();
171
+ return db.prepare('SELECT * FROM room_messages WHERE room_id = ? ORDER BY created_at ASC LIMIT ? OFFSET ?').all(roomId, limit, offset);
172
+ }
173
+ function getLatestRound(roomId) {
174
+ const db = getDb();
175
+ const row = db.prepare('SELECT MAX(round) as max_round FROM room_messages WHERE room_id = ?')
176
+ .get(roomId);
177
+ return (row?.max_round ?? -1) + 1;
178
+ }
179
+ /**
180
+ * Get recent context messages relevant to a specific agent.
181
+ * Returns messages where:
182
+ * - mentions is empty (public message, no @mentions)
183
+ * - mentions includes agentName
184
+ * - message is FROM this agent
185
+ */
186
+ function getRecentContext(roomId, agentName, limit = 20) {
187
+ const db = getDb();
188
+ return db.prepare(`
189
+ SELECT * FROM room_messages
190
+ WHERE room_id = ?
191
+ AND (
192
+ json_array_length(mentions) = 0
193
+ OR agent_name = ?
194
+ OR mentions LIKE ?
195
+ )
196
+ ORDER BY created_at DESC
197
+ LIMIT ?
198
+ `).all(roomId, agentName, `%"${agentName}"%`, limit);
199
+ }
@@ -87,6 +87,8 @@ export declare class GatewayManager {
87
87
  private resolvePort;
88
88
  /** 获取指定 profile 的网关 URL(代理路由使用) */
89
89
  getUpstream(profileName?: string): string;
90
+ /** 读取 profile 的 API_SERVER_KEY(从 .env 文件) */
91
+ getApiKey(profileName?: string): string | null;
90
92
  getActiveProfile(): string;
91
93
  setActiveProfile(name: string): void;
92
94
  /** 列出所有已知 profile 名称(通过 hermes CLI 或文件系统扫描) */
@@ -275,6 +275,21 @@ class GatewayManager {
275
275
  const { port, host } = this.readProfilePort(name);
276
276
  return `http://${host}:${port}`;
277
277
  }
278
+ /** 读取 profile 的 API_SERVER_KEY(从 .env 文件) */
279
+ getApiKey(profileName) {
280
+ const name = profileName || this.activeProfile;
281
+ try {
282
+ const envPath = (0, path_1.join)(this.profileDir(name), '.env');
283
+ if (!(0, fs_1.existsSync)(envPath))
284
+ return null;
285
+ const content = (0, fs_1.readFileSync)(envPath, 'utf-8');
286
+ const match = content.match(/^API_SERVER_KEY\s*=\s*"?([^"\n]+)"?/m);
287
+ return match?.[1]?.trim() || null;
288
+ }
289
+ catch {
290
+ return null;
291
+ }
292
+ }
278
293
  getActiveProfile() {
279
294
  return this.activeProfile;
280
295
  }
@@ -20,3 +20,9 @@ export declare function getActiveEnvPath(): string;
20
20
  * Get the active profile name.
21
21
  */
22
22
  export declare function getActiveProfileName(): string;
23
+ /**
24
+ * Get profile directory by name.
25
+ * default → ~/.hermes/
26
+ * other → ~/.hermes/profiles/{name}/
27
+ */
28
+ export declare function getProfileDir(name: string): string;
@@ -5,6 +5,7 @@ exports.getActiveConfigPath = getActiveConfigPath;
5
5
  exports.getActiveAuthPath = getActiveAuthPath;
6
6
  exports.getActiveEnvPath = getActiveEnvPath;
7
7
  exports.getActiveProfileName = getActiveProfileName;
8
+ exports.getProfileDir = getProfileDir;
8
9
  const path_1 = require("path");
9
10
  const os_1 = require("os");
10
11
  const fs_1 = require("fs");
@@ -58,3 +59,14 @@ function getActiveProfileName() {
58
59
  return 'default';
59
60
  }
60
61
  }
62
+ /**
63
+ * Get profile directory by name.
64
+ * default → ~/.hermes/
65
+ * other → ~/.hermes/profiles/{name}/
66
+ */
67
+ function getProfileDir(name) {
68
+ if (!name || name === 'default')
69
+ return HERMES_BASE;
70
+ const dir = (0, path_1.join)(HERMES_BASE, 'profiles', name);
71
+ return (0, fs_1.existsSync)(dir) ? dir : HERMES_BASE;
72
+ }
@@ -51,22 +51,10 @@ exports.PROVIDER_PRESETS = [
51
51
  },
52
52
  {
53
53
  label: 'Kimi for Coding',
54
- value: 'kimi-coding',
55
- base_url: 'https://api.kimi.com/coding/v1',
56
- models: [
57
- 'kimi-for-coding',
58
- 'kimi-k2.5',
59
- 'kimi-k2-thinking',
60
- 'kimi-k2-thinking-turbo',
61
- 'kimi-k2-turbo-preview',
62
- 'kimi-k2-0905-preview',
63
- ],
64
- },
65
- {
66
- label: 'Kimi for Coding (CN)',
67
54
  value: 'kimi-coding-cn',
68
55
  base_url: 'https://api.kimi.com/coding/v1',
69
56
  models: [
57
+ 'kimi-for-coding',
70
58
  'kimi-k2.5',
71
59
  'kimi-k2-thinking',
72
60
  'kimi-k2-turbo-preview',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermes-web-ui",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Web dashboard for Hermes Agent — multi-platform AI chat, session management, scheduled jobs, usage analytics & channel configuration (Telegram, Discord, Slack, WhatsApp)",
5
5
  "repository": {
6
6
  "type": "git",