vibesurf 0.1.9a6__py3-none-any.whl → 0.1.11__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.

Potentially problematic release.


This version of vibesurf might be problematic. Click here for more details.

Files changed (69) hide show
  1. vibe_surf/_version.py +2 -2
  2. vibe_surf/agents/browser_use_agent.py +68 -45
  3. vibe_surf/agents/prompts/report_writer_prompt.py +73 -0
  4. vibe_surf/agents/prompts/vibe_surf_prompt.py +85 -172
  5. vibe_surf/agents/report_writer_agent.py +380 -226
  6. vibe_surf/agents/vibe_surf_agent.py +878 -814
  7. vibe_surf/agents/views.py +130 -0
  8. vibe_surf/backend/api/activity.py +3 -1
  9. vibe_surf/backend/api/browser.py +70 -0
  10. vibe_surf/backend/api/config.py +8 -5
  11. vibe_surf/backend/api/files.py +59 -50
  12. vibe_surf/backend/api/models.py +2 -2
  13. vibe_surf/backend/api/task.py +47 -13
  14. vibe_surf/backend/database/manager.py +24 -18
  15. vibe_surf/backend/database/queries.py +199 -192
  16. vibe_surf/backend/database/schemas.py +1 -1
  17. vibe_surf/backend/main.py +80 -3
  18. vibe_surf/backend/shared_state.py +30 -35
  19. vibe_surf/backend/utils/encryption.py +3 -1
  20. vibe_surf/backend/utils/llm_factory.py +41 -36
  21. vibe_surf/browser/agent_browser_session.py +308 -62
  22. vibe_surf/browser/browser_manager.py +71 -100
  23. vibe_surf/browser/utils.py +5 -3
  24. vibe_surf/browser/watchdogs/dom_watchdog.py +0 -45
  25. vibe_surf/chrome_extension/background.js +88 -0
  26. vibe_surf/chrome_extension/manifest.json +3 -1
  27. vibe_surf/chrome_extension/scripts/api-client.js +13 -0
  28. vibe_surf/chrome_extension/scripts/file-manager.js +482 -0
  29. vibe_surf/chrome_extension/scripts/history-manager.js +658 -0
  30. vibe_surf/chrome_extension/scripts/modal-manager.js +487 -0
  31. vibe_surf/chrome_extension/scripts/session-manager.js +52 -11
  32. vibe_surf/chrome_extension/scripts/settings-manager.js +1214 -0
  33. vibe_surf/chrome_extension/scripts/ui-manager.js +1530 -3163
  34. vibe_surf/chrome_extension/sidepanel.html +47 -7
  35. vibe_surf/chrome_extension/styles/activity.css +934 -0
  36. vibe_surf/chrome_extension/styles/base.css +76 -0
  37. vibe_surf/chrome_extension/styles/history-modal.css +791 -0
  38. vibe_surf/chrome_extension/styles/input.css +568 -0
  39. vibe_surf/chrome_extension/styles/layout.css +186 -0
  40. vibe_surf/chrome_extension/styles/responsive.css +454 -0
  41. vibe_surf/chrome_extension/styles/settings-environment.css +165 -0
  42. vibe_surf/chrome_extension/styles/settings-forms.css +389 -0
  43. vibe_surf/chrome_extension/styles/settings-modal.css +141 -0
  44. vibe_surf/chrome_extension/styles/settings-profiles.css +244 -0
  45. vibe_surf/chrome_extension/styles/settings-responsive.css +144 -0
  46. vibe_surf/chrome_extension/styles/settings-utilities.css +25 -0
  47. vibe_surf/chrome_extension/styles/variables.css +54 -0
  48. vibe_surf/cli.py +5 -22
  49. vibe_surf/common.py +35 -0
  50. vibe_surf/llm/openai_compatible.py +148 -93
  51. vibe_surf/logger.py +99 -0
  52. vibe_surf/{controller/vibesurf_tools.py → tools/browser_use_tools.py} +233 -221
  53. vibe_surf/tools/file_system.py +415 -0
  54. vibe_surf/{controller → tools}/mcp_client.py +4 -3
  55. vibe_surf/tools/report_writer_tools.py +21 -0
  56. vibe_surf/tools/vibesurf_tools.py +657 -0
  57. vibe_surf/tools/views.py +120 -0
  58. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/METADATA +23 -3
  59. vibesurf-0.1.11.dist-info/RECORD +93 -0
  60. vibe_surf/chrome_extension/styles/main.css +0 -2338
  61. vibe_surf/chrome_extension/styles/settings.css +0 -1100
  62. vibe_surf/controller/file_system.py +0 -53
  63. vibe_surf/controller/views.py +0 -37
  64. vibesurf-0.1.9a6.dist-info/RECORD +0 -71
  65. /vibe_surf/{controller → tools}/__init__.py +0 -0
  66. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/WHEEL +0 -0
  67. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/entry_points.txt +0 -0
  68. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/licenses/LICENSE +0 -0
  69. {vibesurf-0.1.9a6.dist-info → vibesurf-0.1.11.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,487 @@
1
+ // Modal Manager - Handles modal dialogs and user confirmations
2
+ // Manages warning modals, confirmation dialogs, and generic modal utilities
3
+
4
+ class VibeSurfModalManager {
5
+ constructor() {
6
+ this.state = {
7
+ activeModals: new Set(),
8
+ modalCounter: 0
9
+ };
10
+ this.eventListeners = new Map();
11
+
12
+ this.bindGlobalEvents();
13
+ }
14
+
15
+ bindGlobalEvents() {
16
+ // Handle escape key to close modals
17
+ document.addEventListener('keydown', (e) => {
18
+ if (e.key === 'Escape') {
19
+ this.closeTopModal();
20
+ }
21
+ });
22
+
23
+ // Handle background clicks to close modals
24
+ document.addEventListener('click', (e) => {
25
+ if (e.target.classList.contains('modal-overlay')) {
26
+ this.closeModal(e.target.querySelector('.modal'));
27
+ }
28
+ });
29
+ }
30
+
31
+ // Event system for communicating with main UI manager
32
+ on(event, callback) {
33
+ if (!this.eventListeners.has(event)) {
34
+ this.eventListeners.set(event, []);
35
+ }
36
+ this.eventListeners.get(event).push(callback);
37
+ }
38
+
39
+ emit(event, data) {
40
+ if (this.eventListeners.has(event)) {
41
+ this.eventListeners.get(event).forEach(callback => {
42
+ try {
43
+ callback(data);
44
+ } catch (error) {
45
+ console.error(`[ModalManager] Event callback error for ${event}:`, error);
46
+ }
47
+ });
48
+ }
49
+ }
50
+
51
+ // Warning Modal
52
+ showWarningModal(title, message, options = {}) {
53
+ const {
54
+ confirmText = 'OK',
55
+ showCancel = false,
56
+ cancelText = 'Cancel',
57
+ onConfirm = null,
58
+ onCancel = null,
59
+ className = ''
60
+ } = options;
61
+
62
+ const modalId = this.generateModalId();
63
+
64
+ const modalHTML = `
65
+ <div class="modal-overlay" id="${modalId}-overlay">
66
+ <div class="modal warning-modal ${className}" id="${modalId}">
67
+ <div class="modal-header">
68
+ <h3>${this.escapeHtml(title)}</h3>
69
+ <button class="modal-close-btn" data-modal-id="${modalId}">×</button>
70
+ </div>
71
+ <div class="modal-body">
72
+ <div class="warning-icon">⚠️</div>
73
+ <p>${this.escapeHtml(message)}</p>
74
+ </div>
75
+ <div class="modal-footer">
76
+ ${showCancel ? `<button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>` : ''}
77
+ <button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}">${this.escapeHtml(confirmText)}</button>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ `;
82
+
83
+ // Add to DOM
84
+ document.body.insertAdjacentHTML('beforeend', modalHTML);
85
+
86
+ const modal = document.getElementById(modalId);
87
+ const overlay = document.getElementById(`${modalId}-overlay`);
88
+
89
+ // Track active modal
90
+ this.state.activeModals.add(modalId);
91
+
92
+ // Add event listeners
93
+ this.bindModalEvents(modalId, { onConfirm, onCancel });
94
+
95
+ // Show modal
96
+ requestAnimationFrame(() => {
97
+ overlay.classList.add('show');
98
+ });
99
+
100
+ return modalId;
101
+ }
102
+
103
+ // Confirmation Modal
104
+ showConfirmModal(title, message, options = {}) {
105
+ const {
106
+ confirmText = 'Confirm',
107
+ cancelText = 'Cancel',
108
+ onConfirm = null,
109
+ onCancel = null,
110
+ className = '',
111
+ type = 'question' // question, danger, info
112
+ } = options;
113
+
114
+ const modalId = this.generateModalId();
115
+
116
+ const iconMap = {
117
+ question: '❓',
118
+ danger: '🚨',
119
+ info: 'ℹ️'
120
+ };
121
+
122
+ const icon = iconMap[type] || iconMap.question;
123
+
124
+ const modalHTML = `
125
+ <div class="modal-overlay" id="${modalId}-overlay">
126
+ <div class="modal confirm-modal ${className}" id="${modalId}">
127
+ <div class="modal-header">
128
+ <h3>${this.escapeHtml(title)}</h3>
129
+ <button class="modal-close-btn" data-modal-id="${modalId}">×</button>
130
+ </div>
131
+ <div class="modal-body">
132
+ <div class="confirm-icon">${icon}</div>
133
+ <p>${this.escapeHtml(message)}</p>
134
+ </div>
135
+ <div class="modal-footer">
136
+ <button class="btn-secondary modal-cancel-btn" data-modal-id="${modalId}">${this.escapeHtml(cancelText)}</button>
137
+ <button class="btn-primary modal-confirm-btn" data-modal-id="${modalId}" ${type === 'danger' ? 'data-danger="true"' : ''}>${this.escapeHtml(confirmText)}</button>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ `;
142
+
143
+ // Add to DOM
144
+ document.body.insertAdjacentHTML('beforeend', modalHTML);
145
+
146
+ const modal = document.getElementById(modalId);
147
+ const overlay = document.getElementById(`${modalId}-overlay`);
148
+
149
+ // Track active modal
150
+ this.state.activeModals.add(modalId);
151
+
152
+ // Add event listeners
153
+ this.bindModalEvents(modalId, { onConfirm, onCancel });
154
+
155
+ // Show modal
156
+ requestAnimationFrame(() => {
157
+ overlay.classList.add('show');
158
+ });
159
+
160
+ return modalId;
161
+ }
162
+
163
+ // Generic Modal Creator
164
+ createModal(content, options = {}) {
165
+ const {
166
+ title = '',
167
+ className = '',
168
+ showCloseButton = true,
169
+ backdrop = true,
170
+ onShow = null,
171
+ onHide = null
172
+ } = options;
173
+
174
+ const modalId = this.generateModalId();
175
+
176
+ const modalHTML = `
177
+ <div class="modal-overlay ${backdrop ? 'backdrop' : ''}" id="${modalId}-overlay">
178
+ <div class="modal ${className}" id="${modalId}">
179
+ ${title || showCloseButton ? `
180
+ <div class="modal-header">
181
+ ${title ? `<h3>${this.escapeHtml(title)}</h3>` : ''}
182
+ ${showCloseButton ? `<button class="modal-close-btn" data-modal-id="${modalId}">×</button>` : ''}
183
+ </div>
184
+ ` : ''}
185
+ <div class="modal-body">
186
+ ${content}
187
+ </div>
188
+ </div>
189
+ </div>
190
+ `;
191
+
192
+ // Add to DOM
193
+ document.body.insertAdjacentHTML('beforeend', modalHTML);
194
+
195
+ const modal = document.getElementById(modalId);
196
+ const overlay = document.getElementById(`${modalId}-overlay`);
197
+
198
+ // Track active modal
199
+ this.state.activeModals.add(modalId);
200
+
201
+ // Add basic event listeners
202
+ this.bindModalEvents(modalId, { onShow, onHide });
203
+
204
+ // Show modal
205
+ requestAnimationFrame(() => {
206
+ overlay.classList.add('show');
207
+ if (onShow) onShow(modal);
208
+ });
209
+
210
+ return {
211
+ modalId,
212
+ modal,
213
+ overlay,
214
+ close: () => this.closeModal(modal)
215
+ };
216
+ }
217
+
218
+ // Modal Event Binding
219
+ bindModalEvents(modalId, callbacks = {}) {
220
+ const { onConfirm, onCancel, onShow, onHide } = callbacks;
221
+
222
+ // Close button
223
+ const closeBtn = document.querySelector(`[data-modal-id="${modalId}"].modal-close-btn`);
224
+ if (closeBtn) {
225
+ closeBtn.addEventListener('click', () => {
226
+ this.closeModal(document.getElementById(modalId));
227
+ if (onCancel) onCancel();
228
+ });
229
+ }
230
+
231
+ // Confirm button
232
+ const confirmBtn = document.querySelector(`[data-modal-id="${modalId}"].modal-confirm-btn`);
233
+ if (confirmBtn) {
234
+ confirmBtn.addEventListener('click', () => {
235
+ if (onConfirm) {
236
+ const result = onConfirm();
237
+ // Only close if callback doesn't return false
238
+ if (result !== false) {
239
+ this.closeModal(document.getElementById(modalId));
240
+ }
241
+ } else {
242
+ this.closeModal(document.getElementById(modalId));
243
+ }
244
+ });
245
+ }
246
+
247
+ // Cancel button
248
+ const cancelBtn = document.querySelector(`[data-modal-id="${modalId}"].modal-cancel-btn`);
249
+ if (cancelBtn) {
250
+ cancelBtn.addEventListener('click', () => {
251
+ this.closeModal(document.getElementById(modalId));
252
+ if (onCancel) onCancel();
253
+ });
254
+ }
255
+ }
256
+
257
+ // Close Modal
258
+ closeModal(modal) {
259
+ if (!modal) return;
260
+
261
+ const modalId = modal.id;
262
+ const overlay = document.getElementById(`${modalId}-overlay`);
263
+
264
+ if (overlay) {
265
+ overlay.classList.remove('show');
266
+
267
+ // Remove from DOM after animation
268
+ setTimeout(() => {
269
+ if (overlay.parentNode) {
270
+ overlay.parentNode.removeChild(overlay);
271
+ }
272
+ this.state.activeModals.delete(modalId);
273
+ }, 300); // Match CSS transition duration
274
+ }
275
+ }
276
+
277
+ // Close the topmost modal
278
+ closeTopModal() {
279
+ const modalIds = Array.from(this.state.activeModals);
280
+ if (modalIds.length > 0) {
281
+ const topModalId = modalIds[modalIds.length - 1];
282
+ const modal = document.getElementById(topModalId);
283
+ if (modal) {
284
+ this.closeModal(modal);
285
+ }
286
+ }
287
+ }
288
+
289
+ // Close all modals
290
+ closeAllModals() {
291
+ const modalIds = Array.from(this.state.activeModals);
292
+ modalIds.forEach(modalId => {
293
+ const modal = document.getElementById(modalId);
294
+ if (modal) {
295
+ this.closeModal(modal);
296
+ }
297
+ });
298
+ }
299
+
300
+ // Utility Methods
301
+ generateModalId() {
302
+ return `modal-${++this.state.modalCounter}-${Date.now()}`;
303
+ }
304
+
305
+ escapeHtml(text) {
306
+ if (typeof text !== 'string') return '';
307
+ const div = document.createElement('div');
308
+ div.textContent = text;
309
+ return div.innerHTML;
310
+ }
311
+
312
+ // Promise-based modal methods
313
+ showWarningModalAsync(title, message, options = {}) {
314
+ return new Promise((resolve) => {
315
+ this.showWarningModal(title, message, {
316
+ ...options,
317
+ onConfirm: () => {
318
+ if (options.onConfirm) options.onConfirm();
319
+ resolve(true);
320
+ },
321
+ onCancel: () => {
322
+ if (options.onCancel) options.onCancel();
323
+ resolve(false);
324
+ }
325
+ });
326
+ });
327
+ }
328
+
329
+ showConfirmModalAsync(title, message, options = {}) {
330
+ return new Promise((resolve) => {
331
+ this.showConfirmModal(title, message, {
332
+ ...options,
333
+ onConfirm: async () => {
334
+ try {
335
+ let result = true;
336
+ if (options.onConfirm) {
337
+ result = await options.onConfirm();
338
+ // If onConfirm returns false, don't resolve with true
339
+ if (result === false) {
340
+ resolve(false);
341
+ return;
342
+ }
343
+ }
344
+ resolve(true);
345
+ } catch (error) {
346
+ console.error('[ModalManager] onConfirm error:', error);
347
+ resolve(false);
348
+ }
349
+ },
350
+ onCancel: async () => {
351
+ try {
352
+ if (options.onCancel) {
353
+ await options.onCancel();
354
+ }
355
+ resolve(false);
356
+ } catch (error) {
357
+ console.error('[ModalManager] onCancel error:', error);
358
+ resolve(false);
359
+ }
360
+ }
361
+ });
362
+ });
363
+ }
364
+
365
+ // Modal state queries
366
+ hasActiveModals() {
367
+ return this.state.activeModals.size > 0;
368
+ }
369
+
370
+ getActiveModalCount() {
371
+ return this.state.activeModals.size;
372
+ }
373
+
374
+ isModalActive(modalId) {
375
+ return this.state.activeModals.has(modalId);
376
+ }
377
+
378
+ // Quick notification modal (auto-close)
379
+ showNotificationModal(message, type = 'info', duration = 3000) {
380
+ const typeIcons = {
381
+ success: '✅',
382
+ error: '❌',
383
+ warning: '⚠️',
384
+ info: 'ℹ️'
385
+ };
386
+
387
+ const icon = typeIcons[type] || typeIcons.info;
388
+ const typeClass = `notification-${type}`;
389
+
390
+ const modalData = this.createModal(`
391
+ <div class="notification-content">
392
+ <div class="notification-icon">${icon}</div>
393
+ <div class="notification-message">${this.escapeHtml(message)}</div>
394
+ </div>
395
+ `, {
396
+ className: `notification-modal ${typeClass}`,
397
+ showCloseButton: false,
398
+ backdrop: false
399
+ });
400
+
401
+ // Auto-close after duration
402
+ if (duration > 0) {
403
+ setTimeout(() => {
404
+ modalData.close();
405
+ }, duration);
406
+ }
407
+
408
+ return modalData.modalId;
409
+ }
410
+
411
+ // Quick input modal
412
+ showInputModal(title, placeholder = '', defaultValue = '', options = {}) {
413
+ const {
414
+ inputType = 'text',
415
+ confirmText = 'OK',
416
+ cancelText = 'Cancel',
417
+ onConfirm = null,
418
+ onCancel = null,
419
+ validator = null
420
+ } = options;
421
+
422
+ return new Promise((resolve) => {
423
+ const inputId = `input-${Date.now()}`;
424
+
425
+ const modalData = this.createModal(`
426
+ <div class="input-modal-content">
427
+ <label for="${inputId}" class="input-label">${title}</label>
428
+ <input type="${inputType}" id="${inputId}" class="modal-input"
429
+ placeholder="${this.escapeHtml(placeholder)}"
430
+ value="${this.escapeHtml(defaultValue)}">
431
+ <div class="modal-footer">
432
+ <button class="btn-secondary input-cancel-btn">${this.escapeHtml(cancelText)}</button>
433
+ <button class="btn-primary input-confirm-btn">${this.escapeHtml(confirmText)}</button>
434
+ </div>
435
+ </div>
436
+ `, {
437
+ className: 'input-modal',
438
+ showCloseButton: true
439
+ });
440
+
441
+ const input = document.getElementById(inputId);
442
+ const confirmBtn = modalData.modal.querySelector('.input-confirm-btn');
443
+ const cancelBtn = modalData.modal.querySelector('.input-cancel-btn');
444
+
445
+ // Focus input
446
+ setTimeout(() => input.focus(), 100);
447
+
448
+ const handleConfirm = () => {
449
+ const value = input.value.trim();
450
+
451
+ if (validator && !validator(value)) {
452
+ return; // Don't close modal if validation fails
453
+ }
454
+
455
+ modalData.close();
456
+ if (onConfirm) onConfirm(value);
457
+ resolve(value);
458
+ };
459
+
460
+ const handleCancel = () => {
461
+ modalData.close();
462
+ if (onCancel) onCancel();
463
+ resolve(null);
464
+ };
465
+
466
+ confirmBtn.addEventListener('click', handleConfirm);
467
+ cancelBtn.addEventListener('click', handleCancel);
468
+
469
+ // Enter key to confirm
470
+ input.addEventListener('keydown', (e) => {
471
+ if (e.key === 'Enter') {
472
+ handleConfirm();
473
+ }
474
+ });
475
+ });
476
+ }
477
+
478
+ // Get current state
479
+ getState() {
480
+ return { ...this.state };
481
+ }
482
+ }
483
+
484
+ // Export for use in other modules
485
+ if (typeof window !== 'undefined') {
486
+ window.VibeSurfModalManager = VibeSurfModalManager;
487
+ }
@@ -7,7 +7,7 @@ class VibeSurfSessionManager {
7
7
  this.currentSession = null;
8
8
  this.activityLogs = [];
9
9
  this.pollingInterval = null;
10
- this.pollingFrequency = 1000; // 1 second
10
+ this.pollingFrequency = 300; // 300ms for faster response
11
11
  this.isPolling = false;
12
12
  this.eventListeners = new Map();
13
13
 
@@ -201,8 +201,18 @@ class VibeSurfSessionManager {
201
201
  }
202
202
 
203
203
  try {
204
- console.log('[SessionManager] 🔄 Syncing activity logs before task submission...');
205
- await this.syncActivityLogsFromServer();
204
+ // Stop any existing polling before starting new task
205
+ this.stopActivityPolling();
206
+
207
+ // Reset activity logs for new task to ensure proper index synchronization
208
+ this.activityLogs = [];
209
+
210
+ // Sync with server logs to get the correct starting state
211
+ try {
212
+ await this.syncActivityLogsFromServer();
213
+ } catch (error) {
214
+ this.activityLogs = [];
215
+ }
206
216
 
207
217
  const taskPayload = {
208
218
  session_id: this.currentSession.id,
@@ -220,12 +230,12 @@ class VibeSurfSessionManager {
220
230
  submittedAt: new Date().toISOString()
221
231
  };
222
232
 
223
- // Start activity polling
224
- this.startActivityPolling();
225
-
226
233
  // Store updated session
227
234
  await this.storeSessionData();
228
235
 
236
+ // Start polling after task submission and sync
237
+ this.startActivityPolling();
238
+
229
239
  this.emit('taskSubmitted', {
230
240
  sessionId: this.currentSession.id,
231
241
  task: this.currentSession.currentTask,
@@ -273,6 +283,13 @@ class VibeSurfSessionManager {
273
283
  await this.storeSessionData();
274
284
  }
275
285
 
286
+ // Sync activity logs before resuming polling to ensure index consistency
287
+ try {
288
+ await this.syncActivityLogsFromServer();
289
+ } catch (error) {
290
+ // Continue with existing logs if sync fails
291
+ }
292
+
276
293
  // Restart polling when task is resumed
277
294
  this.startActivityPolling();
278
295
 
@@ -298,6 +315,13 @@ class VibeSurfSessionManager {
298
315
 
299
316
  // Stop polling when task is stopped
300
317
  this.stopActivityPolling();
318
+
319
+ // Sync final activity logs to capture any termination messages
320
+ try {
321
+ await this.syncActivityLogsFromServer();
322
+ } catch (error) {
323
+ // Continue if sync fails
324
+ }
301
325
 
302
326
  this.emit('taskStopped', { sessionId: this.currentSession?.id, response });
303
327
 
@@ -309,6 +333,24 @@ class VibeSurfSessionManager {
309
333
  }
310
334
  }
311
335
 
336
+ async addNewTaskToPaused(newTaskDescription) {
337
+ try {
338
+ const response = await this.apiClient.addNewTask(newTaskDescription);
339
+
340
+ this.emit('newTaskAdded', {
341
+ sessionId: this.currentSession?.id,
342
+ newTask: newTaskDescription,
343
+ response
344
+ });
345
+
346
+ return response;
347
+ } catch (error) {
348
+ console.error('[SessionManager] Add new task failed:', error);
349
+ this.emit('taskError', { error: error.message, action: 'add_new_task' });
350
+ throw error;
351
+ }
352
+ }
353
+
312
354
  // Activity polling
313
355
  startActivityPolling() {
314
356
  if (this.isPolling) {
@@ -358,15 +400,14 @@ class VibeSurfSessionManager {
358
400
 
359
401
  if (response && activityLog) {
360
402
  const prevActivityLog = this.activityLogs.length > 0 ? this.activityLogs[this.activityLogs.length - 1] : null;
361
-
362
- // 检查是否为新的、不重复的activity log
403
+
363
404
  const isNewLog = !prevActivityLog || !this.areLogsEqual(prevActivityLog, activityLog);
364
405
 
365
406
  if (isNewLog) {
366
407
  // New activity log received
367
408
  const newLog = { ...activityLog };
368
409
 
369
- // Add timestamp if not present
410
+ // Add timestamp if not present - this should now be handled by UI
370
411
  if (!newLog.timestamp) {
371
412
  newLog.timestamp = new Date().toISOString();
372
413
  }
@@ -456,7 +497,7 @@ class VibeSurfSessionManager {
456
497
  if (missingLogs.length > 0) {
457
498
 
458
499
  for (const log of missingLogs) {
459
- // Add timestamp if not present
500
+ // Add timestamp if not present - this should now be handled by UI
460
501
  if (!log.timestamp) {
461
502
  log.timestamp = new Date().toISOString();
462
503
  }
@@ -492,7 +533,7 @@ class VibeSurfSessionManager {
492
533
  // 完全同步:用服务器端的logs替换本地logs
493
534
  const previousCount = this.activityLogs.length;
494
535
 
495
- // 添加timestamp给没有的logs
536
+ // Add timestamp to logs that don't have them
496
537
  const processedLogs = serverLogs.map(log => ({
497
538
  ...log,
498
539
  timestamp: log.timestamp || new Date().toISOString()