agentgui 1.0.93 → 1.0.95
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/.prd +1 -0
- package/database.js +43 -1
- package/lib/claude-runner.js +24 -22
- package/package.json +5 -3
- package/server.js +209 -146
- package/static/index.html +354 -94
- package/static/js/client.js +218 -144
- package/static/js/features.js +243 -0
- package/static/js/websocket-manager.js +1 -1
- package/static/styles.css +46 -4
package/static/js/client.js
CHANGED
|
@@ -193,66 +193,42 @@ class AgentGUIClient {
|
|
|
193
193
|
}
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
-
/**
|
|
197
|
-
* Handle incoming WebSocket message
|
|
198
|
-
*/
|
|
199
196
|
handleWebSocketMessage(data) {
|
|
200
197
|
try {
|
|
201
|
-
// Route by message type
|
|
202
198
|
switch (data.type) {
|
|
203
199
|
case 'streaming_start':
|
|
204
200
|
this.handleStreamingStart(data);
|
|
205
201
|
break;
|
|
206
|
-
|
|
207
202
|
case 'streaming_progress':
|
|
208
|
-
this.
|
|
203
|
+
this.handleStreamingProgress(data);
|
|
209
204
|
break;
|
|
210
|
-
|
|
211
205
|
case 'streaming_complete':
|
|
212
206
|
this.handleStreamingComplete(data);
|
|
213
207
|
break;
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
case 'file_write':
|
|
217
|
-
case 'command_execute':
|
|
218
|
-
case 'git_status':
|
|
219
|
-
case 'error':
|
|
220
|
-
case 'text_block':
|
|
221
|
-
case 'code_block':
|
|
222
|
-
case 'thinking_block':
|
|
223
|
-
case 'tool_use':
|
|
224
|
-
this.queueEvent(data);
|
|
208
|
+
case 'streaming_error':
|
|
209
|
+
this.handleStreamingError(data);
|
|
225
210
|
break;
|
|
226
|
-
|
|
227
211
|
case 'conversation_created':
|
|
228
212
|
this.handleConversationCreated(data);
|
|
229
213
|
break;
|
|
230
|
-
|
|
231
214
|
case 'message_created':
|
|
232
215
|
this.handleMessageCreated(data);
|
|
233
216
|
break;
|
|
234
|
-
|
|
217
|
+
case 'queue_status':
|
|
218
|
+
this.handleQueueStatus(data);
|
|
219
|
+
break;
|
|
235
220
|
default:
|
|
236
|
-
|
|
221
|
+
break;
|
|
237
222
|
}
|
|
238
223
|
} catch (error) {
|
|
239
224
|
console.error('Message handling error:', error);
|
|
240
225
|
}
|
|
241
226
|
}
|
|
242
227
|
|
|
243
|
-
/**
|
|
244
|
-
* Queue event for rendering
|
|
245
|
-
*/
|
|
246
228
|
queueEvent(data) {
|
|
247
229
|
try {
|
|
248
|
-
// Process event
|
|
249
230
|
const processed = this.eventProcessor.processEvent(data);
|
|
250
231
|
if (!processed) return;
|
|
251
|
-
|
|
252
|
-
// Queue for rendering
|
|
253
|
-
this.renderer.queueEvent(processed);
|
|
254
|
-
|
|
255
|
-
// Track session events
|
|
256
232
|
if (data.sessionId && this.state.currentSession?.id === data.sessionId) {
|
|
257
233
|
this.state.sessionEvents.push(processed);
|
|
258
234
|
}
|
|
@@ -261,9 +237,6 @@ class AgentGUIClient {
|
|
|
261
237
|
}
|
|
262
238
|
}
|
|
263
239
|
|
|
264
|
-
/**
|
|
265
|
-
* Handle streaming start
|
|
266
|
-
*/
|
|
267
240
|
handleStreamingStart(data) {
|
|
268
241
|
console.log('Streaming started:', data);
|
|
269
242
|
this.state.isStreaming = true;
|
|
@@ -273,60 +246,146 @@ class AgentGUIClient {
|
|
|
273
246
|
agentId: data.agentId,
|
|
274
247
|
startTime: Date.now()
|
|
275
248
|
};
|
|
276
|
-
this.state.currentConversation = { id: data.conversationId };
|
|
277
249
|
this.state.sessionEvents = [];
|
|
250
|
+
this.state.streamingBlocks = [];
|
|
278
251
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
window.conversationManager.select(data.conversationId);
|
|
252
|
+
if (this.wsManager.isConnected) {
|
|
253
|
+
this.wsManager.subscribeToSession(data.sessionId);
|
|
282
254
|
}
|
|
283
255
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
256
|
+
const outputEl = document.getElementById('output');
|
|
257
|
+
if (outputEl) {
|
|
258
|
+
let messagesEl = outputEl.querySelector('.conversation-messages');
|
|
259
|
+
if (!messagesEl) {
|
|
260
|
+
outputEl.innerHTML = '<div class="conversation-messages"></div>';
|
|
261
|
+
messagesEl = outputEl.querySelector('.conversation-messages');
|
|
290
262
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
263
|
+
const streamingDiv = document.createElement('div');
|
|
264
|
+
streamingDiv.className = 'message message-assistant streaming-message';
|
|
265
|
+
streamingDiv.id = `streaming-${data.sessionId}`;
|
|
266
|
+
streamingDiv.innerHTML = `
|
|
267
|
+
<div class="message-role">Assistant</div>
|
|
268
|
+
<div class="message-blocks streaming-blocks"></div>
|
|
269
|
+
<div class="streaming-indicator" style="display:flex;align-items:center;gap:0.5rem;padding:0.5rem 0;color:var(--color-text-secondary);font-size:0.875rem;">
|
|
270
|
+
<span class="animate-spin" style="display:inline-block;width:1rem;height:1rem;border:2px solid var(--color-border);border-top-color:var(--color-primary);border-radius:50%;"></span>
|
|
271
|
+
Thinking...
|
|
272
|
+
</div>
|
|
273
|
+
`;
|
|
274
|
+
messagesEl.appendChild(streamingDiv);
|
|
275
|
+
this.scrollToBottom();
|
|
276
|
+
}
|
|
303
277
|
|
|
304
278
|
this.disableControls();
|
|
305
279
|
this.emit('streaming:start', data);
|
|
306
280
|
}
|
|
307
281
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
282
|
+
handleStreamingProgress(data) {
|
|
283
|
+
if (!data.block) return;
|
|
284
|
+
|
|
285
|
+
const block = data.block;
|
|
286
|
+
if (!this.state.streamingBlocks) this.state.streamingBlocks = [];
|
|
287
|
+
this.state.streamingBlocks.push(block);
|
|
288
|
+
|
|
289
|
+
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
290
|
+
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
291
|
+
if (!streamingEl) return;
|
|
292
|
+
|
|
293
|
+
const blocksEl = streamingEl.querySelector('.streaming-blocks');
|
|
294
|
+
if (!blocksEl) return;
|
|
295
|
+
|
|
296
|
+
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
297
|
+
|
|
298
|
+
if (block.type === 'text' && block.text) {
|
|
299
|
+
const existingTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
300
|
+
if (existingTextEl && !data.isResult) {
|
|
301
|
+
existingTextEl.innerHTML = this.renderBlockContent(block);
|
|
302
|
+
} else {
|
|
303
|
+
const div = document.createElement('div');
|
|
304
|
+
div.className = 'message-text streaming-text-current';
|
|
305
|
+
div.innerHTML = this.renderBlockContent(block);
|
|
306
|
+
blocksEl.appendChild(div);
|
|
307
|
+
}
|
|
308
|
+
} else if (block.type === 'tool_use') {
|
|
309
|
+
const prevTextEl = blocksEl.querySelector('.streaming-text-current');
|
|
310
|
+
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
311
|
+
|
|
312
|
+
const div = document.createElement('div');
|
|
313
|
+
div.className = 'message-tool';
|
|
314
|
+
div.textContent = `[Tool: ${block.name || 'unknown'}]`;
|
|
315
|
+
blocksEl.appendChild(div);
|
|
316
|
+
} else if (block.type === 'tool_result') {
|
|
317
|
+
const div = document.createElement('div');
|
|
318
|
+
div.className = 'message-text';
|
|
319
|
+
div.innerHTML = `<em style="color:var(--color-text-secondary)">${this.escapeHtml(String(block.result || '').substring(0, 500))}</em>`;
|
|
320
|
+
blocksEl.appendChild(div);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (indicator) indicator.querySelector('span:last-child')?.remove();
|
|
324
|
+
if (indicator) {
|
|
325
|
+
const label = document.createElement('span');
|
|
326
|
+
label.textContent = block.type === 'tool_use' ? `Using ${block.name}...` : 'Responding...';
|
|
327
|
+
indicator.appendChild(label);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
this.scrollToBottom();
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
renderBlockContent(block) {
|
|
334
|
+
if (block.type === 'text' && block.text) {
|
|
335
|
+
const text = block.text;
|
|
336
|
+
if (text.includes('<') && (text.includes('</') || text.includes('/>'))) {
|
|
337
|
+
return text;
|
|
338
|
+
}
|
|
339
|
+
return this.escapeHtml(text);
|
|
340
|
+
}
|
|
341
|
+
return this.escapeHtml(JSON.stringify(block));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
scrollToBottom() {
|
|
345
|
+
const scrollContainer = document.getElementById('output-scroll');
|
|
346
|
+
if (scrollContainer) {
|
|
347
|
+
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
handleStreamingError(data) {
|
|
352
|
+
console.error('Streaming error:', data);
|
|
353
|
+
this.state.isStreaming = false;
|
|
354
|
+
|
|
355
|
+
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
356
|
+
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
357
|
+
if (streamingEl) {
|
|
358
|
+
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
359
|
+
if (indicator) {
|
|
360
|
+
indicator.innerHTML = `<span style="color:var(--color-error);">Error: ${this.escapeHtml(data.error || 'Unknown error')}</span>`;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
this.enableControls();
|
|
365
|
+
this.emit('streaming:error', data);
|
|
366
|
+
}
|
|
367
|
+
|
|
311
368
|
handleStreamingComplete(data) {
|
|
312
369
|
console.log('Streaming completed:', data);
|
|
313
370
|
this.state.isStreaming = false;
|
|
314
371
|
|
|
315
|
-
const
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
372
|
+
const sessionId = data.sessionId || this.state.currentSession?.id;
|
|
373
|
+
const streamingEl = document.getElementById(`streaming-${sessionId}`);
|
|
374
|
+
if (streamingEl) {
|
|
375
|
+
const indicator = streamingEl.querySelector('.streaming-indicator');
|
|
376
|
+
if (indicator) indicator.remove();
|
|
377
|
+
streamingEl.classList.remove('streaming-message');
|
|
378
|
+
const prevTextEl = streamingEl.querySelector('.streaming-text-current');
|
|
379
|
+
if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
|
|
380
|
+
|
|
381
|
+
const ts = document.createElement('div');
|
|
382
|
+
ts.className = 'message-timestamp';
|
|
383
|
+
ts.textContent = new Date().toLocaleString();
|
|
384
|
+
streamingEl.appendChild(ts);
|
|
385
|
+
}
|
|
323
386
|
|
|
324
387
|
this.enableControls();
|
|
325
|
-
this.emit('streaming:complete',
|
|
326
|
-
...data,
|
|
327
|
-
duration,
|
|
328
|
-
eventCount: this.state.sessionEvents.length
|
|
329
|
-
});
|
|
388
|
+
this.emit('streaming:complete', data);
|
|
330
389
|
}
|
|
331
390
|
|
|
332
391
|
/**
|
|
@@ -339,32 +398,55 @@ class AgentGUIClient {
|
|
|
339
398
|
}
|
|
340
399
|
}
|
|
341
400
|
|
|
342
|
-
/**
|
|
343
|
-
* Handle message created
|
|
344
|
-
*/
|
|
345
401
|
handleMessageCreated(data) {
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (outputEl) {
|
|
350
|
-
const messageHtml = `
|
|
351
|
-
<div class="message message-${data.message.role}">
|
|
352
|
-
<div class="message-role">${data.message.role.charAt(0).toUpperCase() + data.message.role.slice(1)}</div>
|
|
353
|
-
${this.renderMessageContent(data.message.content)}
|
|
354
|
-
<div class="message-timestamp">${new Date(data.message.created_at).toLocaleString()}</div>
|
|
355
|
-
</div>
|
|
356
|
-
`;
|
|
357
|
-
outputEl.insertAdjacentHTML('beforeend', messageHtml);
|
|
358
|
-
// Scroll to bottom
|
|
359
|
-
const scrollContainer = document.getElementById('output-scroll');
|
|
360
|
-
if (scrollContainer) {
|
|
361
|
-
scrollContainer.scrollTop = scrollContainer.scrollHeight;
|
|
362
|
-
}
|
|
363
|
-
}
|
|
402
|
+
if (data.conversationId !== this.state.currentConversation?.id || !data.message) {
|
|
403
|
+
this.emit('message:created', data);
|
|
404
|
+
return;
|
|
364
405
|
}
|
|
406
|
+
|
|
407
|
+
if (data.message.role === 'assistant' && this.state.isStreaming) {
|
|
408
|
+
this.emit('message:created', data);
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const outputEl = document.querySelector('.conversation-messages');
|
|
413
|
+
if (!outputEl) {
|
|
414
|
+
this.emit('message:created', data);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const messageHtml = `
|
|
419
|
+
<div class="message message-${data.message.role}" data-msg-id="${data.message.id}">
|
|
420
|
+
<div class="message-role">${data.message.role.charAt(0).toUpperCase() + data.message.role.slice(1)}</div>
|
|
421
|
+
${this.renderMessageContent(data.message.content)}
|
|
422
|
+
<div class="message-timestamp">${new Date(data.message.created_at).toLocaleString()}</div>
|
|
423
|
+
</div>
|
|
424
|
+
`;
|
|
425
|
+
outputEl.insertAdjacentHTML('beforeend', messageHtml);
|
|
426
|
+
this.scrollToBottom();
|
|
365
427
|
this.emit('message:created', data);
|
|
366
428
|
}
|
|
367
429
|
|
|
430
|
+
handleQueueStatus(data) {
|
|
431
|
+
if (data.conversationId !== this.state.currentConversation?.id) return;
|
|
432
|
+
|
|
433
|
+
const outputEl = document.querySelector('.conversation-messages');
|
|
434
|
+
if (!outputEl) return;
|
|
435
|
+
|
|
436
|
+
let queueEl = outputEl.querySelector('.queue-indicator');
|
|
437
|
+
if (data.queueLength > 0) {
|
|
438
|
+
if (!queueEl) {
|
|
439
|
+
queueEl = document.createElement('div');
|
|
440
|
+
queueEl.className = 'queue-indicator';
|
|
441
|
+
queueEl.style.cssText = 'padding:0.5rem 1rem;margin:0.5rem 0;border-radius:0.375rem;background:var(--color-warning);color:#000;font-size:0.875rem;text-align:center;';
|
|
442
|
+
outputEl.appendChild(queueEl);
|
|
443
|
+
}
|
|
444
|
+
queueEl.textContent = `${data.queueLength} message${data.queueLength > 1 ? 's' : ''} queued`;
|
|
445
|
+
} else if (queueEl) {
|
|
446
|
+
queueEl.remove();
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
368
450
|
/**
|
|
369
451
|
* Parse markdown code blocks from text
|
|
370
452
|
* Returns array of parts with type ('text' or 'code') and content/language/code
|
|
@@ -476,15 +558,7 @@ class AgentGUIClient {
|
|
|
476
558
|
}
|
|
477
559
|
}
|
|
478
560
|
|
|
479
|
-
/**
|
|
480
|
-
* Start execution
|
|
481
|
-
*/
|
|
482
561
|
async startExecution() {
|
|
483
|
-
if (this.state.isStreaming) {
|
|
484
|
-
this.showError('Streaming already in progress');
|
|
485
|
-
return;
|
|
486
|
-
}
|
|
487
|
-
|
|
488
562
|
const prompt = this.ui.messageInput?.value || '';
|
|
489
563
|
const agentId = this.ui.agentSelector?.value || 'claude-code';
|
|
490
564
|
|
|
@@ -493,23 +567,28 @@ class AgentGUIClient {
|
|
|
493
567
|
return;
|
|
494
568
|
}
|
|
495
569
|
|
|
496
|
-
|
|
497
|
-
this.disableControls();
|
|
570
|
+
if (this.ui.messageInput) this.ui.messageInput.value = '';
|
|
498
571
|
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
572
|
+
try {
|
|
573
|
+
if (this.state.currentConversation?.id) {
|
|
574
|
+
await this.streamToConversation(this.state.currentConversation.id, prompt, agentId);
|
|
575
|
+
} else {
|
|
576
|
+
this.disableControls();
|
|
577
|
+
const response = await fetch(window.__BASE_URL + '/api/conversations', {
|
|
578
|
+
method: 'POST',
|
|
579
|
+
headers: { 'Content-Type': 'application/json' },
|
|
580
|
+
body: JSON.stringify({ agentId, title: prompt.substring(0, 50) })
|
|
581
|
+
});
|
|
582
|
+
const { conversation } = await response.json();
|
|
583
|
+
this.state.currentConversation = conversation;
|
|
507
584
|
|
|
508
|
-
|
|
509
|
-
|
|
585
|
+
if (window.conversationManager) {
|
|
586
|
+
window.conversationManager.loadConversations();
|
|
587
|
+
window.conversationManager.select(conversation.id);
|
|
588
|
+
}
|
|
510
589
|
|
|
511
|
-
|
|
512
|
-
|
|
590
|
+
await this.streamToConversation(conversation.id, prompt, agentId);
|
|
591
|
+
}
|
|
513
592
|
} catch (error) {
|
|
514
593
|
console.error('Execution error:', error);
|
|
515
594
|
this.showError('Failed to start execution: ' + error.message);
|
|
@@ -517,33 +596,32 @@ class AgentGUIClient {
|
|
|
517
596
|
}
|
|
518
597
|
}
|
|
519
598
|
|
|
520
|
-
/**
|
|
521
|
-
* Stream execution to conversation
|
|
522
|
-
*/
|
|
523
599
|
async streamToConversation(conversationId, prompt, agentId) {
|
|
524
600
|
try {
|
|
601
|
+
if (this.wsManager.isConnected) {
|
|
602
|
+
this.wsManager.sendMessage({ type: 'subscribe', conversationId });
|
|
603
|
+
}
|
|
604
|
+
|
|
525
605
|
const response = await fetch(`${window.__BASE_URL}/api/conversations/${conversationId}/stream`, {
|
|
526
606
|
method: 'POST',
|
|
527
607
|
headers: { 'Content-Type': 'application/json' },
|
|
528
|
-
body: JSON.stringify({
|
|
529
|
-
content: prompt,
|
|
530
|
-
agentId,
|
|
531
|
-
skipPermissions: false
|
|
532
|
-
})
|
|
608
|
+
body: JSON.stringify({ content: prompt, agentId, skipPermissions: false })
|
|
533
609
|
});
|
|
534
610
|
|
|
535
|
-
if (!response.ok) {
|
|
536
|
-
throw new Error(`HTTP ${response.status}`);
|
|
537
|
-
}
|
|
611
|
+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
|
|
538
612
|
|
|
539
|
-
const
|
|
613
|
+
const result = await response.json();
|
|
540
614
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
615
|
+
if (result.queued) {
|
|
616
|
+
console.log('Message queued, position:', result.queuePosition);
|
|
617
|
+
return;
|
|
544
618
|
}
|
|
545
619
|
|
|
546
|
-
|
|
620
|
+
if (result.session && this.wsManager.isConnected) {
|
|
621
|
+
this.wsManager.subscribeToSession(result.session.id);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
this.emit('execution:started', result);
|
|
547
625
|
} catch (error) {
|
|
548
626
|
console.error('Stream execution error:', error);
|
|
549
627
|
this.showError('Failed to stream execution: ' + error.message);
|
|
@@ -675,37 +753,33 @@ class AgentGUIClient {
|
|
|
675
753
|
}
|
|
676
754
|
}
|
|
677
755
|
|
|
678
|
-
/**
|
|
679
|
-
* Load and display conversation messages
|
|
680
|
-
*/
|
|
681
756
|
async loadConversationMessages(conversationId) {
|
|
682
757
|
try {
|
|
683
|
-
this.state.currentConversation = { id: conversationId };
|
|
684
|
-
|
|
685
|
-
// Fetch conversation details
|
|
686
758
|
const convResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}`);
|
|
687
759
|
const { conversation } = await convResponse.json();
|
|
760
|
+
this.state.currentConversation = conversation;
|
|
688
761
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
if (!messagesResponse.ok) {
|
|
692
|
-
throw new Error(`Failed to fetch messages: ${messagesResponse.status}`);
|
|
762
|
+
if (this.wsManager.isConnected) {
|
|
763
|
+
this.wsManager.sendMessage({ type: 'subscribe', conversationId });
|
|
693
764
|
}
|
|
765
|
+
|
|
766
|
+
const messagesResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}/messages`);
|
|
767
|
+
if (!messagesResponse.ok) throw new Error(`Failed to fetch messages: ${messagesResponse.status}`);
|
|
694
768
|
const messagesData = await messagesResponse.json();
|
|
695
769
|
|
|
696
|
-
// Clear output and display conversation header
|
|
697
770
|
const outputEl = document.getElementById('output');
|
|
698
771
|
if (outputEl) {
|
|
699
|
-
const wdInfo = conversation.workingDirectory ? `
|
|
772
|
+
const wdInfo = conversation.workingDirectory ? ` - ${this.escapeHtml(conversation.workingDirectory)}` : '';
|
|
700
773
|
outputEl.innerHTML = `
|
|
701
774
|
<div class="conversation-header">
|
|
702
775
|
<h2>${this.escapeHtml(conversation.title || 'Conversation')}</h2>
|
|
703
|
-
<p class="text-secondary">${conversation.agentType || 'unknown'}
|
|
776
|
+
<p class="text-secondary">${conversation.agentType || 'unknown'} - ${new Date(conversation.created_at).toLocaleDateString()}${wdInfo}</p>
|
|
704
777
|
</div>
|
|
705
778
|
<div class="conversation-messages">
|
|
706
779
|
${this.renderMessages(messagesData.messages || [])}
|
|
707
780
|
</div>
|
|
708
781
|
`;
|
|
782
|
+
this.scrollToBottom();
|
|
709
783
|
}
|
|
710
784
|
} catch (error) {
|
|
711
785
|
console.error('Failed to load conversation messages:', error);
|