agentgui 1.0.97 → 1.0.98

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.97",
3
+ "version": "1.0.98",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -475,10 +475,27 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
475
475
  let eventCount = 0;
476
476
 
477
477
  const onEvent = (parsed) => {
478
- if (parsed.type === 'assistant' && parsed.message?.content) {
478
+ eventCount++;
479
+
480
+ if (parsed.type === 'system') {
481
+ broadcastSync({
482
+ type: 'streaming_progress',
483
+ sessionId,
484
+ conversationId,
485
+ block: {
486
+ type: 'system',
487
+ subtype: parsed.subtype,
488
+ model: parsed.model,
489
+ cwd: parsed.cwd,
490
+ tools: parsed.tools,
491
+ session_id: parsed.session_id
492
+ },
493
+ blockIndex: allBlocks.length,
494
+ timestamp: Date.now()
495
+ });
496
+ } else if (parsed.type === 'assistant' && parsed.message?.content) {
479
497
  for (const block of parsed.message.content) {
480
498
  allBlocks.push(block);
481
- eventCount++;
482
499
  broadcastSync({
483
500
  type: 'streaming_progress',
484
501
  sessionId,
@@ -488,16 +505,45 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
488
505
  timestamp: Date.now()
489
506
  });
490
507
  }
491
- } else if (parsed.type === 'result' && parsed.result && allBlocks.length === 0) {
508
+ } else if (parsed.type === 'user' && parsed.message?.content) {
509
+ for (const block of parsed.message.content) {
510
+ if (block.type === 'tool_result') {
511
+ broadcastSync({
512
+ type: 'streaming_progress',
513
+ sessionId,
514
+ conversationId,
515
+ block: {
516
+ type: 'tool_result',
517
+ tool_use_id: block.tool_use_id,
518
+ content: typeof block.content === 'string' ? block.content : JSON.stringify(block.content),
519
+ is_error: block.is_error || false
520
+ },
521
+ blockIndex: allBlocks.length,
522
+ timestamp: Date.now()
523
+ });
524
+ }
525
+ }
526
+ } else if (parsed.type === 'result') {
492
527
  broadcastSync({
493
528
  type: 'streaming_progress',
494
529
  sessionId,
495
530
  conversationId,
496
- block: { type: 'text', text: parsed.result },
497
- blockIndex: 0,
531
+ block: {
532
+ type: 'result',
533
+ subtype: parsed.subtype,
534
+ duration_ms: parsed.duration_ms,
535
+ total_cost_usd: parsed.total_cost_usd,
536
+ num_turns: parsed.num_turns,
537
+ is_error: parsed.is_error || false,
538
+ result: parsed.result
539
+ },
540
+ blockIndex: allBlocks.length,
498
541
  isResult: true,
499
542
  timestamp: Date.now()
500
543
  });
544
+ if (parsed.result && allBlocks.length === 0) {
545
+ allBlocks.push({ type: 'text', text: String(parsed.result) });
546
+ }
501
547
  }
502
548
  };
503
549
 
package/static/index.html CHANGED
@@ -431,6 +431,170 @@
431
431
  margin: 0.25rem 0;
432
432
  }
433
433
 
434
+ /* --- Streaming block types --- */
435
+ .streaming-block-system {
436
+ padding: 0.5rem 0.75rem;
437
+ margin: 0.25rem 0;
438
+ background: rgba(59,130,246,0.08);
439
+ border: 1px solid rgba(59,130,246,0.2);
440
+ border-radius: 0.375rem;
441
+ font-size: 0.8rem;
442
+ display: flex;
443
+ align-items: center;
444
+ gap: 0.5rem;
445
+ }
446
+
447
+ .system-model-badge {
448
+ background: var(--color-primary);
449
+ color: white;
450
+ padding: 0.125rem 0.5rem;
451
+ border-radius: 1rem;
452
+ font-size: 0.7rem;
453
+ font-weight: 600;
454
+ white-space: nowrap;
455
+ }
456
+
457
+ .system-info {
458
+ color: var(--color-text-secondary);
459
+ font-size: 0.75rem;
460
+ }
461
+
462
+ .streaming-block-tool-use {
463
+ margin: 0.5rem 0;
464
+ border-left: 3px solid #06b6d4;
465
+ background: rgba(6,182,212,0.06);
466
+ border-radius: 0 0.375rem 0.375rem 0;
467
+ overflow: hidden;
468
+ }
469
+
470
+ .tool-use-header {
471
+ padding: 0.5rem 0.75rem;
472
+ font-weight: 600;
473
+ font-size: 0.85rem;
474
+ display: flex;
475
+ align-items: center;
476
+ gap: 0.375rem;
477
+ }
478
+
479
+ .tool-use-icon {
480
+ font-size: 0.9rem;
481
+ opacity: 0.7;
482
+ }
483
+
484
+ .tool-use-name {
485
+ color: #0891b2;
486
+ }
487
+
488
+ html.dark .tool-use-name { color: #22d3ee; }
489
+
490
+ .tool-input-details {
491
+ border-top: 1px solid rgba(6,182,212,0.15);
492
+ }
493
+
494
+ .tool-input-summary {
495
+ padding: 0.375rem 0.75rem;
496
+ font-size: 0.75rem;
497
+ color: var(--color-text-secondary);
498
+ cursor: pointer;
499
+ user-select: none;
500
+ }
501
+
502
+ .tool-input-summary:hover {
503
+ background: rgba(6,182,212,0.08);
504
+ }
505
+
506
+ .tool-input-pre {
507
+ margin: 0;
508
+ padding: 0.5rem 0.75rem;
509
+ font-size: 0.75rem;
510
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
511
+ background: rgba(0,0,0,0.03);
512
+ overflow-x: auto;
513
+ max-height: 200px;
514
+ overflow-y: auto;
515
+ white-space: pre-wrap;
516
+ word-break: break-all;
517
+ }
518
+
519
+ html.dark .tool-input-pre { background: rgba(255,255,255,0.03); }
520
+
521
+ .streaming-block-tool-result {
522
+ margin: 0.25rem 0 0.5rem 0;
523
+ border-radius: 0.375rem;
524
+ background: var(--color-bg-code);
525
+ overflow: hidden;
526
+ }
527
+
528
+ .tool-result-header {
529
+ padding: 0.375rem 0.75rem;
530
+ font-size: 0.7rem;
531
+ border-bottom: 1px solid rgba(255,255,255,0.05);
532
+ }
533
+
534
+ .tool-result-ok-badge {
535
+ color: #10b981;
536
+ font-weight: 600;
537
+ text-transform: uppercase;
538
+ letter-spacing: 0.05em;
539
+ }
540
+
541
+ .tool-result-error-badge {
542
+ color: #ef4444;
543
+ font-weight: 600;
544
+ text-transform: uppercase;
545
+ letter-spacing: 0.05em;
546
+ }
547
+
548
+ .tool-result-pre {
549
+ margin: 0;
550
+ padding: 0.5rem 0.75rem;
551
+ font-size: 0.75rem;
552
+ font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
553
+ color: #d1d5db;
554
+ overflow-x: auto;
555
+ max-height: 300px;
556
+ overflow-y: auto;
557
+ white-space: pre-wrap;
558
+ word-break: break-all;
559
+ }
560
+
561
+ .streaming-block-tool-result.tool-result-error {
562
+ border: 1px solid rgba(239,68,68,0.3);
563
+ }
564
+
565
+ .streaming-block-result {
566
+ padding: 0.5rem 0.75rem;
567
+ margin: 0.5rem 0;
568
+ border-radius: 0.375rem;
569
+ background: rgba(16,185,129,0.08);
570
+ border: 1px solid rgba(16,185,129,0.2);
571
+ font-size: 0.8rem;
572
+ display: flex;
573
+ align-items: center;
574
+ gap: 0.5rem;
575
+ }
576
+
577
+ .streaming-block-result.result-error {
578
+ background: rgba(239,68,68,0.08);
579
+ border-color: rgba(239,68,68,0.2);
580
+ }
581
+
582
+ .result-status {
583
+ font-weight: 600;
584
+ color: #10b981;
585
+ }
586
+
587
+ .result-error .result-status { color: #ef4444; }
588
+
589
+ .result-stats {
590
+ color: var(--color-text-secondary);
591
+ font-size: 0.75rem;
592
+ }
593
+
594
+ .streaming-indicator-label {
595
+ white-space: nowrap;
596
+ }
597
+
434
598
  /* --- Input area: fixed at bottom --- */
435
599
  .input-section {
436
600
  flex-shrink: 0;
@@ -274,7 +274,7 @@ class AgentGUIClient {
274
274
  <div class="message-blocks streaming-blocks"></div>
275
275
  <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;">
276
276
  <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>
277
- Thinking...
277
+ <span class="streaming-indicator-label">Thinking...</span>
278
278
  </div>
279
279
  `;
280
280
  messagesEl.appendChild(streamingDiv);
@@ -300,37 +300,75 @@ class AgentGUIClient {
300
300
  if (!blocksEl) return;
301
301
 
302
302
  const indicator = streamingEl.querySelector('.streaming-indicator');
303
+ let indicatorText = 'Responding...';
303
304
 
304
- if (block.type === 'text' && block.text) {
305
+ if (block.type === 'system') {
306
+ const div = document.createElement('div');
307
+ div.className = 'streaming-block-system';
308
+ const toolCount = block.tools ? block.tools.length : 0;
309
+ div.innerHTML = `<span class="system-model-badge">${this.escapeHtml(block.model || 'unknown')}</span> <span class="system-info">${toolCount} tools available</span>`;
310
+ blocksEl.appendChild(div);
311
+ indicatorText = 'Initializing...';
312
+ } else if (block.type === 'text' && block.text) {
305
313
  const existingTextEl = blocksEl.querySelector('.streaming-text-current');
306
314
  if (existingTextEl && !data.isResult) {
307
315
  existingTextEl.innerHTML = this.renderBlockContent(block);
308
316
  } else {
317
+ const prevTextEl = blocksEl.querySelector('.streaming-text-current');
318
+ if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
309
319
  const div = document.createElement('div');
310
320
  div.className = 'message-text streaming-text-current';
311
321
  div.innerHTML = this.renderBlockContent(block);
312
322
  blocksEl.appendChild(div);
313
323
  }
324
+ indicatorText = 'Responding...';
314
325
  } else if (block.type === 'tool_use') {
315
326
  const prevTextEl = blocksEl.querySelector('.streaming-text-current');
316
327
  if (prevTextEl) prevTextEl.classList.remove('streaming-text-current');
317
328
 
318
329
  const div = document.createElement('div');
319
- div.className = 'message-tool';
320
- div.textContent = `[Tool: ${block.name || 'unknown'}]`;
330
+ div.className = 'streaming-block-tool-use';
331
+ div.dataset.toolUseId = block.id || '';
332
+ let inputHtml = '';
333
+ if (block.input && Object.keys(block.input).length > 0) {
334
+ const inputStr = JSON.stringify(block.input, null, 2);
335
+ inputHtml = `<details class="tool-input-details"><summary class="tool-input-summary">Input</summary><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></details>`;
336
+ }
337
+ div.innerHTML = `<div class="tool-use-header"><span class="tool-use-icon">&#9881;</span> <span class="tool-use-name">${this.escapeHtml(block.name || 'unknown')}</span></div>${inputHtml}`;
321
338
  blocksEl.appendChild(div);
339
+ indicatorText = `Using ${block.name || 'tool'}...`;
322
340
  } else if (block.type === 'tool_result') {
323
341
  const div = document.createElement('div');
324
- div.className = 'message-text';
325
- div.innerHTML = `<em style="color:var(--color-text-secondary)">${this.escapeHtml(String(block.result || '').substring(0, 500))}</em>`;
342
+ div.className = 'streaming-block-tool-result' + (block.is_error ? ' tool-result-error' : '');
343
+ const content = block.content || '';
344
+ const displayContent = content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content;
345
+ div.innerHTML = `<div class="tool-result-header">${block.is_error ? '<span class="tool-result-error-badge">Error</span>' : '<span class="tool-result-ok-badge">Result</span>'}</div><pre class="tool-result-pre">${this.escapeHtml(displayContent)}</pre>`;
346
+ blocksEl.appendChild(div);
347
+ indicatorText = 'Processing result...';
348
+ } else if (block.type === 'result') {
349
+ const div = document.createElement('div');
350
+ div.className = 'streaming-block-result' + (block.is_error ? ' result-error' : '');
351
+ const duration = block.duration_ms ? (block.duration_ms / 1000).toFixed(1) + 's' : '';
352
+ const cost = block.total_cost_usd ? '$' + block.total_cost_usd.toFixed(4) : '';
353
+ const turns = block.num_turns ? block.num_turns + ' turns' : '';
354
+ const parts = [duration, cost, turns].filter(Boolean);
355
+ div.innerHTML = `<span class="result-status">${block.is_error ? 'Failed' : 'Complete'}</span>${parts.length ? ' <span class="result-stats">' + parts.join(' / ') + '</span>' : ''}`;
326
356
  blocksEl.appendChild(div);
357
+ indicatorText = 'Complete';
327
358
  }
328
359
 
329
- if (indicator) indicator.querySelector('span:last-child')?.remove();
330
360
  if (indicator) {
331
- const label = document.createElement('span');
332
- label.textContent = block.type === 'tool_use' ? `Using ${block.name}...` : 'Responding...';
333
- indicator.appendChild(label);
361
+ const labelEl = indicator.querySelector('.streaming-indicator-label');
362
+ if (labelEl) {
363
+ labelEl.textContent = indicatorText;
364
+ } else {
365
+ const existingLabel = indicator.querySelector('span:last-child');
366
+ if (existingLabel && !existingLabel.classList.contains('animate-spin')) existingLabel.remove();
367
+ const label = document.createElement('span');
368
+ label.className = 'streaming-indicator-label';
369
+ label.textContent = indicatorText;
370
+ indicator.appendChild(label);
371
+ }
334
372
  }
335
373
 
336
374
  this.scrollToBottom();
@@ -555,7 +593,16 @@ class AgentGUIClient {
555
593
  html += `<div class="message-code"><pre>${this.escapeHtml(block.code)}</pre></div>`;
556
594
  }
557
595
  } else if (block.type === 'tool_use') {
558
- html += `<div class="message-tool">[Tool: ${this.escapeHtml(block.name)}]</div>`;
596
+ let inputHtml = '';
597
+ if (block.input && Object.keys(block.input).length > 0) {
598
+ const inputStr = JSON.stringify(block.input, null, 2);
599
+ inputHtml = `<details class="tool-input-details"><summary class="tool-input-summary">Input</summary><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></details>`;
600
+ }
601
+ html += `<div class="streaming-block-tool-use"><div class="tool-use-header"><span class="tool-use-icon">&#9881;</span> <span class="tool-use-name">${this.escapeHtml(block.name || 'unknown')}</span></div>${inputHtml}</div>`;
602
+ } else if (block.type === 'tool_result') {
603
+ const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
604
+ const displayContent = content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content;
605
+ html += `<div class="streaming-block-tool-result${block.is_error ? ' tool-result-error' : ''}"><div class="tool-result-header">${block.is_error ? '<span class="tool-result-error-badge">Error</span>' : '<span class="tool-result-ok-badge">Result</span>'}</div><pre class="tool-result-pre">${this.escapeHtml(displayContent)}</pre></div>`;
559
606
  }
560
607
  });
561
608
  }
@@ -843,7 +890,16 @@ class AgentGUIClient {
843
890
  contentHtml += `<div class="message-code"><pre>${this.escapeHtml(block.code)}</pre></div>`;
844
891
  }
845
892
  } else if (block.type === 'tool_use') {
846
- contentHtml += `<div class="message-tool">[Tool: ${this.escapeHtml(block.name)}]</div>`;
893
+ let inputHtml = '';
894
+ if (block.input && Object.keys(block.input).length > 0) {
895
+ const inputStr = JSON.stringify(block.input, null, 2);
896
+ inputHtml = `<details class="tool-input-details"><summary class="tool-input-summary">Input</summary><pre class="tool-input-pre">${this.escapeHtml(inputStr)}</pre></details>`;
897
+ }
898
+ contentHtml += `<div class="streaming-block-tool-use"><div class="tool-use-header"><span class="tool-use-icon">&#9881;</span> <span class="tool-use-name">${this.escapeHtml(block.name || 'unknown')}</span></div>${inputHtml}</div>`;
899
+ } else if (block.type === 'tool_result') {
900
+ const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
901
+ const displayContent = content.length > 2000 ? content.substring(0, 2000) + '\n... (truncated)' : content;
902
+ contentHtml += `<div class="streaming-block-tool-result${block.is_error ? ' tool-result-error' : ''}"><div class="tool-result-header">${block.is_error ? '<span class="tool-result-error-badge">Error</span>' : '<span class="tool-result-ok-badge">Result</span>'}</div><pre class="tool-result-pre">${this.escapeHtml(displayContent)}</pre></div>`;
847
903
  }
848
904
  });
849
905
  }