copilot-chat-analyzer 0.0.3 → 0.0.4
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/LICENSE +1 -1
- package/README.md +28 -3
- package/dist/index.cjs +166 -8
- package/dist/index.d.cts +77 -1
- package/dist/index.d.ts +77 -1
- package/dist/index.mjs +166 -8
- package/package.json +1 -1
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
[](https://github.com/dealenx/copilot-chat-analyzer/actions/workflows/quality.yml)
|
|
5
5
|
[](https://badge.fury.io/js/copilot-chat-analyzer)
|
|
6
6
|
|
|
7
|
-
TypeScript библиотека для
|
|
7
|
+
TypeScript библиотека для **анализа** экспортированных чатов GitHub Copilot с поддержкой мониторинга MCP инструментов.
|
|
8
|
+
|
|
9
|
+
> **⚠️ Важно**: Эта библиотека предназначена только для **анализа** уже экспортированных JSON-файлов чата. Она НЕ экспортирует данные чата из VS Code. Для экспорта используйте команду VS Code `workbench.action.chat.export`.
|
|
8
10
|
|
|
9
11
|
## 🤖 AI Assistant Quick Reference
|
|
10
12
|
|
|
@@ -16,9 +18,11 @@ const analyzer = new CopilotChatAnalyzer();
|
|
|
16
18
|
const chatData = JSON.parse(fs.readFileSync("chat.json", "utf8"));
|
|
17
19
|
|
|
18
20
|
// Core methods:
|
|
19
|
-
analyzer.getDialogStatus(chatData); // Returns: 'pending' | 'in_progress' | 'completed' | 'canceled'
|
|
21
|
+
analyzer.getDialogStatus(chatData); // Returns: 'pending' | 'in_progress' | 'completed' | 'canceled' | 'failed'
|
|
20
22
|
analyzer.getRequestsCount(chatData); // Returns: number
|
|
21
|
-
analyzer.getDialogStatusDetails(chatData); // Returns: { status, statusText, hasResult, hasFollowups, isCanceled, lastRequestId }
|
|
23
|
+
analyzer.getDialogStatusDetails(chatData); // Returns: { status, statusText, hasResult, hasFollowups, isCanceled, isFailed, lastRequestId, errorCode?, errorMessage? }
|
|
24
|
+
analyzer.getSessionId(chatData); // Returns: string | null - Extract session ID from chat
|
|
25
|
+
analyzer.getSessionInfo(chatData); // Returns: { sessionId, agentId?, modelId? } | null
|
|
22
26
|
analyzer.getMcpToolMonitoring(chatData); // Returns: MCP tool usage statistics
|
|
23
27
|
|
|
24
28
|
// Dialog statuses:
|
|
@@ -26,6 +30,7 @@ analyzer.getMcpToolMonitoring(chatData); // Returns: MCP tool usage statistics
|
|
|
26
30
|
// - 'in_progress': Has requests but not finished
|
|
27
31
|
// - 'completed': Has followups:[] and not canceled
|
|
28
32
|
// - 'canceled': isCanceled:true in last request
|
|
33
|
+
// - 'failed': Has result.errorDetails in last request (API error)
|
|
29
34
|
```
|
|
30
35
|
|
|
31
36
|
## Особенности
|
|
@@ -146,11 +151,31 @@ const errorCalls = analyzer.getMcpToolErrorCalls(
|
|
|
146
151
|
- `isCanceled: true`
|
|
147
152
|
- Может быть с `followups: []` или без него
|
|
148
153
|
|
|
154
|
+
- **`DialogStatus.FAILED`** (`"failed"`) - Диалог завершился с ошибкой
|
|
155
|
+
|
|
156
|
+
- Есть поле `result.errorDetails` в последнем запросе
|
|
157
|
+
- Содержит `errorCode` и `errorMessage` в деталях статуса
|
|
158
|
+
- Обычно указывает на ошибку API или аутентификации
|
|
159
|
+
|
|
149
160
|
- **`DialogStatus.IN_PROGRESS`** (`"in_progress"`) - Диалог в процессе
|
|
150
161
|
- Отсутствует поле `followups`
|
|
151
162
|
- `isCanceled: false`
|
|
152
163
|
- **Такой статус получается при экспорте во время диалога**
|
|
153
164
|
|
|
165
|
+
## Извлечение информации о сессии
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
// Получить ID сессии (уникален для каждого диалога)
|
|
169
|
+
const sessionId = analyzer.getSessionId(chatData);
|
|
170
|
+
console.log(`Session ID: ${sessionId}`);
|
|
171
|
+
// Пример: "ff72bca6-0dec-4953-b130-a103a97e5380"
|
|
172
|
+
|
|
173
|
+
// Получить полную информацию о сессии
|
|
174
|
+
const sessionInfo = analyzer.getSessionInfo(chatData);
|
|
175
|
+
console.log(sessionInfo);
|
|
176
|
+
// { sessionId: "ff72bca6-...", agentId: "github.copilot.editsAgent", modelId: "copilot/gemini-2.5-pro" }
|
|
177
|
+
```
|
|
178
|
+
|
|
154
179
|
## Примеры использования
|
|
155
180
|
|
|
156
181
|
Смотрите файлы в папке `examples/parse-export-chat-json/`:
|
package/dist/index.cjs
CHANGED
|
@@ -29,7 +29,8 @@ var DialogStatus = {
|
|
|
29
29
|
PENDING: "pending",
|
|
30
30
|
COMPLETED: "completed",
|
|
31
31
|
CANCELED: "canceled",
|
|
32
|
-
IN_PROGRESS: "in_progress"
|
|
32
|
+
IN_PROGRESS: "in_progress",
|
|
33
|
+
FAILED: "failed"
|
|
33
34
|
};
|
|
34
35
|
var CopilotChatAnalyzer = class {
|
|
35
36
|
getRequestsCount(chatData) {
|
|
@@ -38,6 +39,43 @@ var CopilotChatAnalyzer = class {
|
|
|
38
39
|
}
|
|
39
40
|
return chatData.requests.length;
|
|
40
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Extract sessionId from chat data.
|
|
44
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
45
|
+
* All requests in a dialog share the same sessionId.
|
|
46
|
+
*/
|
|
47
|
+
getSessionId(chatData) {
|
|
48
|
+
if (!chatData?.requests?.length) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
for (const request of chatData.requests) {
|
|
52
|
+
const sessionId = request?.result?.metadata?.sessionId;
|
|
53
|
+
if (sessionId && typeof sessionId === "string") {
|
|
54
|
+
return sessionId;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Get full session information including agentId and modelId.
|
|
61
|
+
*/
|
|
62
|
+
getSessionInfo(chatData) {
|
|
63
|
+
const sessionId = this.getSessionId(chatData);
|
|
64
|
+
if (!sessionId) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
for (const request of chatData.requests) {
|
|
68
|
+
const metadata = request?.result?.metadata;
|
|
69
|
+
if (metadata?.sessionId === sessionId) {
|
|
70
|
+
return {
|
|
71
|
+
sessionId,
|
|
72
|
+
agentId: metadata?.agentId,
|
|
73
|
+
modelId: metadata?.modelId
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return { sessionId };
|
|
78
|
+
}
|
|
41
79
|
hasRequests(chatData) {
|
|
42
80
|
return Array.isArray(chatData.requests) && chatData.requests.length > 0;
|
|
43
81
|
}
|
|
@@ -58,6 +96,13 @@ var CopilotChatAnalyzer = class {
|
|
|
58
96
|
if (lastRequest.isCanceled === true) {
|
|
59
97
|
return DialogStatus.CANCELED;
|
|
60
98
|
}
|
|
99
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
100
|
+
if (errorDetails) {
|
|
101
|
+
if (errorDetails.code === "canceled") {
|
|
102
|
+
return DialogStatus.CANCELED;
|
|
103
|
+
}
|
|
104
|
+
return DialogStatus.FAILED;
|
|
105
|
+
}
|
|
61
106
|
if ("followups" in lastRequest && Array.isArray(lastRequest.followups)) {
|
|
62
107
|
if (lastRequest.followups.length === 0) {
|
|
63
108
|
return DialogStatus.COMPLETED;
|
|
@@ -73,18 +118,21 @@ var CopilotChatAnalyzer = class {
|
|
|
73
118
|
if (!this.hasRequests(chatData)) {
|
|
74
119
|
return {
|
|
75
120
|
status: DialogStatus.PENDING,
|
|
76
|
-
statusText: "
|
|
121
|
+
statusText: "Dialog not started",
|
|
77
122
|
hasResult: false,
|
|
78
123
|
hasFollowups: false,
|
|
79
|
-
isCanceled: false
|
|
124
|
+
isCanceled: false,
|
|
125
|
+
isFailed: false
|
|
80
126
|
};
|
|
81
127
|
}
|
|
82
128
|
const lastRequest = this.getLastRequest(chatData);
|
|
129
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
83
130
|
const statusTexts = {
|
|
84
|
-
[DialogStatus.PENDING]: "
|
|
85
|
-
[DialogStatus.COMPLETED]: "
|
|
86
|
-
[DialogStatus.CANCELED]: "
|
|
87
|
-
[DialogStatus.IN_PROGRESS]: "
|
|
131
|
+
[DialogStatus.PENDING]: "Dialog not started",
|
|
132
|
+
[DialogStatus.COMPLETED]: "Dialog completed successfully",
|
|
133
|
+
[DialogStatus.CANCELED]: "Dialog was canceled",
|
|
134
|
+
[DialogStatus.IN_PROGRESS]: "Dialog in progress",
|
|
135
|
+
[DialogStatus.FAILED]: "Dialog failed with error"
|
|
88
136
|
};
|
|
89
137
|
return {
|
|
90
138
|
status,
|
|
@@ -92,7 +140,10 @@ var CopilotChatAnalyzer = class {
|
|
|
92
140
|
hasResult: lastRequest && "result" in lastRequest && lastRequest.result !== null,
|
|
93
141
|
hasFollowups: lastRequest && "followups" in lastRequest,
|
|
94
142
|
isCanceled: lastRequest && lastRequest.isCanceled === true,
|
|
95
|
-
|
|
143
|
+
isFailed: !!errorDetails,
|
|
144
|
+
lastRequestId: lastRequest?.requestId,
|
|
145
|
+
errorCode: errorDetails?.code,
|
|
146
|
+
errorMessage: errorDetails?.message
|
|
96
147
|
};
|
|
97
148
|
}
|
|
98
149
|
extractMcpToolCalls(chatData) {
|
|
@@ -215,6 +266,113 @@ var CopilotChatAnalyzer = class {
|
|
|
215
266
|
});
|
|
216
267
|
return Array.from(uniqueNames);
|
|
217
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* Получить историю запросов пользователя из данных чата
|
|
271
|
+
* @param chatData - данные чата Copilot
|
|
272
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
273
|
+
*/
|
|
274
|
+
getUserRequests(chatData) {
|
|
275
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
276
|
+
return [];
|
|
277
|
+
}
|
|
278
|
+
const userRequests = [];
|
|
279
|
+
chatData.requests.forEach((request, index) => {
|
|
280
|
+
let messageText;
|
|
281
|
+
if (typeof request.message === "string") {
|
|
282
|
+
messageText = request.message;
|
|
283
|
+
} else if (request.message && typeof request.message.text === "string") {
|
|
284
|
+
messageText = request.message.text;
|
|
285
|
+
}
|
|
286
|
+
if (messageText) {
|
|
287
|
+
userRequests.push({
|
|
288
|
+
id: request.variableData?.requestId || request.requestId || `req-${index}`,
|
|
289
|
+
message: messageText,
|
|
290
|
+
timestamp: request.timestamp,
|
|
291
|
+
index
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
});
|
|
295
|
+
return userRequests;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Extract response text from a request object
|
|
299
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
300
|
+
*/
|
|
301
|
+
extractResponseText(request) {
|
|
302
|
+
const parts = [];
|
|
303
|
+
if (Array.isArray(request.response)) {
|
|
304
|
+
for (const item of request.response) {
|
|
305
|
+
if (typeof item === "string") {
|
|
306
|
+
parts.push(item);
|
|
307
|
+
} else if (item.value && typeof item.value === "string") {
|
|
308
|
+
parts.push(item.value);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
313
|
+
if (Array.isArray(toolCallRounds)) {
|
|
314
|
+
for (const round of toolCallRounds) {
|
|
315
|
+
if (round.response && typeof round.response === "string") {
|
|
316
|
+
if (!parts.includes(round.response)) {
|
|
317
|
+
parts.push(round.response);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
return parts.join("\n\n");
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Count total tool calls in a request
|
|
326
|
+
*/
|
|
327
|
+
countToolCalls(request) {
|
|
328
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
329
|
+
if (!Array.isArray(toolCallRounds)) return 0;
|
|
330
|
+
return toolCallRounds.reduce((count, round) => {
|
|
331
|
+
return count + (round.toolCalls?.length || 0);
|
|
332
|
+
}, 0);
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Get AI responses from chat data
|
|
336
|
+
* @param chatData - Copilot chat data
|
|
337
|
+
* @returns array of AI responses
|
|
338
|
+
*/
|
|
339
|
+
getAIResponses(chatData) {
|
|
340
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const aiResponses = [];
|
|
344
|
+
chatData.requests.forEach((request, index) => {
|
|
345
|
+
const message = this.extractResponseText(request);
|
|
346
|
+
const toolCallCount = this.countToolCalls(request);
|
|
347
|
+
aiResponses.push({
|
|
348
|
+
requestId: request.requestId || `req-${index}`,
|
|
349
|
+
responseId: request.responseId,
|
|
350
|
+
message,
|
|
351
|
+
timestamp: request.timestamp,
|
|
352
|
+
index,
|
|
353
|
+
hasToolCalls: toolCallCount > 0,
|
|
354
|
+
toolCallCount
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
return aiResponses;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get full conversation history with paired requests and responses
|
|
361
|
+
* @param chatData - Copilot chat data
|
|
362
|
+
* @returns array of conversation turns (request + response pairs)
|
|
363
|
+
*/
|
|
364
|
+
getConversationHistory(chatData) {
|
|
365
|
+
const userRequests = this.getUserRequests(chatData);
|
|
366
|
+
const aiResponses = this.getAIResponses(chatData);
|
|
367
|
+
return userRequests.map((request, index) => {
|
|
368
|
+
const response = aiResponses.find((r) => r.index === index) || null;
|
|
369
|
+
return {
|
|
370
|
+
index,
|
|
371
|
+
request,
|
|
372
|
+
response
|
|
373
|
+
};
|
|
374
|
+
});
|
|
375
|
+
}
|
|
218
376
|
};
|
|
219
377
|
var index_default = CopilotChatAnalyzer;
|
|
220
378
|
// Annotate the CommonJS export names for ESM import in node:
|
package/dist/index.d.cts
CHANGED
|
@@ -8,7 +8,10 @@ interface DialogStatusDetails {
|
|
|
8
8
|
hasResult: boolean;
|
|
9
9
|
hasFollowups: boolean;
|
|
10
10
|
isCanceled: boolean;
|
|
11
|
+
isFailed: boolean;
|
|
11
12
|
lastRequestId?: string;
|
|
13
|
+
errorCode?: string;
|
|
14
|
+
errorMessage?: string;
|
|
12
15
|
}
|
|
13
16
|
interface McpToolCall {
|
|
14
17
|
toolId: string;
|
|
@@ -38,15 +41,61 @@ interface McpMonitoringSummary {
|
|
|
38
41
|
overallSuccessRate: number;
|
|
39
42
|
tools: McpToolMonitoring[];
|
|
40
43
|
}
|
|
44
|
+
interface UserRequest {
|
|
45
|
+
id: string;
|
|
46
|
+
message: string;
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
index: number;
|
|
49
|
+
}
|
|
50
|
+
interface DialogSession {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
agentId?: string;
|
|
53
|
+
modelId?: string;
|
|
54
|
+
}
|
|
55
|
+
interface AIResponse {
|
|
56
|
+
/** Reference to the original request */
|
|
57
|
+
requestId: string;
|
|
58
|
+
/** Response ID if available */
|
|
59
|
+
responseId?: string;
|
|
60
|
+
/** Aggregated response text (all parts combined) */
|
|
61
|
+
message: string;
|
|
62
|
+
/** Timestamp when response was generated */
|
|
63
|
+
timestamp?: number;
|
|
64
|
+
/** Index in the conversation (0-based) */
|
|
65
|
+
index: number;
|
|
66
|
+
/** Whether response includes tool calls */
|
|
67
|
+
hasToolCalls: boolean;
|
|
68
|
+
/** Number of tool calls in this response */
|
|
69
|
+
toolCallCount: number;
|
|
70
|
+
}
|
|
71
|
+
interface ConversationTurn {
|
|
72
|
+
/** Turn index (0-based) */
|
|
73
|
+
index: number;
|
|
74
|
+
/** User's request */
|
|
75
|
+
request: UserRequest;
|
|
76
|
+
/** AI's response (null if no response yet) */
|
|
77
|
+
response: AIResponse | null;
|
|
78
|
+
}
|
|
41
79
|
declare const DialogStatus: {
|
|
42
80
|
readonly PENDING: "pending";
|
|
43
81
|
readonly COMPLETED: "completed";
|
|
44
82
|
readonly CANCELED: "canceled";
|
|
45
83
|
readonly IN_PROGRESS: "in_progress";
|
|
84
|
+
readonly FAILED: "failed";
|
|
46
85
|
};
|
|
47
86
|
type DialogStatusType = (typeof DialogStatus)[keyof typeof DialogStatus];
|
|
48
87
|
declare class CopilotChatAnalyzer {
|
|
49
88
|
getRequestsCount(chatData: CopilotChatData): number;
|
|
89
|
+
/**
|
|
90
|
+
* Extract sessionId from chat data.
|
|
91
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
92
|
+
* All requests in a dialog share the same sessionId.
|
|
93
|
+
*/
|
|
94
|
+
getSessionId(chatData: CopilotChatData): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Get full session information including agentId and modelId.
|
|
97
|
+
*/
|
|
98
|
+
getSessionInfo(chatData: CopilotChatData): DialogSession | null;
|
|
50
99
|
private hasRequests;
|
|
51
100
|
private getLastRequest;
|
|
52
101
|
getDialogStatus(chatData: CopilotChatData): DialogStatusType;
|
|
@@ -68,6 +117,33 @@ declare class CopilotChatAnalyzer {
|
|
|
68
117
|
* @returns массив названий инструментов
|
|
69
118
|
*/
|
|
70
119
|
getMcpToolNames(chatData: CopilotChatData): string[];
|
|
120
|
+
/**
|
|
121
|
+
* Получить историю запросов пользователя из данных чата
|
|
122
|
+
* @param chatData - данные чата Copilot
|
|
123
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
124
|
+
*/
|
|
125
|
+
getUserRequests(chatData: CopilotChatData): UserRequest[];
|
|
126
|
+
/**
|
|
127
|
+
* Extract response text from a request object
|
|
128
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
129
|
+
*/
|
|
130
|
+
private extractResponseText;
|
|
131
|
+
/**
|
|
132
|
+
* Count total tool calls in a request
|
|
133
|
+
*/
|
|
134
|
+
private countToolCalls;
|
|
135
|
+
/**
|
|
136
|
+
* Get AI responses from chat data
|
|
137
|
+
* @param chatData - Copilot chat data
|
|
138
|
+
* @returns array of AI responses
|
|
139
|
+
*/
|
|
140
|
+
getAIResponses(chatData: CopilotChatData): AIResponse[];
|
|
141
|
+
/**
|
|
142
|
+
* Get full conversation history with paired requests and responses
|
|
143
|
+
* @param chatData - Copilot chat data
|
|
144
|
+
* @returns array of conversation turns (request + response pairs)
|
|
145
|
+
*/
|
|
146
|
+
getConversationHistory(chatData: CopilotChatData): ConversationTurn[];
|
|
71
147
|
}
|
|
72
148
|
|
|
73
|
-
export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, CopilotChatAnalyzer as default };
|
|
149
|
+
export { type AIResponse, type ConversationTurn, CopilotChatAnalyzer, type DialogSession, DialogStatus, type DialogStatusDetails, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, type UserRequest, CopilotChatAnalyzer as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,10 @@ interface DialogStatusDetails {
|
|
|
8
8
|
hasResult: boolean;
|
|
9
9
|
hasFollowups: boolean;
|
|
10
10
|
isCanceled: boolean;
|
|
11
|
+
isFailed: boolean;
|
|
11
12
|
lastRequestId?: string;
|
|
13
|
+
errorCode?: string;
|
|
14
|
+
errorMessage?: string;
|
|
12
15
|
}
|
|
13
16
|
interface McpToolCall {
|
|
14
17
|
toolId: string;
|
|
@@ -38,15 +41,61 @@ interface McpMonitoringSummary {
|
|
|
38
41
|
overallSuccessRate: number;
|
|
39
42
|
tools: McpToolMonitoring[];
|
|
40
43
|
}
|
|
44
|
+
interface UserRequest {
|
|
45
|
+
id: string;
|
|
46
|
+
message: string;
|
|
47
|
+
timestamp?: number;
|
|
48
|
+
index: number;
|
|
49
|
+
}
|
|
50
|
+
interface DialogSession {
|
|
51
|
+
sessionId: string;
|
|
52
|
+
agentId?: string;
|
|
53
|
+
modelId?: string;
|
|
54
|
+
}
|
|
55
|
+
interface AIResponse {
|
|
56
|
+
/** Reference to the original request */
|
|
57
|
+
requestId: string;
|
|
58
|
+
/** Response ID if available */
|
|
59
|
+
responseId?: string;
|
|
60
|
+
/** Aggregated response text (all parts combined) */
|
|
61
|
+
message: string;
|
|
62
|
+
/** Timestamp when response was generated */
|
|
63
|
+
timestamp?: number;
|
|
64
|
+
/** Index in the conversation (0-based) */
|
|
65
|
+
index: number;
|
|
66
|
+
/** Whether response includes tool calls */
|
|
67
|
+
hasToolCalls: boolean;
|
|
68
|
+
/** Number of tool calls in this response */
|
|
69
|
+
toolCallCount: number;
|
|
70
|
+
}
|
|
71
|
+
interface ConversationTurn {
|
|
72
|
+
/** Turn index (0-based) */
|
|
73
|
+
index: number;
|
|
74
|
+
/** User's request */
|
|
75
|
+
request: UserRequest;
|
|
76
|
+
/** AI's response (null if no response yet) */
|
|
77
|
+
response: AIResponse | null;
|
|
78
|
+
}
|
|
41
79
|
declare const DialogStatus: {
|
|
42
80
|
readonly PENDING: "pending";
|
|
43
81
|
readonly COMPLETED: "completed";
|
|
44
82
|
readonly CANCELED: "canceled";
|
|
45
83
|
readonly IN_PROGRESS: "in_progress";
|
|
84
|
+
readonly FAILED: "failed";
|
|
46
85
|
};
|
|
47
86
|
type DialogStatusType = (typeof DialogStatus)[keyof typeof DialogStatus];
|
|
48
87
|
declare class CopilotChatAnalyzer {
|
|
49
88
|
getRequestsCount(chatData: CopilotChatData): number;
|
|
89
|
+
/**
|
|
90
|
+
* Extract sessionId from chat data.
|
|
91
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
92
|
+
* All requests in a dialog share the same sessionId.
|
|
93
|
+
*/
|
|
94
|
+
getSessionId(chatData: CopilotChatData): string | null;
|
|
95
|
+
/**
|
|
96
|
+
* Get full session information including agentId and modelId.
|
|
97
|
+
*/
|
|
98
|
+
getSessionInfo(chatData: CopilotChatData): DialogSession | null;
|
|
50
99
|
private hasRequests;
|
|
51
100
|
private getLastRequest;
|
|
52
101
|
getDialogStatus(chatData: CopilotChatData): DialogStatusType;
|
|
@@ -68,6 +117,33 @@ declare class CopilotChatAnalyzer {
|
|
|
68
117
|
* @returns массив названий инструментов
|
|
69
118
|
*/
|
|
70
119
|
getMcpToolNames(chatData: CopilotChatData): string[];
|
|
120
|
+
/**
|
|
121
|
+
* Получить историю запросов пользователя из данных чата
|
|
122
|
+
* @param chatData - данные чата Copilot
|
|
123
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
124
|
+
*/
|
|
125
|
+
getUserRequests(chatData: CopilotChatData): UserRequest[];
|
|
126
|
+
/**
|
|
127
|
+
* Extract response text from a request object
|
|
128
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
129
|
+
*/
|
|
130
|
+
private extractResponseText;
|
|
131
|
+
/**
|
|
132
|
+
* Count total tool calls in a request
|
|
133
|
+
*/
|
|
134
|
+
private countToolCalls;
|
|
135
|
+
/**
|
|
136
|
+
* Get AI responses from chat data
|
|
137
|
+
* @param chatData - Copilot chat data
|
|
138
|
+
* @returns array of AI responses
|
|
139
|
+
*/
|
|
140
|
+
getAIResponses(chatData: CopilotChatData): AIResponse[];
|
|
141
|
+
/**
|
|
142
|
+
* Get full conversation history with paired requests and responses
|
|
143
|
+
* @param chatData - Copilot chat data
|
|
144
|
+
* @returns array of conversation turns (request + response pairs)
|
|
145
|
+
*/
|
|
146
|
+
getConversationHistory(chatData: CopilotChatData): ConversationTurn[];
|
|
71
147
|
}
|
|
72
148
|
|
|
73
|
-
export { CopilotChatAnalyzer, DialogStatus, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, CopilotChatAnalyzer as default };
|
|
149
|
+
export { type AIResponse, type ConversationTurn, CopilotChatAnalyzer, type DialogSession, DialogStatus, type DialogStatusDetails, type DialogStatusType, type McpMonitoringSummary, type McpToolCall, type McpToolMonitoring, type UserRequest, CopilotChatAnalyzer as default };
|
package/dist/index.mjs
CHANGED
|
@@ -3,7 +3,8 @@ var DialogStatus = {
|
|
|
3
3
|
PENDING: "pending",
|
|
4
4
|
COMPLETED: "completed",
|
|
5
5
|
CANCELED: "canceled",
|
|
6
|
-
IN_PROGRESS: "in_progress"
|
|
6
|
+
IN_PROGRESS: "in_progress",
|
|
7
|
+
FAILED: "failed"
|
|
7
8
|
};
|
|
8
9
|
var CopilotChatAnalyzer = class {
|
|
9
10
|
getRequestsCount(chatData) {
|
|
@@ -12,6 +13,43 @@ var CopilotChatAnalyzer = class {
|
|
|
12
13
|
}
|
|
13
14
|
return chatData.requests.length;
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract sessionId from chat data.
|
|
18
|
+
* The sessionId is stored in result.metadata.sessionId of any request.
|
|
19
|
+
* All requests in a dialog share the same sessionId.
|
|
20
|
+
*/
|
|
21
|
+
getSessionId(chatData) {
|
|
22
|
+
if (!chatData?.requests?.length) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
for (const request of chatData.requests) {
|
|
26
|
+
const sessionId = request?.result?.metadata?.sessionId;
|
|
27
|
+
if (sessionId && typeof sessionId === "string") {
|
|
28
|
+
return sessionId;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get full session information including agentId and modelId.
|
|
35
|
+
*/
|
|
36
|
+
getSessionInfo(chatData) {
|
|
37
|
+
const sessionId = this.getSessionId(chatData);
|
|
38
|
+
if (!sessionId) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
for (const request of chatData.requests) {
|
|
42
|
+
const metadata = request?.result?.metadata;
|
|
43
|
+
if (metadata?.sessionId === sessionId) {
|
|
44
|
+
return {
|
|
45
|
+
sessionId,
|
|
46
|
+
agentId: metadata?.agentId,
|
|
47
|
+
modelId: metadata?.modelId
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { sessionId };
|
|
52
|
+
}
|
|
15
53
|
hasRequests(chatData) {
|
|
16
54
|
return Array.isArray(chatData.requests) && chatData.requests.length > 0;
|
|
17
55
|
}
|
|
@@ -32,6 +70,13 @@ var CopilotChatAnalyzer = class {
|
|
|
32
70
|
if (lastRequest.isCanceled === true) {
|
|
33
71
|
return DialogStatus.CANCELED;
|
|
34
72
|
}
|
|
73
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
74
|
+
if (errorDetails) {
|
|
75
|
+
if (errorDetails.code === "canceled") {
|
|
76
|
+
return DialogStatus.CANCELED;
|
|
77
|
+
}
|
|
78
|
+
return DialogStatus.FAILED;
|
|
79
|
+
}
|
|
35
80
|
if ("followups" in lastRequest && Array.isArray(lastRequest.followups)) {
|
|
36
81
|
if (lastRequest.followups.length === 0) {
|
|
37
82
|
return DialogStatus.COMPLETED;
|
|
@@ -47,18 +92,21 @@ var CopilotChatAnalyzer = class {
|
|
|
47
92
|
if (!this.hasRequests(chatData)) {
|
|
48
93
|
return {
|
|
49
94
|
status: DialogStatus.PENDING,
|
|
50
|
-
statusText: "
|
|
95
|
+
statusText: "Dialog not started",
|
|
51
96
|
hasResult: false,
|
|
52
97
|
hasFollowups: false,
|
|
53
|
-
isCanceled: false
|
|
98
|
+
isCanceled: false,
|
|
99
|
+
isFailed: false
|
|
54
100
|
};
|
|
55
101
|
}
|
|
56
102
|
const lastRequest = this.getLastRequest(chatData);
|
|
103
|
+
const errorDetails = lastRequest?.result?.errorDetails;
|
|
57
104
|
const statusTexts = {
|
|
58
|
-
[DialogStatus.PENDING]: "
|
|
59
|
-
[DialogStatus.COMPLETED]: "
|
|
60
|
-
[DialogStatus.CANCELED]: "
|
|
61
|
-
[DialogStatus.IN_PROGRESS]: "
|
|
105
|
+
[DialogStatus.PENDING]: "Dialog not started",
|
|
106
|
+
[DialogStatus.COMPLETED]: "Dialog completed successfully",
|
|
107
|
+
[DialogStatus.CANCELED]: "Dialog was canceled",
|
|
108
|
+
[DialogStatus.IN_PROGRESS]: "Dialog in progress",
|
|
109
|
+
[DialogStatus.FAILED]: "Dialog failed with error"
|
|
62
110
|
};
|
|
63
111
|
return {
|
|
64
112
|
status,
|
|
@@ -66,7 +114,10 @@ var CopilotChatAnalyzer = class {
|
|
|
66
114
|
hasResult: lastRequest && "result" in lastRequest && lastRequest.result !== null,
|
|
67
115
|
hasFollowups: lastRequest && "followups" in lastRequest,
|
|
68
116
|
isCanceled: lastRequest && lastRequest.isCanceled === true,
|
|
69
|
-
|
|
117
|
+
isFailed: !!errorDetails,
|
|
118
|
+
lastRequestId: lastRequest?.requestId,
|
|
119
|
+
errorCode: errorDetails?.code,
|
|
120
|
+
errorMessage: errorDetails?.message
|
|
70
121
|
};
|
|
71
122
|
}
|
|
72
123
|
extractMcpToolCalls(chatData) {
|
|
@@ -189,6 +240,113 @@ var CopilotChatAnalyzer = class {
|
|
|
189
240
|
});
|
|
190
241
|
return Array.from(uniqueNames);
|
|
191
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Получить историю запросов пользователя из данных чата
|
|
245
|
+
* @param chatData - данные чата Copilot
|
|
246
|
+
* @returns массив запросов пользователя с текстом сообщения
|
|
247
|
+
*/
|
|
248
|
+
getUserRequests(chatData) {
|
|
249
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
const userRequests = [];
|
|
253
|
+
chatData.requests.forEach((request, index) => {
|
|
254
|
+
let messageText;
|
|
255
|
+
if (typeof request.message === "string") {
|
|
256
|
+
messageText = request.message;
|
|
257
|
+
} else if (request.message && typeof request.message.text === "string") {
|
|
258
|
+
messageText = request.message.text;
|
|
259
|
+
}
|
|
260
|
+
if (messageText) {
|
|
261
|
+
userRequests.push({
|
|
262
|
+
id: request.variableData?.requestId || request.requestId || `req-${index}`,
|
|
263
|
+
message: messageText,
|
|
264
|
+
timestamp: request.timestamp,
|
|
265
|
+
index
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
return userRequests;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Extract response text from a request object
|
|
273
|
+
* Aggregates text from response[] and toolCallRounds[].response
|
|
274
|
+
*/
|
|
275
|
+
extractResponseText(request) {
|
|
276
|
+
const parts = [];
|
|
277
|
+
if (Array.isArray(request.response)) {
|
|
278
|
+
for (const item of request.response) {
|
|
279
|
+
if (typeof item === "string") {
|
|
280
|
+
parts.push(item);
|
|
281
|
+
} else if (item.value && typeof item.value === "string") {
|
|
282
|
+
parts.push(item.value);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
287
|
+
if (Array.isArray(toolCallRounds)) {
|
|
288
|
+
for (const round of toolCallRounds) {
|
|
289
|
+
if (round.response && typeof round.response === "string") {
|
|
290
|
+
if (!parts.includes(round.response)) {
|
|
291
|
+
parts.push(round.response);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
return parts.join("\n\n");
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Count total tool calls in a request
|
|
300
|
+
*/
|
|
301
|
+
countToolCalls(request) {
|
|
302
|
+
const toolCallRounds = request.result?.metadata?.toolCallRounds;
|
|
303
|
+
if (!Array.isArray(toolCallRounds)) return 0;
|
|
304
|
+
return toolCallRounds.reduce((count, round) => {
|
|
305
|
+
return count + (round.toolCalls?.length || 0);
|
|
306
|
+
}, 0);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Get AI responses from chat data
|
|
310
|
+
* @param chatData - Copilot chat data
|
|
311
|
+
* @returns array of AI responses
|
|
312
|
+
*/
|
|
313
|
+
getAIResponses(chatData) {
|
|
314
|
+
if (!chatData || !Array.isArray(chatData.requests)) {
|
|
315
|
+
return [];
|
|
316
|
+
}
|
|
317
|
+
const aiResponses = [];
|
|
318
|
+
chatData.requests.forEach((request, index) => {
|
|
319
|
+
const message = this.extractResponseText(request);
|
|
320
|
+
const toolCallCount = this.countToolCalls(request);
|
|
321
|
+
aiResponses.push({
|
|
322
|
+
requestId: request.requestId || `req-${index}`,
|
|
323
|
+
responseId: request.responseId,
|
|
324
|
+
message,
|
|
325
|
+
timestamp: request.timestamp,
|
|
326
|
+
index,
|
|
327
|
+
hasToolCalls: toolCallCount > 0,
|
|
328
|
+
toolCallCount
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
return aiResponses;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Get full conversation history with paired requests and responses
|
|
335
|
+
* @param chatData - Copilot chat data
|
|
336
|
+
* @returns array of conversation turns (request + response pairs)
|
|
337
|
+
*/
|
|
338
|
+
getConversationHistory(chatData) {
|
|
339
|
+
const userRequests = this.getUserRequests(chatData);
|
|
340
|
+
const aiResponses = this.getAIResponses(chatData);
|
|
341
|
+
return userRequests.map((request, index) => {
|
|
342
|
+
const response = aiResponses.find((r) => r.index === index) || null;
|
|
343
|
+
return {
|
|
344
|
+
index,
|
|
345
|
+
request,
|
|
346
|
+
response
|
|
347
|
+
};
|
|
348
|
+
});
|
|
349
|
+
}
|
|
192
350
|
};
|
|
193
351
|
var index_default = CopilotChatAnalyzer;
|
|
194
352
|
export {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "copilot-chat-analyzer",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.4",
|
|
5
5
|
"description": "TypeScript library for analyzing and monitoring GitHub Copilot chat exports with status detection, request counting, and MCP tool tracking",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"copilot",
|