agentstudio 0.1.0
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.
- package/.env +15 -0
- package/README.md +85 -0
- package/dist/bin/agentstudio.d.ts +3 -0
- package/dist/bin/agentstudio.d.ts.map +1 -0
- package/dist/bin/agentstudio.js +141 -0
- package/dist/bin/agentstudio.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +87 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +7 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +21 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/routes/agents.d.ts +4 -0
- package/dist/routes/agents.d.ts.map +1 -0
- package/dist/routes/agents.js +804 -0
- package/dist/routes/agents.js.map +1 -0
- package/dist/routes/auth.d.ts +4 -0
- package/dist/routes/auth.d.ts.map +1 -0
- package/dist/routes/auth.js +60 -0
- package/dist/routes/auth.js.map +1 -0
- package/dist/routes/files.d.ts +4 -0
- package/dist/routes/files.d.ts.map +1 -0
- package/dist/routes/files.js +301 -0
- package/dist/routes/files.js.map +1 -0
- package/dist/routes/mcp.d.ts +4 -0
- package/dist/routes/mcp.d.ts.map +1 -0
- package/dist/routes/mcp.js +652 -0
- package/dist/routes/mcp.js.map +1 -0
- package/dist/routes/media.d.ts +5 -0
- package/dist/routes/media.d.ts.map +1 -0
- package/dist/routes/media.js +117 -0
- package/dist/routes/media.js.map +1 -0
- package/dist/routes/slides.d.ts +4 -0
- package/dist/routes/slides.d.ts.map +1 -0
- package/dist/routes/slides.js +146 -0
- package/dist/routes/slides.js.map +1 -0
- package/dist/services/claudeSession.d.ts +83 -0
- package/dist/services/claudeSession.d.ts.map +1 -0
- package/dist/services/claudeSession.js +255 -0
- package/dist/services/claudeSession.js.map +1 -0
- package/dist/services/messageQueue.d.ts +31 -0
- package/dist/services/messageQueue.d.ts.map +1 -0
- package/dist/services/messageQueue.js +67 -0
- package/dist/services/messageQueue.js.map +1 -0
- package/dist/services/sessionManager.d.ts +132 -0
- package/dist/services/sessionManager.d.ts.map +1 -0
- package/dist/services/sessionManager.js +439 -0
- package/dist/services/sessionManager.js.map +1 -0
- package/dist/types/claude-history.d.ts +48 -0
- package/dist/types/claude-history.d.ts.map +1 -0
- package/dist/types/claude-history.js +2 -0
- package/dist/types/claude-history.js.map +1 -0
- package/dist/types/claude-versions.d.ts +31 -0
- package/dist/types/claude-versions.d.ts.map +1 -0
- package/dist/types/claude-versions.js +2 -0
- package/dist/types/claude-versions.js.map +1 -0
- package/dist/types/commands.d.ts +32 -0
- package/dist/types/commands.d.ts.map +1 -0
- package/dist/types/commands.js +2 -0
- package/dist/types/commands.js.map +1 -0
- package/dist/types/index.d.ts +81 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +150 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/subagents.d.ts +88 -0
- package/dist/types/subagents.d.ts.map +1 -0
- package/dist/types/subagents.js +2 -0
- package/dist/types/subagents.js.map +1 -0
- package/dist/utils/agentStorage.d.ts +19 -0
- package/dist/utils/agentStorage.d.ts.map +1 -0
- package/dist/utils/agentStorage.js +110 -0
- package/dist/utils/agentStorage.js.map +1 -0
- package/dist/utils/claudeVersionStorage.d.ts +33 -0
- package/dist/utils/claudeVersionStorage.d.ts.map +1 -0
- package/dist/utils/claudeVersionStorage.js +168 -0
- package/dist/utils/claudeVersionStorage.js.map +1 -0
- package/dist/utils/jwt.d.ts +15 -0
- package/dist/utils/jwt.d.ts.map +1 -0
- package/dist/utils/jwt.js +28 -0
- package/dist/utils/jwt.js.map +1 -0
- package/dist/utils/projectMetadataStorage.d.ts +21 -0
- package/dist/utils/projectMetadataStorage.d.ts.map +1 -0
- package/dist/utils/projectMetadataStorage.js +68 -0
- package/dist/utils/projectMetadataStorage.js.map +1 -0
- package/frontend/dist/index.html +86 -0
- package/package.json +66 -0
- package/src/bin/agentstudio.ts +161 -0
- package/src/index.ts +100 -0
- package/src/middleware/auth.ts +26 -0
- package/src/routes/agents.ts +885 -0
- package/src/routes/auth.ts +73 -0
- package/src/routes/commands.ts.bak +441 -0
- package/src/routes/files.ts +352 -0
- package/src/routes/mcp.ts +751 -0
- package/src/routes/media.ts +140 -0
- package/src/routes/projects.ts.bak +601 -0
- package/src/routes/sessions.ts.bak +809 -0
- package/src/routes/settings.ts.bak +718 -0
- package/src/routes/slides.ts +170 -0
- package/src/routes/subagents.ts.bak +364 -0
- package/src/services/claudeSession.ts +293 -0
- package/src/services/messageQueue.ts +71 -0
- package/src/services/sessionManager.ts +532 -0
- package/src/types/claude-history.ts +50 -0
- package/src/types/claude-versions.ts +33 -0
- package/src/types/commands.ts +35 -0
- package/src/types/index.ts +248 -0
- package/src/types/subagents.ts +106 -0
- package/src/utils/agentStorage.ts +126 -0
- package/src/utils/claudeVersionStorage.ts +199 -0
- package/src/utils/jwt.ts +36 -0
- package/src/utils/projectMetadataStorage.ts +86 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,809 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as fs from 'fs';
|
|
4
|
+
import * as os from 'os';
|
|
5
|
+
import { AgentStorage } from '../utils/agentStorage.js';
|
|
6
|
+
import { ClaudeHistoryMessage, ClaudeHistorySession } from '../types/claude-history.js';
|
|
7
|
+
import { sessionManager } from '../services/sessionManager.js';
|
|
8
|
+
|
|
9
|
+
const router: express.Router = express.Router();
|
|
10
|
+
|
|
11
|
+
// Storage instances
|
|
12
|
+
const globalAgentStorage = new AgentStorage();
|
|
13
|
+
|
|
14
|
+
// Helper functions for reading Claude Code history from ~/.claude/projects
|
|
15
|
+
function convertProjectPathToClaudeFormat(projectPath: string): string {
|
|
16
|
+
// Convert path like /Users/kongjie/claude-code-projects/ppt-editor-project-2025-08-27-00-12
|
|
17
|
+
// to: -Users-kongjie-claude-code-projects-ppt-editor-project-2025-08-27-00-12
|
|
18
|
+
return projectPath.replace(/\//g, '-');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Function to get AgentStorage instance for specific project directory
|
|
22
|
+
const getAgentStorageForRequest = (req: express.Request): AgentStorage => {
|
|
23
|
+
const projectPath = req.query.projectPath as string || req.body?.projectPath as string;
|
|
24
|
+
const workingDir = projectPath || process.cwd();
|
|
25
|
+
return new AgentStorage(workingDir);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
// Process compact context messages - detect and convert the 4-message or 5-message pattern
|
|
29
|
+
function processCompactContextMessages(messages: ClaudeHistoryMessage[]): ClaudeHistoryMessage[] {
|
|
30
|
+
const processedMessages: ClaudeHistoryMessage[] = [];
|
|
31
|
+
let i = 0;
|
|
32
|
+
|
|
33
|
+
while (i < messages.length) {
|
|
34
|
+
const currentMsg = messages[i];
|
|
35
|
+
|
|
36
|
+
// Case 1a: New format (5-message pattern) - Check for compact_boundary message first
|
|
37
|
+
if ((currentMsg as any).type === 'system' &&
|
|
38
|
+
(currentMsg as any).subtype === 'compact_boundary' &&
|
|
39
|
+
currentMsg.parentUuid === null &&
|
|
40
|
+
i + 4 < messages.length) {
|
|
41
|
+
|
|
42
|
+
const summaryMsg = messages[i + 1];
|
|
43
|
+
const metaMsg = messages[i + 2];
|
|
44
|
+
const commandMsg = messages[i + 3];
|
|
45
|
+
const outputMsg = messages[i + 4];
|
|
46
|
+
|
|
47
|
+
// Verify this is the new 5-message compact pattern
|
|
48
|
+
if (summaryMsg.isCompactSummary &&
|
|
49
|
+
summaryMsg.parentUuid === currentMsg.uuid &&
|
|
50
|
+
metaMsg.isMeta === true &&
|
|
51
|
+
commandMsg.type === 'user' &&
|
|
52
|
+
commandMsg.message?.content &&
|
|
53
|
+
typeof commandMsg.message.content === 'string' &&
|
|
54
|
+
commandMsg.message.content.includes('<command-name>/compact</command-name>') &&
|
|
55
|
+
commandMsg.parentUuid === metaMsg.uuid &&
|
|
56
|
+
outputMsg.type === 'user' &&
|
|
57
|
+
outputMsg.message?.content &&
|
|
58
|
+
typeof outputMsg.message.content === 'string' &&
|
|
59
|
+
outputMsg.message.content.includes('<local-command-stdout>') &&
|
|
60
|
+
outputMsg.parentUuid === commandMsg.uuid) {
|
|
61
|
+
|
|
62
|
+
// Create synthetic user command message
|
|
63
|
+
const userCommandMessage: ClaudeHistoryMessage = {
|
|
64
|
+
type: 'user',
|
|
65
|
+
uuid: `synthetic_cmd_${commandMsg.uuid}`,
|
|
66
|
+
timestamp: commandMsg.timestamp,
|
|
67
|
+
sessionId: commandMsg.sessionId,
|
|
68
|
+
parentUuid: currentMsg.parentUuid,
|
|
69
|
+
message: {
|
|
70
|
+
role: 'user',
|
|
71
|
+
content: '/compact'
|
|
72
|
+
},
|
|
73
|
+
isCompactCommand: true
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Create synthetic AI response with compressed content
|
|
77
|
+
const aiResponseMessage: ClaudeHistoryMessage = {
|
|
78
|
+
type: 'assistant',
|
|
79
|
+
uuid: `synthetic_ai_${summaryMsg.uuid}`,
|
|
80
|
+
timestamp: summaryMsg.timestamp,
|
|
81
|
+
sessionId: summaryMsg.sessionId,
|
|
82
|
+
parentUuid: userCommandMessage.uuid,
|
|
83
|
+
message: {
|
|
84
|
+
role: 'assistant',
|
|
85
|
+
content: extractContentFromClaudeMessage(summaryMsg, messages) || '会话上下文已压缩'
|
|
86
|
+
},
|
|
87
|
+
isCompactSummary: true
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
processedMessages.push(userCommandMessage, aiResponseMessage);
|
|
91
|
+
i += 5; // Skip all 5 messages
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Case 1b: Old format (4-message pattern) - Check for isCompactSummary with parentUuid === null
|
|
97
|
+
if (currentMsg.isCompactSummary && currentMsg.parentUuid === null && i + 3 < messages.length) {
|
|
98
|
+
const metaMsg = messages[i + 1];
|
|
99
|
+
const commandMsg = messages[i + 2];
|
|
100
|
+
const outputMsg = messages[i + 3];
|
|
101
|
+
|
|
102
|
+
// Verify this is the old 4-message manual compact pattern
|
|
103
|
+
if (metaMsg.isMeta === true &&
|
|
104
|
+
commandMsg.type === 'user' &&
|
|
105
|
+
commandMsg.message?.content &&
|
|
106
|
+
typeof commandMsg.message.content === 'string' &&
|
|
107
|
+
commandMsg.message.content.includes('<command-name>/compact</command-name>') &&
|
|
108
|
+
commandMsg.parentUuid === metaMsg.uuid &&
|
|
109
|
+
outputMsg.type === 'user' &&
|
|
110
|
+
outputMsg.message?.content &&
|
|
111
|
+
typeof outputMsg.message.content === 'string' &&
|
|
112
|
+
outputMsg.message.content.includes('<local-command-stdout>') &&
|
|
113
|
+
outputMsg.parentUuid === commandMsg.uuid) {
|
|
114
|
+
|
|
115
|
+
// Create synthetic user command message
|
|
116
|
+
const userCommandMessage: ClaudeHistoryMessage = {
|
|
117
|
+
type: 'user',
|
|
118
|
+
uuid: `synthetic_cmd_${commandMsg.uuid}`,
|
|
119
|
+
timestamp: commandMsg.timestamp,
|
|
120
|
+
sessionId: commandMsg.sessionId,
|
|
121
|
+
parentUuid: currentMsg.parentUuid,
|
|
122
|
+
message: {
|
|
123
|
+
role: 'user',
|
|
124
|
+
content: '/compact'
|
|
125
|
+
},
|
|
126
|
+
isCompactCommand: true
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// Create synthetic AI response with compressed content
|
|
130
|
+
const aiResponseMessage: ClaudeHistoryMessage = {
|
|
131
|
+
type: 'assistant',
|
|
132
|
+
uuid: `synthetic_ai_${currentMsg.uuid}`,
|
|
133
|
+
timestamp: currentMsg.timestamp,
|
|
134
|
+
sessionId: currentMsg.sessionId,
|
|
135
|
+
parentUuid: userCommandMessage.uuid,
|
|
136
|
+
message: {
|
|
137
|
+
role: 'assistant',
|
|
138
|
+
content: extractContentFromClaudeMessage(currentMsg, messages) || '会话上下文已压缩'
|
|
139
|
+
},
|
|
140
|
+
isCompactSummary: true
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
processedMessages.push(userCommandMessage, aiResponseMessage);
|
|
144
|
+
i += 4; // Skip all 4 messages
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Case 2: Auto compact - Single message with isCompactSummary
|
|
150
|
+
if (currentMsg.isCompactSummary && currentMsg.parentUuid === null &&
|
|
151
|
+
!(i + 1 < messages.length && messages[i + 1].isMeta === true)) {
|
|
152
|
+
|
|
153
|
+
// Create synthetic AI response for auto-compressed content
|
|
154
|
+
const aiResponseMessage: ClaudeHistoryMessage = {
|
|
155
|
+
type: 'assistant',
|
|
156
|
+
uuid: `synthetic_auto_${currentMsg.uuid}`,
|
|
157
|
+
timestamp: currentMsg.timestamp,
|
|
158
|
+
sessionId: currentMsg.sessionId,
|
|
159
|
+
parentUuid: currentMsg.parentUuid,
|
|
160
|
+
message: {
|
|
161
|
+
role: 'assistant',
|
|
162
|
+
content: extractContentFromClaudeMessage(currentMsg, messages) || '会话上下文已自动压缩'
|
|
163
|
+
},
|
|
164
|
+
isCompactSummary: true
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
processedMessages.push(aiResponseMessage);
|
|
168
|
+
i++;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Regular message - pass through
|
|
173
|
+
processedMessages.push(currentMsg);
|
|
174
|
+
i++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return processedMessages;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function readClaudeHistorySessions(projectPath: string): ClaudeHistorySession[] {
|
|
181
|
+
try {
|
|
182
|
+
const claudeProjectPath = convertProjectPathToClaudeFormat(projectPath);
|
|
183
|
+
const historyDir = path.join(os.homedir(), '.claude', 'projects', claudeProjectPath);
|
|
184
|
+
|
|
185
|
+
if (!fs.existsSync(historyDir)) {
|
|
186
|
+
console.log('Claude history directory not found:', historyDir);
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const jsonlFiles = fs.readdirSync(historyDir)
|
|
191
|
+
.filter(file => file.endsWith('.jsonl'))
|
|
192
|
+
.filter(file => !file.startsWith('.'));
|
|
193
|
+
|
|
194
|
+
const sessions: ClaudeHistorySession[] = [];
|
|
195
|
+
|
|
196
|
+
for (const filename of jsonlFiles) {
|
|
197
|
+
const sessionId = filename.replace('.jsonl', '');
|
|
198
|
+
const filePath = path.join(historyDir, filename);
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
202
|
+
const lines = content.trim().split('\n').filter(line => line.trim());
|
|
203
|
+
|
|
204
|
+
if (lines.length === 0) continue;
|
|
205
|
+
|
|
206
|
+
const messages: ClaudeHistoryMessage[] = lines.map(line => JSON.parse(line));
|
|
207
|
+
|
|
208
|
+
// Find summary message for session title
|
|
209
|
+
const summaryMessage = messages.find(msg => msg.type === 'summary');
|
|
210
|
+
const title = summaryMessage?.summary || `会话 ${sessionId.slice(0, 8)}`;
|
|
211
|
+
|
|
212
|
+
// Process compact context messages before filtering
|
|
213
|
+
const processedMessages = processCompactContextMessages(messages);
|
|
214
|
+
|
|
215
|
+
// Filter user and assistant messages, but exclude tool_result-only user messages, isMeta messages,
|
|
216
|
+
const conversationMessages = processedMessages.filter(msg => {
|
|
217
|
+
// Filter out isMeta messages (rule 1)
|
|
218
|
+
if ((msg as any).isMeta === true) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Filter out /clear command messages and its related output messages
|
|
223
|
+
if (msg.type === 'user' && msg.message?.content && typeof msg.message.content === 'string') {
|
|
224
|
+
// Check for /clear command format
|
|
225
|
+
if (msg.message.content.includes('<command-name>/clear</command-name>')) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
// Check for /clear command's output (local-command-stdout that follows /clear command)
|
|
229
|
+
if (msg.message.content.includes('<local-command-stdout></local-command-stdout>') &&
|
|
230
|
+
msg.parentUuid) {
|
|
231
|
+
// Find the parent message to check if it was a /clear command
|
|
232
|
+
const parentMsg = messages.find(m => m.uuid === msg.parentUuid);
|
|
233
|
+
if (parentMsg && parentMsg.message?.content && typeof parentMsg.message.content === 'string' &&
|
|
234
|
+
parentMsg.message.content.includes('<command-name>/clear</command-name>')) {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (msg.type === 'assistant') return true;
|
|
241
|
+
if (msg.type === 'user') {
|
|
242
|
+
// Check if this user message contains only tool_result
|
|
243
|
+
if (msg.message?.content && Array.isArray(msg.message.content)) {
|
|
244
|
+
const hasNonToolResult = msg.message.content.some((block: any) => block.type !== 'tool_result');
|
|
245
|
+
return hasNonToolResult; // Only include if it has content other than tool_result
|
|
246
|
+
}
|
|
247
|
+
// Include user messages with string content or no content array
|
|
248
|
+
return typeof msg.message?.content === 'string' || !msg.message?.content;
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
if (conversationMessages.length === 0) continue;
|
|
254
|
+
|
|
255
|
+
// Convert messages to our format and group consecutive assistant messages
|
|
256
|
+
const convertedMessages: any[] = [];
|
|
257
|
+
let i = 0;
|
|
258
|
+
|
|
259
|
+
while (i < conversationMessages.length) {
|
|
260
|
+
const msg = conversationMessages[i];
|
|
261
|
+
|
|
262
|
+
if (msg.type === 'user') {
|
|
263
|
+
// Check if this is a local-command-stdout message
|
|
264
|
+
const content = msg.message?.content;
|
|
265
|
+
if (typeof content === 'string' && content.includes('<local-command-stdout>')) {
|
|
266
|
+
// Extract content from local-command-stdout tag
|
|
267
|
+
const outputMatch = content.match(/<local-command-stdout>([^<]*)<\/local-command-stdout>/);
|
|
268
|
+
const displayOutput = outputMatch ? outputMatch[1] : '';
|
|
269
|
+
|
|
270
|
+
// Create AI response message
|
|
271
|
+
convertedMessages.push({
|
|
272
|
+
id: `msg_${convertedMessages.length}_${msg.uuid}`,
|
|
273
|
+
role: 'assistant',
|
|
274
|
+
content: displayOutput,
|
|
275
|
+
timestamp: new Date(msg.timestamp).getTime(),
|
|
276
|
+
messageParts: [{
|
|
277
|
+
id: `part_0_${msg.uuid}`,
|
|
278
|
+
type: 'text',
|
|
279
|
+
content: displayOutput,
|
|
280
|
+
order: 0
|
|
281
|
+
}]
|
|
282
|
+
});
|
|
283
|
+
} else {
|
|
284
|
+
// Regular user message
|
|
285
|
+
convertedMessages.push({
|
|
286
|
+
id: `msg_${convertedMessages.length}_${msg.uuid}`,
|
|
287
|
+
role: msg.message?.role || msg.type,
|
|
288
|
+
content: extractContentFromClaudeMessage(msg, messages),
|
|
289
|
+
timestamp: new Date(msg.timestamp).getTime(),
|
|
290
|
+
messageParts: convertClaudeMessageToMessageParts(msg, messages)
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
i++;
|
|
294
|
+
} else if (msg.type === 'assistant') {
|
|
295
|
+
// Find all consecutive assistant messages and combine them
|
|
296
|
+
const assistantMessages = [msg];
|
|
297
|
+
let j = i + 1;
|
|
298
|
+
|
|
299
|
+
// Collect all consecutive assistant messages
|
|
300
|
+
while (j < conversationMessages.length && conversationMessages[j].type === 'assistant') {
|
|
301
|
+
assistantMessages.push(conversationMessages[j]);
|
|
302
|
+
j++;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Create combined assistant message
|
|
306
|
+
const combinedMessage = {
|
|
307
|
+
id: `msg_${convertedMessages.length}_${msg.uuid}`,
|
|
308
|
+
role: 'assistant',
|
|
309
|
+
content: '',
|
|
310
|
+
timestamp: new Date(msg.timestamp).getTime(),
|
|
311
|
+
messageParts: [] as any[]
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
// Combine all assistant message parts
|
|
315
|
+
assistantMessages.forEach((assistantMsg) => {
|
|
316
|
+
const textContent = extractContentFromClaudeMessage(assistantMsg, messages);
|
|
317
|
+
const msgParts = convertClaudeMessageToMessageParts(assistantMsg, messages);
|
|
318
|
+
|
|
319
|
+
combinedMessage.content += textContent;
|
|
320
|
+
combinedMessage.messageParts.push(...msgParts.map(part => ({
|
|
321
|
+
...part,
|
|
322
|
+
order: combinedMessage.messageParts.length + part.order
|
|
323
|
+
})));
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
convertedMessages.push(combinedMessage);
|
|
327
|
+
i = j; // Skip to next non-assistant message
|
|
328
|
+
} else {
|
|
329
|
+
i++;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Process tool results - find tool_result messages and associate them with tool_use
|
|
334
|
+
for (let i = 0; i < messages.length; i++) {
|
|
335
|
+
const msg = messages[i];
|
|
336
|
+
if (msg.type === 'user' && msg.message?.content && Array.isArray(msg.message.content)) {
|
|
337
|
+
for (const block of msg.message.content) {
|
|
338
|
+
if (block.type === 'tool_result' && block.tool_use_id) {
|
|
339
|
+
// Find the assistant message that contains the matching tool_use
|
|
340
|
+
// Look backwards through conversation messages (not all messages)
|
|
341
|
+
for (let j = convertedMessages.length - 1; j >= 0; j--) {
|
|
342
|
+
const assistantMsg = convertedMessages[j];
|
|
343
|
+
if (assistantMsg && assistantMsg.role === 'assistant') {
|
|
344
|
+
// Find the tool part with matching claudeId
|
|
345
|
+
const toolPart = assistantMsg.messageParts.find((part: any) =>
|
|
346
|
+
part.type === 'tool' &&
|
|
347
|
+
part.toolData &&
|
|
348
|
+
part.toolData.claudeId === block.tool_use_id
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
if (toolPart && toolPart.toolData) {
|
|
352
|
+
toolPart.toolData.toolResult = typeof block.content === 'string'
|
|
353
|
+
? block.content
|
|
354
|
+
: JSON.stringify(block.content);
|
|
355
|
+
|
|
356
|
+
// Check if the original message has toolUseResult (from Claude Code SDK)
|
|
357
|
+
if (msg.toolUseResult) {
|
|
358
|
+
toolPart.toolData.toolUseResult = msg.toolUseResult;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
toolPart.toolData.isError = block.is_error || false;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Get timestamps
|
|
372
|
+
const timestamps = conversationMessages
|
|
373
|
+
.map(msg => new Date(msg.timestamp).getTime())
|
|
374
|
+
.filter(t => !isNaN(t));
|
|
375
|
+
|
|
376
|
+
const createdAt = timestamps.length > 0 ? Math.min(...timestamps) : Date.now();
|
|
377
|
+
const lastUpdated = timestamps.length > 0 ? Math.max(...timestamps) : Date.now();
|
|
378
|
+
|
|
379
|
+
sessions.push({
|
|
380
|
+
id: sessionId,
|
|
381
|
+
title,
|
|
382
|
+
createdAt: new Date(createdAt).toISOString(),
|
|
383
|
+
lastUpdated: new Date(lastUpdated).toISOString(),
|
|
384
|
+
messages: convertedMessages
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
} catch (error) {
|
|
388
|
+
console.error(`Failed to parse Claude history file ${filename}:`, error);
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return sessions.sort((a, b) => new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime());
|
|
394
|
+
|
|
395
|
+
} catch (error) {
|
|
396
|
+
console.error('Failed to read Claude history sessions:', error);
|
|
397
|
+
return [];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function extractContentFromClaudeMessage(msg: ClaudeHistoryMessage, allMessages: ClaudeHistoryMessage[] = []): string {
|
|
402
|
+
if (!msg.message?.content) return '';
|
|
403
|
+
|
|
404
|
+
// Handle both array and string content
|
|
405
|
+
if (typeof msg.message.content === 'string') {
|
|
406
|
+
const commandMatch = msg.message.content.match(/<command-name>(.+?)<\/command-name>/);
|
|
407
|
+
if (commandMatch) {
|
|
408
|
+
// Check for two different patterns:
|
|
409
|
+
// Pattern 1: Parent is meta message (3-message local-command pattern)
|
|
410
|
+
const parentMessage = msg.parentUuid ? allMessages.find(m => m.uuid === msg.parentUuid) : null;
|
|
411
|
+
const isMetaParent = parentMessage && (parentMessage as any).isMeta === true;
|
|
412
|
+
|
|
413
|
+
// Pattern 2: Child is meta message (2-message user-custom-command pattern)
|
|
414
|
+
const childMessage = allMessages.find(m => m.parentUuid === msg.uuid);
|
|
415
|
+
const isMetaChild = childMessage && (childMessage as any).isMeta === true;
|
|
416
|
+
|
|
417
|
+
if (isMetaParent) {
|
|
418
|
+
// 3-message pattern: return only command name
|
|
419
|
+
return commandMatch[1];
|
|
420
|
+
} else if (isMetaChild) {
|
|
421
|
+
// 2-message pattern: return command name + args
|
|
422
|
+
const argsMatch = msg.message.content.match(/<command-args>([^<]*)<\/command-args>/);
|
|
423
|
+
const args = argsMatch ? argsMatch[1].trim() : '';
|
|
424
|
+
return args ? `${commandMatch[1]} ${args}` : commandMatch[1];
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
return msg.message.content;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (Array.isArray(msg.message.content)) {
|
|
431
|
+
return msg.message.content
|
|
432
|
+
.filter((block: any) => block.type === 'text' || block.type === 'thinking')
|
|
433
|
+
.map((block: any) => block.text || block.thinking || '')
|
|
434
|
+
.join('');
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return '';
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function convertClaudeMessageToMessageParts(msg: ClaudeHistoryMessage, allMessages: ClaudeHistoryMessage[] = []): any[] {
|
|
441
|
+
if (!msg.message?.content) return [];
|
|
442
|
+
|
|
443
|
+
// Handle compact command messages
|
|
444
|
+
if (msg.isCompactCommand) {
|
|
445
|
+
return [{
|
|
446
|
+
id: `part_0_${msg.uuid}`,
|
|
447
|
+
type: 'command',
|
|
448
|
+
content: '/compact',
|
|
449
|
+
order: 0
|
|
450
|
+
}];
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// Handle compact summary messages
|
|
454
|
+
if (msg.isCompactSummary) {
|
|
455
|
+
return [{
|
|
456
|
+
id: `part_0_${msg.uuid}`,
|
|
457
|
+
type: 'compactSummary',
|
|
458
|
+
content: msg.message.content,
|
|
459
|
+
order: 0
|
|
460
|
+
}];
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Handle string content
|
|
464
|
+
if (typeof msg.message.content === 'string') {
|
|
465
|
+
// Rule 2: Check for command message format and create command-specific part
|
|
466
|
+
const commandMatch = msg.message.content.match(/<command-name>(.+?)<\/command-name>/);
|
|
467
|
+
if (commandMatch) {
|
|
468
|
+
// Check for two different patterns:
|
|
469
|
+
// Pattern 1: Parent is meta message (3-message local-command pattern)
|
|
470
|
+
const parentMessage = msg.parentUuid ? allMessages.find(m => m.uuid === msg.parentUuid) : null;
|
|
471
|
+
const isMetaParent = parentMessage && (parentMessage as any).isMeta === true;
|
|
472
|
+
|
|
473
|
+
// Pattern 2: Child is meta message (2-message user-custom-command pattern)
|
|
474
|
+
const childMessage = allMessages.find(m => m.parentUuid === msg.uuid);
|
|
475
|
+
const isMetaChild = childMessage && (childMessage as any).isMeta === true;
|
|
476
|
+
|
|
477
|
+
if (isMetaParent) {
|
|
478
|
+
// 3-message pattern: show only command name
|
|
479
|
+
return [{
|
|
480
|
+
id: `part_0_${msg.uuid}`,
|
|
481
|
+
type: 'command',
|
|
482
|
+
content: commandMatch[1], // Only the command name
|
|
483
|
+
originalContent: msg.message.content, // Keep original for reference
|
|
484
|
+
order: 0
|
|
485
|
+
}];
|
|
486
|
+
} else if (isMetaChild) {
|
|
487
|
+
// 2-message pattern: show command name + args
|
|
488
|
+
const argsMatch = msg.message.content.match(/<command-args>([^<]*)<\/command-args>/);
|
|
489
|
+
const args = argsMatch ? argsMatch[1].trim() : '';
|
|
490
|
+
const displayContent = args ? `${commandMatch[1]} ${args}` : commandMatch[1];
|
|
491
|
+
|
|
492
|
+
return [{
|
|
493
|
+
id: `part_0_${msg.uuid}`,
|
|
494
|
+
type: 'command',
|
|
495
|
+
content: displayContent, // Command name + args
|
|
496
|
+
originalContent: msg.message.content, // Keep original for reference
|
|
497
|
+
order: 0
|
|
498
|
+
}];
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return [{
|
|
503
|
+
id: `part_0_${msg.uuid}`,
|
|
504
|
+
type: 'text',
|
|
505
|
+
content: msg.message.content,
|
|
506
|
+
order: 0
|
|
507
|
+
}];
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Handle array content
|
|
511
|
+
if (Array.isArray(msg.message.content)) {
|
|
512
|
+
return msg.message.content.map((block: any, index: number) => {
|
|
513
|
+
if (block.type === 'text') {
|
|
514
|
+
return {
|
|
515
|
+
id: `part_${index}_${msg.uuid}`,
|
|
516
|
+
type: 'text',
|
|
517
|
+
content: block.text,
|
|
518
|
+
order: index
|
|
519
|
+
};
|
|
520
|
+
} else if (block.type === 'tool_use') {
|
|
521
|
+
return {
|
|
522
|
+
id: `part_${index}_${msg.uuid}`,
|
|
523
|
+
type: 'tool',
|
|
524
|
+
toolData: {
|
|
525
|
+
id: `tool_${index}_${msg.uuid}`,
|
|
526
|
+
claudeId: block.id,
|
|
527
|
+
toolName: block.name,
|
|
528
|
+
toolInput: block.input || {},
|
|
529
|
+
toolResult: '', // Will be filled by tool_result if available
|
|
530
|
+
isExecuting: false, // Historical data is not executing
|
|
531
|
+
isError: false
|
|
532
|
+
},
|
|
533
|
+
order: index
|
|
534
|
+
};
|
|
535
|
+
} else if (block.type === 'tool_result') {
|
|
536
|
+
// Skip tool_result blocks as they will be merged with tool_use blocks
|
|
537
|
+
return null;
|
|
538
|
+
} else if (block.type === 'image') {
|
|
539
|
+
// Handle image content blocks
|
|
540
|
+
return {
|
|
541
|
+
id: `part_${index}_${msg.uuid}`,
|
|
542
|
+
type: 'image',
|
|
543
|
+
imageData: {
|
|
544
|
+
id: `img_${index}_${msg.uuid}`,
|
|
545
|
+
data: block.source?.data || '',
|
|
546
|
+
mediaType: block.source?.media_type || 'image/jpeg',
|
|
547
|
+
filename: `image_${index}.jpg` // Default filename since Claude history may not store original filename
|
|
548
|
+
},
|
|
549
|
+
order: index
|
|
550
|
+
};
|
|
551
|
+
} else if (block.type === 'thinking') {
|
|
552
|
+
// Handle thinking content blocks
|
|
553
|
+
return {
|
|
554
|
+
id: `part_${index}_${msg.uuid}`,
|
|
555
|
+
type: 'thinking',
|
|
556
|
+
content: block.thinking || '',
|
|
557
|
+
order: index
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
// Handle other content types
|
|
561
|
+
return {
|
|
562
|
+
id: `part_${index}_${msg.uuid}`,
|
|
563
|
+
type: 'unknown',
|
|
564
|
+
content: JSON.stringify(block),
|
|
565
|
+
order: index
|
|
566
|
+
};
|
|
567
|
+
}).filter((part: any) => part !== null);
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
// GET /api/sessions/_status - Get all sessions status (for monitoring)
|
|
574
|
+
router.get('/_status', (req, res) => {
|
|
575
|
+
try {
|
|
576
|
+
const sessionsInfo = sessionManager.getSessionsInfo();
|
|
577
|
+
res.json({
|
|
578
|
+
activeSessionCount: sessionManager.getActiveSessionCount(),
|
|
579
|
+
sessions: sessionsInfo
|
|
580
|
+
});
|
|
581
|
+
} catch (error) {
|
|
582
|
+
console.error('Failed to get sessions status:', error);
|
|
583
|
+
res.status(500).json({ error: 'Failed to get sessions status' });
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
// GET /api/sessions/:agentId - Get agent sessions
|
|
588
|
+
router.get('/:agentId', (req, res) => {
|
|
589
|
+
try {
|
|
590
|
+
const { agentId } = req.params;
|
|
591
|
+
const { search } = req.query;
|
|
592
|
+
const projectPath = req.query.projectPath as string;
|
|
593
|
+
|
|
594
|
+
// Verify agent exists
|
|
595
|
+
const agent = globalAgentStorage.getAgent(agentId);
|
|
596
|
+
if (!agent) {
|
|
597
|
+
return res.status(404).json({ error: 'Agent not found' });
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
let sessions: any[] = [];
|
|
601
|
+
|
|
602
|
+
// If projectPath is provided, read from Claude Code history
|
|
603
|
+
if (projectPath) {
|
|
604
|
+
console.log('Reading Claude history sessions for project:', projectPath);
|
|
605
|
+
const claudeSessions = readClaudeHistorySessions(projectPath);
|
|
606
|
+
sessions = claudeSessions.map(session => ({
|
|
607
|
+
id: session.id,
|
|
608
|
+
agentId: agentId, // Associate with current agent
|
|
609
|
+
title: session.title,
|
|
610
|
+
createdAt: session.createdAt,
|
|
611
|
+
lastUpdated: session.lastUpdated,
|
|
612
|
+
messageCount: session.messages.length
|
|
613
|
+
}));
|
|
614
|
+
} else {
|
|
615
|
+
// Use project-specific AgentStorage for sessions (existing behavior)
|
|
616
|
+
const agentStorage = getAgentStorageForRequest(req);
|
|
617
|
+
const agentSessions = agentStorage.getAgentSessions(agentId, search as string);
|
|
618
|
+
sessions = agentSessions.map(session => ({
|
|
619
|
+
id: session.id,
|
|
620
|
+
agentId: session.agentId,
|
|
621
|
+
title: session.title,
|
|
622
|
+
createdAt: session.createdAt,
|
|
623
|
+
lastUpdated: session.lastUpdated,
|
|
624
|
+
messageCount: session.messages.length
|
|
625
|
+
}));
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
// Apply search filter if provided
|
|
629
|
+
if (search && typeof search === 'string' && search.trim()) {
|
|
630
|
+
const searchTerm = search.trim().toLowerCase();
|
|
631
|
+
sessions = sessions.filter(session =>
|
|
632
|
+
session.title.toLowerCase().includes(searchTerm)
|
|
633
|
+
);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
res.json({ sessions });
|
|
637
|
+
} catch (error) {
|
|
638
|
+
console.error('Failed to get agent sessions:', error);
|
|
639
|
+
res.status(500).json({ error: 'Failed to retrieve agent sessions' });
|
|
640
|
+
}
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
// GET /api/sessions/:agentId/:sessionId/messages - Get session messages
|
|
644
|
+
router.get('/:agentId/:sessionId/messages', (req, res) => {
|
|
645
|
+
try {
|
|
646
|
+
const { agentId, sessionId } = req.params;
|
|
647
|
+
const projectPath = req.query.projectPath as string;
|
|
648
|
+
|
|
649
|
+
let session: any = null;
|
|
650
|
+
|
|
651
|
+
// If projectPath is provided, read from Claude Code history
|
|
652
|
+
if (projectPath) {
|
|
653
|
+
console.log('Reading Claude history messages for session:', sessionId, 'in project:', projectPath);
|
|
654
|
+
const claudeSessions = readClaudeHistorySessions(projectPath);
|
|
655
|
+
session = claudeSessions.find(s => s.id === sessionId);
|
|
656
|
+
|
|
657
|
+
if (session) {
|
|
658
|
+
console.log('📨 Found session with', session.messages?.length || 0, 'messages');
|
|
659
|
+
console.log('📨 First few messages:', session.messages?.slice(0, 3).map((msg: any) => ({
|
|
660
|
+
role: msg.role,
|
|
661
|
+
hasMessageParts: !!msg.messageParts,
|
|
662
|
+
messagePartsCount: msg.messageParts?.length || 0,
|
|
663
|
+
content: msg.content?.slice(0, 50) + '...'
|
|
664
|
+
})));
|
|
665
|
+
|
|
666
|
+
// Add agentId to match expected format
|
|
667
|
+
session = {
|
|
668
|
+
...session,
|
|
669
|
+
agentId: agentId
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
} else {
|
|
673
|
+
// Use project-specific AgentStorage for sessions (existing behavior)
|
|
674
|
+
const agentStorage = getAgentStorageForRequest(req);
|
|
675
|
+
session = agentStorage.getSession(agentId, sessionId);
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
if (!session) {
|
|
679
|
+
return res.status(404).json({ error: 'Session not found' });
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
res.json({
|
|
683
|
+
sessionId: session.id,
|
|
684
|
+
agentId: session.agentId,
|
|
685
|
+
title: session.title,
|
|
686
|
+
messages: session.messages
|
|
687
|
+
});
|
|
688
|
+
} catch (error) {
|
|
689
|
+
console.error('Failed to get session messages:', error);
|
|
690
|
+
res.status(500).json({ error: 'Failed to retrieve session messages' });
|
|
691
|
+
}
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
// POST /api/sessions/:agentId - Create new session
|
|
695
|
+
router.post('/:agentId', (req, res) => {
|
|
696
|
+
try {
|
|
697
|
+
const { agentId } = req.params;
|
|
698
|
+
|
|
699
|
+
// Verify agent exists
|
|
700
|
+
const agent = globalAgentStorage.getAgent(agentId);
|
|
701
|
+
if (!agent) {
|
|
702
|
+
return res.status(404).json({ error: 'Agent not found' });
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// Use project-specific AgentStorage for sessions
|
|
706
|
+
const agentStorage = getAgentStorageForRequest(req);
|
|
707
|
+
const session = agentStorage.createSession(agentId, req.body.title);
|
|
708
|
+
res.json({ sessionId: session.id, session });
|
|
709
|
+
} catch (error) {
|
|
710
|
+
console.error('Failed to create agent session:', error);
|
|
711
|
+
res.status(500).json({ error: 'Failed to create agent session' });
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
// DELETE /api/sessions/:agentId/:sessionId - Delete session
|
|
716
|
+
router.delete('/:agentId/:sessionId', (req, res) => {
|
|
717
|
+
try {
|
|
718
|
+
const { agentId, sessionId } = req.params;
|
|
719
|
+
|
|
720
|
+
// Use project-specific AgentStorage for sessions
|
|
721
|
+
const agentStorage = getAgentStorageForRequest(req);
|
|
722
|
+
const deleted = agentStorage.deleteSession(agentId, sessionId);
|
|
723
|
+
res.json({ success: deleted });
|
|
724
|
+
} catch (error) {
|
|
725
|
+
console.error('Failed to delete agent session:', error);
|
|
726
|
+
res.status(500).json({ error: 'Failed to delete agent session' });
|
|
727
|
+
}
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
// POST /api/sessions/:agentId/:sessionId/heartbeat - Update session heartbeat
|
|
731
|
+
router.post('/:agentId/:sessionId/heartbeat', (req, res) => {
|
|
732
|
+
try {
|
|
733
|
+
const { sessionId } = req.params;
|
|
734
|
+
|
|
735
|
+
// Update heartbeat in SessionManager
|
|
736
|
+
const success = sessionManager.updateHeartbeat(sessionId);
|
|
737
|
+
|
|
738
|
+
if (success) {
|
|
739
|
+
res.json({
|
|
740
|
+
success: true,
|
|
741
|
+
timestamp: Date.now(),
|
|
742
|
+
message: 'Heartbeat updated successfully'
|
|
743
|
+
});
|
|
744
|
+
} else {
|
|
745
|
+
res.status(404).json({
|
|
746
|
+
success: false,
|
|
747
|
+
error: 'Session not found or not active'
|
|
748
|
+
});
|
|
749
|
+
}
|
|
750
|
+
} catch (error) {
|
|
751
|
+
console.error('Failed to update session heartbeat:', error);
|
|
752
|
+
res.status(500).json({
|
|
753
|
+
success: false,
|
|
754
|
+
error: 'Failed to update heartbeat'
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// DELETE /api/sessions/:agentId/:sessionId/cleanup - Manual cleanup session
|
|
760
|
+
router.delete('/:agentId/:sessionId/cleanup', async (req, res) => {
|
|
761
|
+
try {
|
|
762
|
+
const { sessionId } = req.params;
|
|
763
|
+
|
|
764
|
+
// Manual cleanup session in SessionManager
|
|
765
|
+
const success = await sessionManager.manualCleanupSession(sessionId);
|
|
766
|
+
|
|
767
|
+
if (success) {
|
|
768
|
+
res.json({
|
|
769
|
+
success: true,
|
|
770
|
+
message: 'Session cleaned up successfully'
|
|
771
|
+
});
|
|
772
|
+
} else {
|
|
773
|
+
res.status(404).json({
|
|
774
|
+
success: false,
|
|
775
|
+
error: 'Session not found'
|
|
776
|
+
});
|
|
777
|
+
}
|
|
778
|
+
} catch (error) {
|
|
779
|
+
console.error('Failed to cleanup session:', error);
|
|
780
|
+
res.status(500).json({
|
|
781
|
+
success: false,
|
|
782
|
+
error: 'Failed to cleanup session'
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// GET /api/sessions/:agentId/:sessionId/check - Check if session exists in SessionManager
|
|
788
|
+
router.get('/:agentId/:sessionId/check', (req, res) => {
|
|
789
|
+
try {
|
|
790
|
+
const { sessionId } = req.params;
|
|
791
|
+
|
|
792
|
+
// Check if session exists in SessionManager
|
|
793
|
+
const exists = sessionManager.hasActiveSession(sessionId);
|
|
794
|
+
|
|
795
|
+
res.json({
|
|
796
|
+
exists,
|
|
797
|
+
sessionId,
|
|
798
|
+
message: exists ? 'Session is active in SessionManager' : 'Session not found in SessionManager'
|
|
799
|
+
});
|
|
800
|
+
} catch (error) {
|
|
801
|
+
console.error('Failed to check session:', error);
|
|
802
|
+
res.status(500).json({
|
|
803
|
+
exists: false,
|
|
804
|
+
error: 'Failed to check session'
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
|
|
809
|
+
export default router;
|