agentgui 1.0.85 → 1.0.86

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.85",
3
+ "version": "1.0.86",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -65,13 +65,16 @@ const server = http.createServer(async (req, res) => {
65
65
  const routePath = req.url.slice(BASE_URL.length) || '/';
66
66
 
67
67
  try {
68
- if (routePath === '/api/conversations' && req.method === 'GET') {
68
+ // Remove query parameters from routePath for matching
69
+ const pathOnly = routePath.split('?')[0];
70
+
71
+ if (pathOnly === '/api/conversations' && req.method === 'GET') {
69
72
  res.writeHead(200, { 'Content-Type': 'application/json' });
70
73
  res.end(JSON.stringify({ conversations: queries.getConversationsList() }));
71
74
  return;
72
75
  }
73
76
 
74
- if (routePath === '/api/conversations' && req.method === 'POST') {
77
+ if (pathOnly === '/api/conversations' && req.method === 'POST') {
75
78
  const body = await parseBody(req);
76
79
  const conversation = queries.createConversation(body.agentId, body.title);
77
80
  queries.createEvent('conversation.created', { agentId: body.agentId }, conversation.id);
@@ -81,7 +84,7 @@ const server = http.createServer(async (req, res) => {
81
84
  return;
82
85
  }
83
86
 
84
- const convMatch = routePath.match(/^\/api\/conversations\/([^/]+)$/);
87
+ const convMatch = pathOnly.match(/^\/api\/conversations\/([^/]+)$/);
85
88
  if (convMatch) {
86
89
  if (req.method === 'GET') {
87
90
  const conv = queries.getConversation(convMatch[1]);
@@ -111,7 +114,7 @@ const server = http.createServer(async (req, res) => {
111
114
  }
112
115
  }
113
116
 
114
- const messagesMatch = routePath.match(/^\/api\/conversations\/([^/]+)\/messages$/);
117
+ const messagesMatch = pathOnly.match(/^\/api\/conversations\/([^/]+)\/messages$/);
115
118
  if (messagesMatch) {
116
119
  if (req.method === 'GET') {
117
120
  const url = new URL(req.url, 'http://localhost');
@@ -141,7 +144,7 @@ const server = http.createServer(async (req, res) => {
141
144
  }
142
145
  }
143
146
 
144
- const streamMatch = routePath.match(/^\/api\/conversations\/([^/]+)\/stream$/);
147
+ const streamMatch = pathOnly.match(/^\/api\/conversations\/([^/]+)\/stream$/);
145
148
  if (streamMatch && req.method === 'POST') {
146
149
  const conversationId = streamMatch[1];
147
150
  const body = await parseBody(req);
@@ -180,7 +183,7 @@ const server = http.createServer(async (req, res) => {
180
183
  return;
181
184
  }
182
185
 
183
- const messageMatch = routePath.match(/^\/api\/conversations\/([^/]+)\/messages\/([^/]+)$/);
186
+ const messageMatch = pathOnly.match(/^\/api\/conversations\/([^/]+)\/messages\/([^/]+)$/);
184
187
  if (messageMatch && req.method === 'GET') {
185
188
  const msg = queries.getMessage(messageMatch[2]);
186
189
  if (!msg || msg.conversationId !== messageMatch[1]) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); return; }
@@ -189,7 +192,7 @@ const server = http.createServer(async (req, res) => {
189
192
  return;
190
193
  }
191
194
 
192
- const sessionMatch = routePath.match(/^\/api\/sessions\/([^/]+)$/);
195
+ const sessionMatch = pathOnly.match(/^\/api\/sessions\/([^/]+)$/);
193
196
  if (sessionMatch && req.method === 'GET') {
194
197
  const sess = queries.getSession(sessionMatch[1]);
195
198
  if (!sess) { res.writeHead(404, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Not found' })); return; }
@@ -199,8 +202,8 @@ const server = http.createServer(async (req, res) => {
199
202
  return;
200
203
  }
201
204
 
202
- if (routePath.match(/^\/api\/conversations\/([^/]+)\/sessions\/latest$/) && req.method === 'GET') {
203
- const convId = routePath.match(/^\/api\/conversations\/([^/]+)\/sessions\/latest$/)[1];
205
+ if (pathOnly.match(/^\/api\/conversations\/([^/]+)\/sessions\/latest$/) && req.method === 'GET') {
206
+ const convId = pathOnly.match(/^\/api\/conversations\/([^/]+)\/sessions\/latest$/)[1];
204
207
  const latestSession = queries.getLatestSession(convId);
205
208
  if (!latestSession) {
206
209
  res.writeHead(200, { 'Content-Type': 'application/json' });
@@ -213,7 +216,7 @@ const server = http.createServer(async (req, res) => {
213
216
  return;
214
217
  }
215
218
 
216
- const executionMatch = routePath.match(/^\/api\/sessions\/([^/]+)\/execution$/);
219
+ const executionMatch = pathOnly.match(/^\/api\/sessions\/([^/]+)\/execution$/);
217
220
  if (executionMatch && req.method === 'GET') {
218
221
  const sessionId = executionMatch[1];
219
222
  const url = new URL(req.url, 'http://localhost');
package/static/index.html CHANGED
@@ -359,6 +359,106 @@
359
359
  font-size: 0.7rem;
360
360
  }
361
361
 
362
+ /* Conversation display */
363
+ .conversation-header {
364
+ padding: 1rem;
365
+ border-bottom: 1px solid var(--color-border);
366
+ margin-bottom: 1rem;
367
+ }
368
+
369
+ .conversation-header h2 {
370
+ margin: 0 0 0.5rem 0;
371
+ font-size: 1.5rem;
372
+ }
373
+
374
+ .conversation-header p {
375
+ margin: 0;
376
+ font-size: 0.875rem;
377
+ color: var(--color-text-secondary);
378
+ }
379
+
380
+ .conversation-messages {
381
+ padding: 1rem;
382
+ }
383
+
384
+ .message {
385
+ margin-bottom: 1rem;
386
+ padding: 1rem;
387
+ border-radius: 0.375rem;
388
+ background-color: var(--color-bg-secondary);
389
+ border-left: 3px solid var(--color-primary);
390
+ }
391
+
392
+ .message-user {
393
+ border-left-color: var(--color-primary);
394
+ }
395
+
396
+ .message-assistant {
397
+ border-left-color: var(--color-success);
398
+ }
399
+
400
+ .message-role {
401
+ font-weight: 600;
402
+ font-size: 0.75rem;
403
+ text-transform: uppercase;
404
+ color: var(--color-text-secondary);
405
+ margin-bottom: 0.5rem;
406
+ }
407
+
408
+ .message-content {
409
+ font-size: 0.9rem;
410
+ line-height: 1.5;
411
+ white-space: pre-wrap;
412
+ word-break: break-word;
413
+ }
414
+
415
+ .message-timestamp {
416
+ font-size: 0.75rem;
417
+ color: var(--color-text-secondary);
418
+ margin-top: 0.5rem;
419
+ }
420
+
421
+ .message-blocks {
422
+ display: flex;
423
+ flex-direction: column;
424
+ gap: 0.75rem;
425
+ }
426
+
427
+ .message-text {
428
+ line-height: 1.6;
429
+ word-break: break-word;
430
+ white-space: pre-wrap;
431
+ }
432
+
433
+ .message-code {
434
+ background-color: var(--color-bg-code);
435
+ border-radius: 0.375rem;
436
+ padding: 0.75rem;
437
+ overflow-x: auto;
438
+ margin: 0.5rem 0;
439
+ }
440
+
441
+ .message-code pre {
442
+ margin: 0;
443
+ font-family: 'Courier New', monospace;
444
+ font-size: 0.85rem;
445
+ color: #e0e0e0;
446
+ }
447
+
448
+ .message-tool {
449
+ background-color: var(--color-primary);
450
+ color: white;
451
+ padding: 0.25rem 0.5rem;
452
+ border-radius: 0.25rem;
453
+ font-size: 0.75rem;
454
+ display: inline-block;
455
+ margin: 0.25rem 0;
456
+ }
457
+
458
+ .text-secondary {
459
+ color: var(--color-text-secondary);
460
+ }
461
+
362
462
  /* Responsive */
363
463
  @media (max-width: 768px) {
364
464
  .layout-with-sidebar {
@@ -170,6 +170,11 @@ class AgentGUIClient {
170
170
 
171
171
  // Listen for new conversation creation
172
172
  window.addEventListener('create-new-conversation', () => this.createNewConversation());
173
+
174
+ // Listen for conversation selection
175
+ window.addEventListener('conversation-selected', (event) => {
176
+ this.loadConversationMessages(event.detail.conversationId);
177
+ });
173
178
  }
174
179
 
175
180
  /**
@@ -517,6 +522,94 @@ class AgentGUIClient {
517
522
  }
518
523
  }
519
524
 
525
+ /**
526
+ * Load and display conversation messages
527
+ */
528
+ async loadConversationMessages(conversationId) {
529
+ try {
530
+ this.state.currentConversation = { id: conversationId };
531
+
532
+ // Fetch conversation details
533
+ const convResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}`);
534
+ const { conversation } = await convResponse.json();
535
+
536
+ // Fetch messages
537
+ const messagesResponse = await fetch(window.__BASE_URL + `/api/conversations/${conversationId}/messages`);
538
+ if (!messagesResponse.ok) {
539
+ throw new Error(`Failed to fetch messages: ${messagesResponse.status}`);
540
+ }
541
+ const messagesData = await messagesResponse.json();
542
+
543
+ // Clear output and display conversation header
544
+ const outputEl = document.getElementById('output');
545
+ if (outputEl) {
546
+ outputEl.innerHTML = `
547
+ <div class="conversation-header">
548
+ <h2>${this.escapeHtml(conversation.title || 'Conversation')}</h2>
549
+ <p class="text-secondary">${conversation.agentType || 'unknown'} • ${new Date(conversation.created_at).toLocaleDateString()}</p>
550
+ </div>
551
+ <div class="conversation-messages">
552
+ ${this.renderMessages(messagesData.messages || [])}
553
+ </div>
554
+ `;
555
+ }
556
+ } catch (error) {
557
+ console.error('Failed to load conversation messages:', error);
558
+ this.showError('Failed to load conversation: ' + error.message);
559
+ }
560
+ }
561
+
562
+ /**
563
+ * Render messages for display
564
+ */
565
+ renderMessages(messages) {
566
+ if (messages.length === 0) {
567
+ return '<p class="text-secondary">No messages in this conversation yet</p>';
568
+ }
569
+
570
+ return messages.map(msg => {
571
+ let contentHtml = '';
572
+
573
+ // Handle different content types
574
+ if (typeof msg.content === 'string') {
575
+ contentHtml = `<div class="message-text">${this.escapeHtml(msg.content)}</div>`;
576
+ } else if (msg.content && typeof msg.content === 'object' && msg.content.type === 'claude_execution') {
577
+ // Handle Claude execution blocks
578
+ contentHtml = '<div class="message-blocks">';
579
+ if (msg.content.blocks && Array.isArray(msg.content.blocks)) {
580
+ msg.content.blocks.forEach(block => {
581
+ if (block.type === 'text') {
582
+ contentHtml += `<div class="message-text">${this.escapeHtml(block.text)}</div>`;
583
+ } else if (block.type === 'code_block') {
584
+ contentHtml += `<div class="message-code"><pre>${this.escapeHtml(block.code)}</pre></div>`;
585
+ } else if (block.type === 'tool_use') {
586
+ contentHtml += `<div class="message-tool">[Tool: ${this.escapeHtml(block.name)}]</div>`;
587
+ }
588
+ });
589
+ }
590
+ contentHtml += '</div>';
591
+ } else {
592
+ contentHtml = `<div class="message-text">${this.escapeHtml(JSON.stringify(msg.content))}</div>`;
593
+ }
594
+
595
+ return `
596
+ <div class="message message-${msg.role}">
597
+ <div class="message-role">${msg.role.charAt(0).toUpperCase() + msg.role.slice(1)}</div>
598
+ ${contentHtml}
599
+ <div class="message-timestamp">${new Date(msg.created_at).toLocaleString()}</div>
600
+ </div>
601
+ `;
602
+ }).join('');
603
+ }
604
+
605
+ /**
606
+ * Escape HTML to prevent XSS
607
+ */
608
+ escapeHtml(text) {
609
+ const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
610
+ return text.replace(/[&<>"']/g, c => map[c]);
611
+ }
612
+
520
613
  /**
521
614
  * Show error message
522
615
  */
@@ -65,6 +65,7 @@ class ConversationManager {
65
65
  createConversationItem(conv) {
66
66
  const li = document.createElement('li');
67
67
  li.className = 'conversation-item';
68
+ li.dataset.convId = conv.id;
68
69
  if (conv.id === this.activeId) li.classList.add('active');
69
70
 
70
71
  const title = conv.title || `Conversation ${conv.id.slice(0, 8)}`;