cloneloop 0.1.0__py3-none-any.whl

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 (118) hide show
  1. cloneloop-0.1.0.dist-info/METADATA +392 -0
  2. cloneloop-0.1.0.dist-info/RECORD +118 -0
  3. cloneloop-0.1.0.dist-info/WHEEL +4 -0
  4. cloneloop-0.1.0.dist-info/entry_points.txt +5 -0
  5. mcp_ai_supervisor/__init__.py +52 -0
  6. mcp_ai_supervisor/__main__.py +551 -0
  7. mcp_ai_supervisor/debug.py +15 -0
  8. mcp_ai_supervisor/desktop_app/__init__.py +29 -0
  9. mcp_ai_supervisor/desktop_app/desktop_app.py +390 -0
  10. mcp_ai_supervisor/helpers/__init__.py +4 -0
  11. mcp_ai_supervisor/helpers/feedback_helpers.py +140 -0
  12. mcp_ai_supervisor/helpers/image_helpers.py +73 -0
  13. mcp_ai_supervisor/i18n.py +14 -0
  14. mcp_ai_supervisor/log_writer.py +16 -0
  15. mcp_ai_supervisor/logging/__init__.py +8 -0
  16. mcp_ai_supervisor/logging/log_parser.py +293 -0
  17. mcp_ai_supervisor/logging/log_tailer.py +367 -0
  18. mcp_ai_supervisor/py.typed +0 -0
  19. mcp_ai_supervisor/server.py +654 -0
  20. mcp_ai_supervisor/utils/__init__.py +28 -0
  21. mcp_ai_supervisor/utils/error_handler.py +10 -0
  22. mcp_ai_supervisor/utils/memory_monitor.py +11 -0
  23. mcp_ai_supervisor/utils/paths.py +7 -0
  24. mcp_ai_supervisor/utils/resource_manager.py +15 -0
  25. mcp_ai_supervisor/utils/structure_checker.py +291 -0
  26. mcp_ai_supervisor/web/__init__.py +26 -0
  27. mcp_ai_supervisor/web/constants/__init__.py +8 -0
  28. mcp_ai_supervisor/web/constants/message_codes.py +173 -0
  29. mcp_ai_supervisor/web/core/__init__.py +30 -0
  30. mcp_ai_supervisor/web/core/agent_bridge.py +957 -0
  31. mcp_ai_supervisor/web/core/auto_responder.py +332 -0
  32. mcp_ai_supervisor/web/core/context_collector.py +124 -0
  33. mcp_ai_supervisor/web/core/conversation_recorder.py +751 -0
  34. mcp_ai_supervisor/web/core/delegate_manager.py +1193 -0
  35. mcp_ai_supervisor/web/core/feedback_mediator.py +259 -0
  36. mcp_ai_supervisor/web/core/message_channel.py +551 -0
  37. mcp_ai_supervisor/web/core/response_builder.py +333 -0
  38. mcp_ai_supervisor/web/core/scene_detector.py +184 -0
  39. mcp_ai_supervisor/web/core/task_state.py +194 -0
  40. mcp_ai_supervisor/web/locales/en/translation.json +643 -0
  41. mcp_ai_supervisor/web/locales/zh-CN/translation.json +629 -0
  42. mcp_ai_supervisor/web/locales/zh-TW/translation.json +648 -0
  43. mcp_ai_supervisor/web/main.py +747 -0
  44. mcp_ai_supervisor/web/managers/__init__.py +8 -0
  45. mcp_ai_supervisor/web/managers/desktop_manager.py +228 -0
  46. mcp_ai_supervisor/web/managers/server_manager.py +170 -0
  47. mcp_ai_supervisor/web/managers/session_manager.py +515 -0
  48. mcp_ai_supervisor/web/managers/workbench_connector.py +186 -0
  49. mcp_ai_supervisor/web/models/__init__.py +13 -0
  50. mcp_ai_supervisor/web/models/feedback_result.py +16 -0
  51. mcp_ai_supervisor/web/models/feedback_session.py +938 -0
  52. mcp_ai_supervisor/web/models/session_cleaner.py +245 -0
  53. mcp_ai_supervisor/web/models/session_commands.py +213 -0
  54. mcp_ai_supervisor/web/models/session_resolver.py +158 -0
  55. mcp_ai_supervisor/web/models/session_timer.py +242 -0
  56. mcp_ai_supervisor/web/routes/__init__.py +12 -0
  57. mcp_ai_supervisor/web/routes/main_routes.py +965 -0
  58. mcp_ai_supervisor/web/routes/settings_routes.py +195 -0
  59. mcp_ai_supervisor/web/routes/ws_handlers.py +270 -0
  60. mcp_ai_supervisor/web/static/css/audio-management.css +545 -0
  61. mcp_ai_supervisor/web/static/css/notification-settings.css +152 -0
  62. mcp_ai_supervisor/web/static/css/prompt-management.css +566 -0
  63. mcp_ai_supervisor/web/static/css/session-management.css +1428 -0
  64. mcp_ai_supervisor/web/static/css/styles.css +2267 -0
  65. mcp_ai_supervisor/web/static/favicon.ico +0 -0
  66. mcp_ai_supervisor/web/static/icon-192.png +0 -0
  67. mcp_ai_supervisor/web/static/icon.svg +11 -0
  68. mcp_ai_supervisor/web/static/index.html +37 -0
  69. mcp_ai_supervisor/web/static/js/app.js +1721 -0
  70. mcp_ai_supervisor/web/static/js/i18n.js +376 -0
  71. mcp_ai_supervisor/web/static/js/modules/audio/audio-manager.js +610 -0
  72. mcp_ai_supervisor/web/static/js/modules/audio/audio-settings-ui.js +732 -0
  73. mcp_ai_supervisor/web/static/js/modules/connection-monitor.js +435 -0
  74. mcp_ai_supervisor/web/static/js/modules/constants/message-codes.js +168 -0
  75. mcp_ai_supervisor/web/static/js/modules/countdown-manager.js +273 -0
  76. mcp_ai_supervisor/web/static/js/modules/drag-drop-handler.js +677 -0
  77. mcp_ai_supervisor/web/static/js/modules/file-upload-manager.js +555 -0
  78. mcp_ai_supervisor/web/static/js/modules/image-handler.js +199 -0
  79. mcp_ai_supervisor/web/static/js/modules/logger.js +404 -0
  80. mcp_ai_supervisor/web/static/js/modules/notification/notification-manager.js +360 -0
  81. mcp_ai_supervisor/web/static/js/modules/notification/notification-settings.js +344 -0
  82. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-input-buttons.js +427 -0
  83. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-manager.js +414 -0
  84. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-modal.js +458 -0
  85. mcp_ai_supervisor/web/static/js/modules/prompt/prompt-settings-ui.js +524 -0
  86. mcp_ai_supervisor/web/static/js/modules/session/session-data-manager.js +1042 -0
  87. mcp_ai_supervisor/web/static/js/modules/session/session-details-modal.js +594 -0
  88. mcp_ai_supervisor/web/static/js/modules/session/session-ui-renderer.js +836 -0
  89. mcp_ai_supervisor/web/static/js/modules/session-manager.js +1059 -0
  90. mcp_ai_supervisor/web/static/js/modules/settings-manager.js +1002 -0
  91. mcp_ai_supervisor/web/static/js/modules/tab-manager.js +235 -0
  92. mcp_ai_supervisor/web/static/js/modules/textarea-height-manager.js +267 -0
  93. mcp_ai_supervisor/web/static/js/modules/ui-manager.js +578 -0
  94. mcp_ai_supervisor/web/static/js/modules/utils/dom-utils.js +392 -0
  95. mcp_ai_supervisor/web/static/js/modules/utils/status-utils.js +403 -0
  96. mcp_ai_supervisor/web/static/js/modules/utils/time-utils.js +440 -0
  97. mcp_ai_supervisor/web/static/js/modules/utils.js +557 -0
  98. mcp_ai_supervisor/web/static/js/modules/websocket-manager.js +875 -0
  99. mcp_ai_supervisor/web/static/js/modules/workbench-notification.js +103 -0
  100. mcp_ai_supervisor/web/static/js/vendor/marked.min.js +6 -0
  101. mcp_ai_supervisor/web/static/js/vendor/purify.min.js +3 -0
  102. mcp_ai_supervisor/web/templates/components/image-upload.html +43 -0
  103. mcp_ai_supervisor/web/templates/components/settings-card.html +58 -0
  104. mcp_ai_supervisor/web/templates/components/status-indicator.html +31 -0
  105. mcp_ai_supervisor/web/templates/components/toggle-switch.html +19 -0
  106. mcp_ai_supervisor/web/templates/feedback.html +1198 -0
  107. mcp_ai_supervisor/web/templates/index.html +378 -0
  108. mcp_ai_supervisor/web/utils/__init__.py +12 -0
  109. mcp_ai_supervisor/web/utils/browser.py +35 -0
  110. mcp_ai_supervisor/web/utils/compression_config.py +195 -0
  111. mcp_ai_supervisor/web/utils/compression_monitor.py +314 -0
  112. mcp_ai_supervisor/web/utils/ide_opener.py +286 -0
  113. mcp_ai_supervisor/web/utils/network.py +66 -0
  114. mcp_ai_supervisor/web/utils/port_manager.py +340 -0
  115. mcp_ai_supervisor/web/utils/session_cleanup_manager.py +543 -0
  116. mcp_ai_supervisor/web/utils/workbench_client.py +899 -0
  117. mcp_ai_supervisor/workbench/__init__.py +5 -0
  118. mcp_ai_supervisor/workbench/__main__.py +5 -0
@@ -0,0 +1,1042 @@
1
+ /**
2
+ * MCP AI Supervisor - 會話數據管理模組
3
+ * ========================================
4
+ *
5
+ * 負責會話數據的存儲、更新和狀態管理
6
+ */
7
+
8
+ (function() {
9
+ 'use strict';
10
+
11
+ // 確保命名空間存在
12
+ window.MCPFeedback = window.MCPFeedback || {};
13
+ window.MCPFeedback.Session = window.MCPFeedback.Session || {};
14
+
15
+ const TimeUtils = window.MCPFeedback.Utils.Time;
16
+ const StatusUtils = window.MCPFeedback.Utils.Status;
17
+
18
+ /**
19
+ * 會話數據管理器
20
+ */
21
+ function SessionDataManager(options) {
22
+ options = options || {};
23
+
24
+ // 會話數據
25
+ this.currentSession = null;
26
+ this.sessionHistory = [];
27
+ this.lastStatusUpdate = null;
28
+
29
+ // 統計數據
30
+ this.sessionStats = {
31
+ todayCount: 0,
32
+ averageDuration: 0
33
+ };
34
+
35
+ // 設定管理器
36
+ this.settingsManager = options.settingsManager || null;
37
+
38
+ // 回調函數
39
+ this.onSessionChange = options.onSessionChange || null;
40
+ this.onHistoryChange = options.onHistoryChange || null;
41
+ this.onStatsChange = options.onStatsChange || null;
42
+ this.onDataChanged = options.onDataChanged || null;
43
+
44
+ // 初始化:載入歷史記錄並清理過期資料
45
+ // 注意:loadFromServer 是異步的,會在載入完成後自動觸發更新
46
+ this.loadFromServer();
47
+
48
+ console.log('📊 SessionDataManager 初始化完成');
49
+ }
50
+
51
+ /**
52
+ * 更新當前會話
53
+ */
54
+ SessionDataManager.prototype.updateCurrentSession = function(sessionData) {
55
+ console.log('📊 更新當前會話:', sessionData);
56
+
57
+ if (this.currentSession && this.currentSession.session_id === sessionData.session_id) {
58
+ // 合併數據,保留重要資訊
59
+ this.currentSession = this.mergeSessionData(this.currentSession, sessionData);
60
+ } else {
61
+ // 新會話或不同會話 ID - 需要處理舊會話
62
+ if (this.currentSession && this.currentSession.session_id) {
63
+ console.log('📊 檢測到會話 ID 變更,處理舊會話:', this.currentSession.session_id, '->', sessionData.session_id);
64
+
65
+ // 將舊會話加入歷史記錄,保持其原有狀態
66
+ const oldSession = Object.assign({}, this.currentSession);
67
+
68
+ // 完全保持舊會話的原有狀態,不做任何修改
69
+ // 讓服務器端負責狀態轉換,前端只負責顯示
70
+ console.log('📊 保持舊會話的原有狀態:', oldSession.status);
71
+
72
+ oldSession.completed_at = TimeUtils.getCurrentTimestamp();
73
+
74
+ // 計算持續時間
75
+ if (oldSession.created_at && !oldSession.duration) {
76
+ oldSession.duration = oldSession.completed_at - oldSession.created_at;
77
+ }
78
+
79
+ console.log('📊 將舊會話加入歷史記錄:', oldSession);
80
+ this.addSessionToHistory(oldSession);
81
+ }
82
+
83
+ // 設置新會話
84
+ this.currentSession = this.normalizeSessionData(sessionData);
85
+ }
86
+
87
+ // 觸發回調
88
+ if (this.onSessionChange) {
89
+ this.onSessionChange(this.currentSession);
90
+ }
91
+
92
+ return this.currentSession;
93
+ };
94
+
95
+ /**
96
+ * 合併會話數據
97
+ */
98
+ SessionDataManager.prototype.mergeSessionData = function(existingData, newData) {
99
+ const merged = Object.assign({}, existingData, newData);
100
+
101
+ // 確保重要欄位不會被覆蓋為空值
102
+ if (!merged.created_at && existingData.created_at) {
103
+ merged.created_at = existingData.created_at;
104
+ }
105
+
106
+ if (!merged.status && existingData.status) {
107
+ merged.status = existingData.status;
108
+ }
109
+
110
+ return merged;
111
+ };
112
+
113
+ /**
114
+ * 標準化會話數據
115
+ */
116
+ SessionDataManager.prototype.normalizeSessionData = function(sessionData) {
117
+ const normalized = Object.assign({}, sessionData);
118
+
119
+ // 補充缺失的時間戳
120
+ if (!normalized.created_at) {
121
+ if (this.lastStatusUpdate && this.lastStatusUpdate.created_at) {
122
+ normalized.created_at = this.lastStatusUpdate.created_at;
123
+ } else {
124
+ normalized.created_at = TimeUtils.getCurrentTimestamp();
125
+ }
126
+ }
127
+
128
+ // 補充缺失的狀態
129
+ if (!normalized.status) {
130
+ normalized.status = 'waiting';
131
+ }
132
+
133
+ // 標準化時間戳
134
+ if (normalized.created_at) {
135
+ normalized.created_at = TimeUtils.normalizeTimestamp(normalized.created_at);
136
+ }
137
+
138
+ return normalized;
139
+ };
140
+
141
+ /**
142
+ * 更新狀態資訊
143
+ */
144
+ SessionDataManager.prototype.updateStatusInfo = function(statusInfo) {
145
+ console.log('📊 更新狀態資訊:', statusInfo);
146
+
147
+ this.lastStatusUpdate = statusInfo;
148
+
149
+ if (statusInfo.session_id || statusInfo.created_at) {
150
+ const sessionData = {
151
+ session_id: statusInfo.session_id || (this.currentSession && this.currentSession.session_id),
152
+ status: statusInfo.status,
153
+ created_at: statusInfo.created_at,
154
+ project_directory: statusInfo.project_directory || this.getProjectDirectory(),
155
+ summary: statusInfo.summary || this.getAISummary()
156
+ };
157
+
158
+ // 檢查會話是否完成
159
+ if (StatusUtils.isCompletedStatus(statusInfo.status)) {
160
+ this.handleSessionCompleted(sessionData);
161
+ } else {
162
+ this.updateCurrentSession(sessionData);
163
+ }
164
+ }
165
+ };
166
+
167
+ /**
168
+ * 處理會話完成
169
+ */
170
+ SessionDataManager.prototype.handleSessionCompleted = function(sessionData) {
171
+ console.log('📊 處理會話完成:', sessionData);
172
+
173
+ // 優先使用用戶最後互動時間作為完成時間
174
+ if (this.currentSession &&
175
+ this.currentSession.session_id === sessionData.session_id &&
176
+ this.currentSession.last_user_interaction) {
177
+ sessionData.completed_at = this.currentSession.last_user_interaction;
178
+ console.log('📊 使用用戶最後互動時間作為完成時間:', sessionData.completed_at);
179
+ } else if (!sessionData.completed_at) {
180
+ sessionData.completed_at = TimeUtils.getCurrentTimestamp();
181
+ console.log('📊 使用當前時間作為完成時間:', sessionData.completed_at);
182
+ }
183
+
184
+ // 計算持續時間
185
+ if (sessionData.created_at && !sessionData.duration) {
186
+ sessionData.duration = sessionData.completed_at - sessionData.created_at;
187
+ }
188
+
189
+ // 確保包含用戶訊息(如果當前會話有的話)
190
+ if (this.currentSession &&
191
+ this.currentSession.session_id === sessionData.session_id &&
192
+ this.currentSession.user_messages) {
193
+ sessionData.user_messages = this.currentSession.user_messages;
194
+ console.log('📊 會話完成時包含', sessionData.user_messages.length, '條用戶訊息');
195
+ }
196
+
197
+ // 將完成的會話加入歷史記錄
198
+ this.addSessionToHistory(sessionData);
199
+
200
+ // 如果是當前會話完成,保持引用但標記為完成
201
+ if (this.currentSession && this.currentSession.session_id === sessionData.session_id) {
202
+ this.currentSession = Object.assign(this.currentSession, sessionData);
203
+ if (this.onSessionChange) {
204
+ this.onSessionChange(this.currentSession);
205
+ }
206
+ }
207
+ };
208
+
209
+ /**
210
+ * 新增會話到歷史記錄
211
+ */
212
+ SessionDataManager.prototype.addSessionToHistory = function(sessionData) {
213
+ console.log('📊 新增會話到歷史記錄:', sessionData);
214
+
215
+ // 只有已完成的會話才加入歷史記錄
216
+ if (!StatusUtils.isCompletedStatus(sessionData.status)) {
217
+ console.log('📊 跳過未完成的會話:', sessionData.session_id);
218
+ return false;
219
+ }
220
+
221
+ // 新增儲存時間戳記
222
+ sessionData.saved_at = TimeUtils.getCurrentTimestamp();
223
+
224
+ // 確保 user_messages 陣列存在(向後相容)
225
+ if (!sessionData.user_messages) {
226
+ sessionData.user_messages = [];
227
+ }
228
+
229
+ // 避免重複新增
230
+ const existingIndex = this.sessionHistory.findIndex(s => s.session_id === sessionData.session_id);
231
+ if (existingIndex !== -1) {
232
+ // 合併用戶訊息記錄
233
+ const existingSession = this.sessionHistory[existingIndex];
234
+ if (existingSession.user_messages && sessionData.user_messages) {
235
+ sessionData.user_messages = this.mergeUserMessages(existingSession.user_messages, sessionData.user_messages);
236
+ }
237
+ this.sessionHistory[existingIndex] = sessionData;
238
+ } else {
239
+ this.sessionHistory.unshift(sessionData);
240
+ }
241
+
242
+ // 限制歷史記錄數量
243
+ if (this.sessionHistory.length > 10) {
244
+ this.sessionHistory = this.sessionHistory.slice(0, 10);
245
+ }
246
+
247
+ // 保存到伺服器端
248
+ this.saveToServer();
249
+
250
+ this.updateStats();
251
+
252
+ // 觸發回調
253
+ if (this.onHistoryChange) {
254
+ this.onHistoryChange(this.sessionHistory);
255
+ }
256
+
257
+ return true;
258
+ };
259
+
260
+ /**
261
+ * 合併用戶訊息記錄
262
+ */
263
+ SessionDataManager.prototype.mergeUserMessages = function(existingMessages, newMessages) {
264
+ const merged = existingMessages.slice(); // 複製現有訊息
265
+
266
+ // 新增不重複的訊息(基於時間戳記去重)
267
+ newMessages.forEach(function(newMsg) {
268
+ const exists = merged.some(function(existingMsg) {
269
+ return existingMsg.timestamp === newMsg.timestamp;
270
+ });
271
+ if (!exists) {
272
+ merged.push(newMsg);
273
+ }
274
+ });
275
+
276
+ // 按時間戳記排序
277
+ merged.sort(function(a, b) {
278
+ return a.timestamp - b.timestamp;
279
+ });
280
+
281
+ return merged;
282
+ };
283
+
284
+ /**
285
+ * 新增用戶訊息到當前會話
286
+ */
287
+ SessionDataManager.prototype.addUserMessage = function(messageData) {
288
+ console.log('📊 新增用戶訊息:', messageData);
289
+
290
+ // 檢查隱私設定
291
+ if (!this.isUserMessageRecordingEnabled()) {
292
+ console.log('📊 用戶訊息記錄已停用,跳過記錄');
293
+ return false;
294
+ }
295
+
296
+ // 檢查是否有當前會話
297
+ if (!this.currentSession || !this.currentSession.session_id) {
298
+ console.warn('📊 沒有當前會話,無法記錄用戶訊息');
299
+ return false;
300
+ }
301
+
302
+ // 確保當前會話有 user_messages 陣列
303
+ if (!this.currentSession.user_messages) {
304
+ this.currentSession.user_messages = [];
305
+ }
306
+
307
+ // 建立用戶訊息記錄
308
+ const userMessage = this.createUserMessageRecord(messageData);
309
+
310
+ // 新增到當前會話
311
+ this.currentSession.user_messages.push(userMessage);
312
+
313
+ // 記錄用戶最後互動時間
314
+ this.currentSession.last_user_interaction = TimeUtils.getCurrentTimestamp();
315
+
316
+ // 發送用戶消息到服務器端
317
+ this.sendUserMessageToServer(userMessage);
318
+
319
+ // 立即保存當前會話到伺服器
320
+ this.saveCurrentSessionToServer();
321
+
322
+ console.log('📊 用戶訊息已記錄到當前會話:', this.currentSession.session_id);
323
+ return true;
324
+ };
325
+
326
+ /**
327
+ * 發送用戶消息到服務器端
328
+ */
329
+ SessionDataManager.prototype.sendUserMessageToServer = function(userMessage) {
330
+ const lang = window.i18nManager ? window.i18nManager.getCurrentLanguage() : 'zh-TW';
331
+ var apiUrlFn = window.MCPFeedback && window.MCPFeedback.Utils && window.MCPFeedback.Utils.apiUrl;
332
+ var url = apiUrlFn ? apiUrlFn.call(window.MCPFeedback.Utils, '/api/add-user-message?lang=' + lang) : ('/api/add-user-message?lang=' + lang);
333
+ fetch(url, {
334
+ method: 'POST',
335
+ headers: {
336
+ 'Content-Type': 'application/json'
337
+ },
338
+ body: JSON.stringify(userMessage)
339
+ })
340
+ .then(function(response) {
341
+ if (response.ok) {
342
+ console.log('📊 用戶消息已發送到服務器端');
343
+ } else {
344
+ console.warn('📊 發送用戶消息到服務器端失敗:', response.status);
345
+ }
346
+ })
347
+ .catch(function(error) {
348
+ console.warn('📊 發送用戶消息到服務器端出錯:', error);
349
+ });
350
+ };
351
+
352
+ /**
353
+ * 建立用戶訊息記錄
354
+ */
355
+ SessionDataManager.prototype.createUserMessageRecord = function(messageData) {
356
+ const timestamp = TimeUtils.getCurrentTimestamp();
357
+ const privacyLevel = this.getUserMessagePrivacyLevel();
358
+
359
+ const record = {
360
+ timestamp: timestamp,
361
+ submission_method: messageData.submission_method || 'manual',
362
+ type: 'feedback'
363
+ };
364
+
365
+ // 根據隱私等級決定記錄內容
366
+ if (privacyLevel === 'full') {
367
+ record.content = messageData.content || '';
368
+ record.images = this.processImageDataForRecord(messageData.images || []);
369
+ } else if (privacyLevel === 'basic') {
370
+ record.content_length = (messageData.content || '').length;
371
+ record.image_count = (messageData.images || []).length;
372
+ record.has_content = !!(messageData.content && messageData.content.trim());
373
+ } else if (privacyLevel === 'disabled') {
374
+ // 停用記錄時,只記錄最基本的時間戳記和提交方式
375
+ record.privacy_note = 'Content recording disabled by user privacy settings';
376
+ }
377
+
378
+ return record;
379
+ };
380
+
381
+ /**
382
+ * 處理圖片資料用於記錄
383
+ */
384
+ SessionDataManager.prototype.processImageDataForRecord = function(images) {
385
+ if (!Array.isArray(images)) {
386
+ return [];
387
+ }
388
+
389
+ return images.map(function(img) {
390
+ return {
391
+ name: img.name || 'unknown',
392
+ size: img.size || 0,
393
+ type: img.type || 'unknown'
394
+ };
395
+ });
396
+ };
397
+
398
+ /**
399
+ * 檢查是否啟用用戶訊息記錄
400
+ */
401
+ SessionDataManager.prototype.isUserMessageRecordingEnabled = function() {
402
+ if (!this.settingsManager) {
403
+ return true; // 預設啟用
404
+ }
405
+
406
+ // 檢查總開關
407
+ const recordingEnabled = this.settingsManager.get('userMessageRecordingEnabled', true);
408
+ if (!recordingEnabled) {
409
+ return false;
410
+ }
411
+
412
+ // 檢查隱私等級(disabled 等級視為停用記錄)
413
+ const privacyLevel = this.settingsManager.get('userMessagePrivacyLevel', 'full');
414
+ return privacyLevel !== 'disabled';
415
+ };
416
+
417
+ /**
418
+ * 獲取用戶訊息隱私等級
419
+ */
420
+ SessionDataManager.prototype.getUserMessagePrivacyLevel = function() {
421
+ if (!this.settingsManager) {
422
+ return 'full'; // 預設完整記錄
423
+ }
424
+ return this.settingsManager.get('userMessagePrivacyLevel', 'full');
425
+ };
426
+
427
+ /**
428
+ * 清空所有會話的用戶訊息記錄
429
+ */
430
+ SessionDataManager.prototype.clearAllUserMessages = function() {
431
+ console.log('📊 清空所有會話的用戶訊息記錄...');
432
+
433
+ // 清空當前會話的用戶訊息
434
+ if (this.currentSession && this.currentSession.user_messages) {
435
+ this.currentSession.user_messages = [];
436
+ }
437
+
438
+ // 清空歷史會話的用戶訊息
439
+ this.sessionHistory.forEach(function(session) {
440
+ if (session.user_messages) {
441
+ session.user_messages = [];
442
+ }
443
+ });
444
+
445
+ // 保存到伺服器端
446
+ this.saveToServer();
447
+
448
+ console.log('📊 所有用戶訊息記錄已清空');
449
+ return true;
450
+ };
451
+
452
+ /**
453
+ * 清空指定會話的用戶訊息記錄
454
+ */
455
+ SessionDataManager.prototype.clearSessionUserMessages = function(sessionId) {
456
+ console.log('📊 清空會話用戶訊息記錄:', sessionId);
457
+
458
+ // 查找並清空指定會話的用戶訊息
459
+ const session = this.sessionHistory.find(function(s) {
460
+ return s.session_id === sessionId;
461
+ });
462
+
463
+ if (session && session.user_messages) {
464
+ session.user_messages = [];
465
+ this.saveToServer();
466
+ console.log('📊 會話用戶訊息記錄已清空:', sessionId);
467
+ return true;
468
+ }
469
+
470
+ console.warn('📊 找不到指定會話或該會話沒有用戶訊息記錄:', sessionId);
471
+ return false;
472
+ };
473
+
474
+ /**
475
+ * 獲取當前會話
476
+ */
477
+ SessionDataManager.prototype.getCurrentSession = function() {
478
+ return this.currentSession;
479
+ };
480
+
481
+ /**
482
+ * 獲取會話歷史
483
+ */
484
+ SessionDataManager.prototype.getSessionHistory = function() {
485
+ return this.sessionHistory.slice(); // 返回副本
486
+ };
487
+
488
+ /**
489
+ * 根據 ID 查找會話(包含完整的用戶消息數據)
490
+ */
491
+ SessionDataManager.prototype.findSessionById = function(sessionId) {
492
+ // 先檢查當前會話
493
+ if (this.currentSession && this.currentSession.session_id === sessionId) {
494
+ console.log('📊 從當前會話獲取數據:', sessionId, '用戶消息數量:', this.currentSession.user_messages ? this.currentSession.user_messages.length : 0);
495
+ return this.currentSession;
496
+ }
497
+
498
+ // 再檢查歷史記錄
499
+ const historySession = this.sessionHistory.find(s => s.session_id === sessionId);
500
+ if (historySession) {
501
+ console.log('📊 從歷史記錄獲取數據:', sessionId, '用戶消息數量:', historySession.user_messages ? historySession.user_messages.length : 0);
502
+ return historySession;
503
+ }
504
+
505
+ console.warn('📊 找不到會話:', sessionId);
506
+ return null;
507
+ };
508
+
509
+ /**
510
+ * 更新統計資訊
511
+ */
512
+ SessionDataManager.prototype.updateStats = function() {
513
+ // 計算今日會話數
514
+ const todayStart = TimeUtils.getTodayStartTimestamp();
515
+ const todaySessions = this.sessionHistory.filter(function(session) {
516
+ return session.created_at && session.created_at >= todayStart;
517
+ });
518
+ this.sessionStats.todayCount = todaySessions.length;
519
+
520
+ // 計算今日平均持續時間
521
+ const todayCompletedSessions = todaySessions.filter(function(s) {
522
+ // 過濾有效的持續時間:大於 0 且小於 24 小時(86400 秒)
523
+ return s.duration && s.duration > 0 && s.duration < 86400;
524
+ });
525
+
526
+ if (todayCompletedSessions.length > 0) {
527
+ const totalDuration = todayCompletedSessions.reduce(function(sum, s) {
528
+ // 確保持續時間是合理的數值
529
+ const duration = Math.min(s.duration, 86400); // 最大 24 小時
530
+ return sum + duration;
531
+ }, 0);
532
+ this.sessionStats.averageDuration = Math.round(totalDuration / todayCompletedSessions.length);
533
+ } else {
534
+ this.sessionStats.averageDuration = 0;
535
+ }
536
+
537
+ // 觸發回調
538
+ if (this.onStatsChange) {
539
+ this.onStatsChange(this.sessionStats);
540
+ }
541
+ };
542
+
543
+ /**
544
+ * 獲取統計資訊
545
+ */
546
+ SessionDataManager.prototype.getStats = function() {
547
+ return Object.assign({}, this.sessionStats);
548
+ };
549
+
550
+ /**
551
+ * 清空會話數據
552
+ */
553
+ SessionDataManager.prototype.clearCurrentSession = function() {
554
+ this.currentSession = null;
555
+ if (this.onSessionChange) {
556
+ this.onSessionChange(null);
557
+ }
558
+ };
559
+
560
+ /**
561
+ * 清空歷史記錄
562
+ */
563
+ SessionDataManager.prototype.clearHistory = function() {
564
+ this.sessionHistory = [];
565
+
566
+ // 清空伺服器端資料
567
+ this.clearServerData();
568
+
569
+ this.updateStats();
570
+ if (this.onHistoryChange) {
571
+ this.onHistoryChange(this.sessionHistory);
572
+ }
573
+ };
574
+
575
+ /**
576
+ * 獲取專案目錄(輔助方法)
577
+ */
578
+ SessionDataManager.prototype.getProjectDirectory = function() {
579
+ // 嘗試從多個來源獲取專案目錄
580
+ const sources = [
581
+ () => document.querySelector('.session-project')?.textContent?.replace('專案: ', ''),
582
+ () => document.querySelector('.project-info')?.textContent?.replace('專案目錄: ', ''),
583
+ () => this.currentSession?.project_directory
584
+ ];
585
+
586
+ for (const source of sources) {
587
+ try {
588
+ const result = source();
589
+ if (result && result !== '未知') {
590
+ return result;
591
+ }
592
+ } catch (error) {
593
+ // 忽略錯誤,繼續嘗試下一個來源
594
+ }
595
+ }
596
+
597
+ return '未知';
598
+ };
599
+
600
+ /**
601
+ * 獲取 AI 摘要(輔助方法)
602
+ */
603
+ SessionDataManager.prototype.getAISummary = function() {
604
+ // 嘗試從多個來源獲取 AI 摘要
605
+ const sources = [
606
+ () => {
607
+ const element = document.querySelector('.session-summary');
608
+ const text = element?.textContent;
609
+ return text && text !== 'AI 摘要: 載入中...' ? text.replace('AI 摘要: ', '') : null;
610
+ },
611
+ () => {
612
+ const element = document.querySelector('#combinedSummaryContent');
613
+ return element?.textContent?.trim();
614
+ },
615
+ () => this.currentSession?.summary
616
+ ];
617
+
618
+ for (const source of sources) {
619
+ try {
620
+ const result = source();
621
+ if (result && result !== '暫無摘要') {
622
+ return result;
623
+ }
624
+ } catch (error) {
625
+ // 忽略錯誤,繼續嘗試下一個來源
626
+ }
627
+ }
628
+
629
+ return '暫無摘要';
630
+ };
631
+
632
+ /**
633
+ * 從伺服器載入會話歷史(包含實時狀態)
634
+ */
635
+ SessionDataManager.prototype.loadFromServer = function() {
636
+ const self = this;
637
+
638
+ // 首先嘗試獲取實時會話狀態
639
+ const lang = window.i18nManager ? window.i18nManager.getCurrentLanguage() : 'zh-TW';
640
+ fetch('/api/all-sessions?lang=' + lang)
641
+ .then(function(response) {
642
+ if (response.ok) {
643
+ return response.json();
644
+ } else {
645
+ throw new Error('獲取實時會話狀態失敗: ' + response.status);
646
+ }
647
+ })
648
+ .then(function(data) {
649
+ if (data && Array.isArray(data.sessions)) {
650
+ // 使用實時會話狀態
651
+ self.sessionHistory = data.sessions;
652
+ console.log('📊 從伺服器載入', self.sessionHistory.length, '個實時會話狀態');
653
+
654
+ // 載入完成後進行清理和統計更新
655
+ self.cleanupExpiredSessions();
656
+ self.updateStats();
657
+
658
+ // 觸發歷史記錄變更回調
659
+ if (self.onHistoryChange) {
660
+ self.onHistoryChange(self.sessionHistory);
661
+ }
662
+
663
+ // 觸發資料變更回調
664
+ if (self.onDataChanged) {
665
+ self.onDataChanged();
666
+ }
667
+ } else {
668
+ console.warn('📊 實時會話狀態回應格式錯誤,回退到歷史文件');
669
+ self.loadFromHistoryFile();
670
+ }
671
+ })
672
+ .catch(function(error) {
673
+ console.warn('📊 獲取實時會話狀態失敗,回退到歷史文件:', error);
674
+ self.loadFromHistoryFile();
675
+ });
676
+ };
677
+
678
+ /**
679
+ * 從歷史文件載入會話數據(備用方案)
680
+ */
681
+ SessionDataManager.prototype.loadFromHistoryFile = function() {
682
+ const self = this;
683
+
684
+ const lang = window.i18nManager ? window.i18nManager.getCurrentLanguage() : 'zh-TW';
685
+ fetch('/api/load-session-history?lang=' + lang)
686
+ .then(function(response) {
687
+ if (response.ok) {
688
+ return response.json();
689
+ } else {
690
+ throw new Error('伺服器回應錯誤: ' + response.status);
691
+ }
692
+ })
693
+ .then(function(data) {
694
+ if (data && Array.isArray(data.sessions)) {
695
+ self.sessionHistory = data.sessions;
696
+ console.log('📊 從歷史文件載入', self.sessionHistory.length, '個會話');
697
+
698
+ // 載入完成後進行清理和統計更新
699
+ self.cleanupExpiredSessions();
700
+ self.updateStats();
701
+
702
+ // 觸發歷史記錄變更回調
703
+ if (self.onHistoryChange) {
704
+ self.onHistoryChange(self.sessionHistory);
705
+ }
706
+
707
+ // 觸發資料變更回調
708
+ if (self.onDataChanged) {
709
+ self.onDataChanged();
710
+ }
711
+ } else {
712
+ console.warn('📊 歷史文件回應格式錯誤:', data);
713
+ self.sessionHistory = [];
714
+ self.updateStats();
715
+
716
+ if (self.onHistoryChange) {
717
+ self.onHistoryChange(self.sessionHistory);
718
+ }
719
+
720
+ if (self.onDataChanged) {
721
+ self.onDataChanged();
722
+ }
723
+ }
724
+ })
725
+ .catch(function(error) {
726
+ console.warn('📊 從歷史文件載入失敗:', error);
727
+ self.sessionHistory = [];
728
+ self.updateStats();
729
+
730
+ if (self.onHistoryChange) {
731
+ self.onHistoryChange(self.sessionHistory);
732
+ }
733
+
734
+ if (self.onDataChanged) {
735
+ self.onDataChanged();
736
+ }
737
+ });
738
+ };
739
+
740
+ /**
741
+ * 立即保存當前會話到伺服器
742
+ */
743
+ SessionDataManager.prototype.saveCurrentSessionToServer = function() {
744
+ if (!this.currentSession) {
745
+ console.log('📊 沒有當前會話,跳過即時保存');
746
+ return;
747
+ }
748
+
749
+ console.log('📊 立即保存當前會話到伺服器:', this.currentSession.session_id);
750
+
751
+ // 建立當前會話的快照(包含用戶訊息)
752
+ const sessionSnapshot = Object.assign({}, this.currentSession);
753
+
754
+ // 確保快照包含在歷史記錄中(用於即時保存)
755
+ const updatedHistory = this.sessionHistory.slice();
756
+ const existingIndex = updatedHistory.findIndex(s => s.session_id === sessionSnapshot.session_id);
757
+
758
+ if (existingIndex !== -1) {
759
+ // 更新現有會話,保留用戶訊息
760
+ const existingSession = updatedHistory[existingIndex];
761
+ if (existingSession.user_messages && sessionSnapshot.user_messages) {
762
+ sessionSnapshot.user_messages = this.mergeUserMessages(existingSession.user_messages, sessionSnapshot.user_messages);
763
+ }
764
+ updatedHistory[existingIndex] = sessionSnapshot;
765
+ } else {
766
+ // 新增會話快照到歷史記錄開頭
767
+ updatedHistory.unshift(sessionSnapshot);
768
+ }
769
+
770
+ // 保存包含當前會話的歷史記錄
771
+ this.saveSessionSnapshot(updatedHistory);
772
+ };
773
+
774
+ /**
775
+ * 保存會話快照到伺服器
776
+ */
777
+ SessionDataManager.prototype.saveSessionSnapshot = function(sessions) {
778
+ const data = {
779
+ sessions: sessions,
780
+ lastCleanup: TimeUtils.getCurrentTimestamp()
781
+ };
782
+
783
+ const lang = window.i18nManager ? window.i18nManager.getCurrentLanguage() : 'zh-TW';
784
+ fetch('/api/save-session-history?lang=' + lang, {
785
+ method: 'POST',
786
+ headers: {
787
+ 'Content-Type': 'application/json',
788
+ },
789
+ body: JSON.stringify(data)
790
+ })
791
+ .then(function(response) {
792
+ if (response.ok) {
793
+ console.log('📊 已保存會話快照到伺服器,包含', data.sessions.length, '個會話');
794
+ return response.json();
795
+ } else {
796
+ throw new Error('伺服器回應錯誤: ' + response.status);
797
+ }
798
+ })
799
+ .then(function(result) {
800
+ if (result.messageCode && window.i18nManager) {
801
+ const message = window.i18nManager.t(result.messageCode, result.params);
802
+ console.log('📊 會話快照保存回應:', message);
803
+ } else {
804
+ console.log('📊 會話快照保存回應:', result.message);
805
+ }
806
+ })
807
+ .catch(function(error) {
808
+ console.error('📊 保存會話快照到伺服器失敗:', error);
809
+ });
810
+ };
811
+
812
+ /**
813
+ * 保存會話歷史到伺服器
814
+ */
815
+ SessionDataManager.prototype.saveToServer = function() {
816
+ const data = {
817
+ sessions: this.sessionHistory,
818
+ lastCleanup: TimeUtils.getCurrentTimestamp()
819
+ };
820
+
821
+ const lang = window.i18nManager ? window.i18nManager.getCurrentLanguage() : 'zh-TW';
822
+ fetch('/api/save-session-history?lang=' + lang, {
823
+ method: 'POST',
824
+ headers: {
825
+ 'Content-Type': 'application/json',
826
+ },
827
+ body: JSON.stringify(data)
828
+ })
829
+ .then(function(response) {
830
+ if (response.ok) {
831
+ console.log('📊 已保存', data.sessions.length, '個會話到伺服器');
832
+ return response.json();
833
+ } else {
834
+ throw new Error('伺服器回應錯誤: ' + response.status);
835
+ }
836
+ })
837
+ .then(function(result) {
838
+ if (result.messageCode && window.i18nManager) {
839
+ const message = window.i18nManager.t(result.messageCode, result.params);
840
+ console.log('📊 伺服器保存回應:', message);
841
+ } else {
842
+ console.log('📊 伺服器保存回應:', result.message);
843
+ }
844
+ })
845
+ .catch(function(error) {
846
+ console.error('📊 保存會話歷史到伺服器失敗:', error);
847
+ });
848
+ };
849
+
850
+ /**
851
+ * 清空伺服器端的會話歷史
852
+ */
853
+ SessionDataManager.prototype.clearServerData = function() {
854
+ const emptyData = {
855
+ sessions: [],
856
+ lastCleanup: TimeUtils.getCurrentTimestamp()
857
+ };
858
+
859
+ fetch('/api/save-session-history', {
860
+ method: 'POST',
861
+ headers: {
862
+ 'Content-Type': 'application/json',
863
+ },
864
+ body: JSON.stringify(emptyData)
865
+ })
866
+ .then(function(response) {
867
+ if (response.ok) {
868
+ console.log('📊 已清空伺服器端的會話歷史');
869
+ } else {
870
+ throw new Error('伺服器回應錯誤: ' + response.status);
871
+ }
872
+ })
873
+ .catch(function(error) {
874
+ console.error('📊 清空伺服器端會話歷史失敗:', error);
875
+ });
876
+ };
877
+
878
+
879
+
880
+ /**
881
+ * 清理過期的會話
882
+ */
883
+ SessionDataManager.prototype.cleanupExpiredSessions = function() {
884
+ if (!this.settingsManager) {
885
+ return;
886
+ }
887
+
888
+ const retentionHours = this.settingsManager.get('sessionHistoryRetentionHours', 72);
889
+ const retentionMs = retentionHours * 60 * 60 * 1000;
890
+ const now = TimeUtils.getCurrentTimestamp();
891
+
892
+ const originalCount = this.sessionHistory.length;
893
+ this.sessionHistory = this.sessionHistory.filter(function(session) {
894
+ const sessionAge = now - (session.saved_at || session.completed_at || session.created_at || 0);
895
+ return sessionAge < retentionMs;
896
+ });
897
+
898
+ const cleanedCount = originalCount - this.sessionHistory.length;
899
+ if (cleanedCount > 0) {
900
+ console.log('📊 清理了', cleanedCount, '個過期會話');
901
+ this.saveToServer();
902
+ }
903
+ };
904
+
905
+ /**
906
+ * 檢查會話是否過期
907
+ */
908
+ SessionDataManager.prototype.isSessionExpired = function(session) {
909
+ if (!this.settingsManager) {
910
+ return false;
911
+ }
912
+
913
+ const retentionHours = this.settingsManager.get('sessionHistoryRetentionHours', 72);
914
+ const retentionMs = retentionHours * 60 * 60 * 1000;
915
+ const now = TimeUtils.getCurrentTimestamp();
916
+ const sessionTime = session.saved_at || session.completed_at || session.created_at || 0;
917
+
918
+ return (now - sessionTime) > retentionMs;
919
+ };
920
+
921
+ /**
922
+ * 匯出會話歷史
923
+ */
924
+ SessionDataManager.prototype.exportSessionHistory = function() {
925
+ const self = this;
926
+ const exportData = {
927
+ exportedAt: new Date().toISOString(),
928
+ sessionCount: this.sessionHistory.length,
929
+ sessions: this.sessionHistory.map(function(session) {
930
+ const sessionData = {
931
+ session_id: session.session_id,
932
+ created_at: session.created_at,
933
+ completed_at: session.completed_at,
934
+ duration: session.duration,
935
+ status: session.status,
936
+ project_directory: session.project_directory,
937
+ ai_summary: session.summary || session.ai_summary,
938
+ saved_at: session.saved_at
939
+ };
940
+
941
+ // 包含用戶訊息記錄(如果存在且允許匯出)
942
+ if (session.user_messages && self.isUserMessageRecordingEnabled()) {
943
+ sessionData.user_messages = session.user_messages;
944
+ sessionData.user_message_count = session.user_messages.length;
945
+ }
946
+
947
+ return sessionData;
948
+ })
949
+ };
950
+
951
+ const filename = 'session-history-' + new Date().toISOString().split('T')[0] + '.json';
952
+ this.downloadJSON(exportData, filename);
953
+
954
+ console.log('📊 匯出了', this.sessionHistory.length, '個會話');
955
+ return filename;
956
+ };
957
+
958
+ /**
959
+ * 匯出單一會話
960
+ */
961
+ SessionDataManager.prototype.exportSingleSession = function(sessionId) {
962
+ const session = this.sessionHistory.find(function(s) {
963
+ return s.session_id === sessionId;
964
+ });
965
+
966
+ if (!session) {
967
+ console.error('📊 找不到會話:', sessionId);
968
+ return null;
969
+ }
970
+
971
+ const sessionData = {
972
+ session_id: session.session_id,
973
+ created_at: session.created_at,
974
+ completed_at: session.completed_at,
975
+ duration: session.duration,
976
+ status: session.status,
977
+ project_directory: session.project_directory,
978
+ ai_summary: session.summary || session.ai_summary,
979
+ saved_at: session.saved_at
980
+ };
981
+
982
+ // 包含用戶訊息記錄(如果存在且允許匯出)
983
+ if (session.user_messages && this.isUserMessageRecordingEnabled()) {
984
+ sessionData.user_messages = session.user_messages;
985
+ sessionData.user_message_count = session.user_messages.length;
986
+ }
987
+
988
+ const exportData = {
989
+ exportedAt: new Date().toISOString(),
990
+ session: sessionData
991
+ };
992
+
993
+ const shortId = sessionId.substring(0, 8);
994
+ const filename = 'session-' + shortId + '-' + new Date().toISOString().split('T')[0] + '.json';
995
+ this.downloadJSON(exportData, filename);
996
+
997
+ console.log('📊 匯出會話:', sessionId);
998
+ return filename;
999
+ };
1000
+
1001
+ /**
1002
+ * 下載 JSON 檔案
1003
+ */
1004
+ SessionDataManager.prototype.downloadJSON = function(data, filename) {
1005
+ try {
1006
+ const jsonString = JSON.stringify(data, null, 2);
1007
+ const blob = new Blob([jsonString], { type: 'application/json' });
1008
+ const url = URL.createObjectURL(blob);
1009
+
1010
+ const a = document.createElement('a');
1011
+ a.href = url;
1012
+ a.download = filename;
1013
+ document.body.appendChild(a);
1014
+ a.click();
1015
+ document.body.removeChild(a);
1016
+ URL.revokeObjectURL(url);
1017
+ } catch (error) {
1018
+ console.error('📊 下載檔案失敗:', error);
1019
+ }
1020
+ };
1021
+
1022
+ /**
1023
+ * 清理資源
1024
+ */
1025
+ SessionDataManager.prototype.cleanup = function() {
1026
+ this.currentSession = null;
1027
+ this.sessionHistory = [];
1028
+ this.lastStatusUpdate = null;
1029
+ this.sessionStats = {
1030
+ todayCount: 0,
1031
+ averageDuration: 0
1032
+ };
1033
+
1034
+ console.log('📊 SessionDataManager 清理完成');
1035
+ };
1036
+
1037
+ // 將 SessionDataManager 加入命名空間
1038
+ window.MCPFeedback.Session.DataManager = SessionDataManager;
1039
+
1040
+ console.log('✅ SessionDataManager 模組載入完成');
1041
+
1042
+ })();