agentgui 1.0.258 → 1.0.260

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.258",
3
+ "version": "1.0.260",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/static/index.html CHANGED
@@ -1715,15 +1715,15 @@
1715
1715
  text-align: center;
1716
1716
  }
1717
1717
 
1718
- /* --- Folded Tool Use (compact success-style bar) --- */
1718
+ /* --- Folded Tool Use (neutral base, colored by block-type-* classes) --- */
1719
1719
  .folded-tool {
1720
1720
  margin: 0;
1721
1721
  border-radius: 0.375rem;
1722
1722
  overflow: hidden;
1723
- background: #f0fdf4;
1723
+ background: #f3f4f6;
1724
1724
  }
1725
1725
  html.dark .folded-tool {
1726
- background: #0a1f0f;
1726
+ background: #1f2937;
1727
1727
  }
1728
1728
  .folded-tool-bar {
1729
1729
  display: flex;
@@ -1735,11 +1735,11 @@
1735
1735
  list-style: none;
1736
1736
  font-size: 0.85rem;
1737
1737
  line-height: 1.3;
1738
- background: #dcfce7;
1738
+ background: #e5e7eb;
1739
1739
  transition: background 0.15s;
1740
1740
  }
1741
1741
  html.dark .folded-tool-bar {
1742
- background: #0f2b1a;
1742
+ background: #374151;
1743
1743
  }
1744
1744
  .folded-tool-bar::-webkit-details-marker { display: none; }
1745
1745
  .folded-tool-bar::marker { display: none; content: ''; }
@@ -1749,33 +1749,33 @@
1749
1749
  margin-right: 0.125rem;
1750
1750
  display: inline-block;
1751
1751
  transition: transform 0.15s;
1752
- color: #16a34a;
1752
+ color: #6b7280;
1753
1753
  flex-shrink: 0;
1754
1754
  }
1755
- html.dark .folded-tool-bar::before { color: #4ade80; }
1755
+ html.dark .folded-tool-bar::before { color: #9ca3af; }
1756
1756
  .folded-tool[open] > .folded-tool-bar::before { transform: rotate(90deg); }
1757
- .folded-tool-bar:hover { background: #bbf7d0; }
1758
- html.dark .folded-tool-bar:hover { background: #14532d; }
1757
+ .folded-tool-bar:hover { background: #d1d5db; }
1758
+ html.dark .folded-tool-bar:hover { background: #4b5563; }
1759
1759
  .folded-tool-icon {
1760
1760
  display: flex;
1761
1761
  align-items: center;
1762
- color: #16a34a;
1762
+ color: #6b7280;
1763
1763
  width: 1rem;
1764
1764
  height: 1rem;
1765
1765
  flex-shrink: 0;
1766
1766
  }
1767
- html.dark .folded-tool-icon { color: #4ade80; }
1767
+ html.dark .folded-tool-icon { color: #9ca3af; }
1768
1768
  .folded-tool-icon svg { width: 1rem; height: 1rem; }
1769
1769
  .folded-tool-name {
1770
1770
  font-weight: 600;
1771
- color: #166534;
1771
+ color: #374151;
1772
1772
  font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1773
1773
  font-size: 0.8rem;
1774
1774
  flex-shrink: 0;
1775
1775
  }
1776
- html.dark .folded-tool-name { color: #86efac; }
1776
+ html.dark .folded-tool-name { color: #d1d5db; }
1777
1777
  .folded-tool-desc {
1778
- color: #15803d;
1778
+ color: #6b7280;
1779
1779
  font-family: 'Monaco','Menlo','Ubuntu Mono', monospace;
1780
1780
  font-size: 0.8rem;
1781
1781
  overflow: hidden;
@@ -1785,13 +1785,13 @@
1785
1785
  min-width: 0;
1786
1786
  opacity: 0.8;
1787
1787
  }
1788
- html.dark .folded-tool-desc { color: #4ade80; }
1788
+ html.dark .folded-tool-desc { color: #9ca3af; }
1789
1789
  .folded-tool-body {
1790
1790
  padding: 0.5rem 0.75rem;
1791
1791
  font-size: 0.75rem;
1792
- border-top: 1px solid #bbf7d0;
1792
+ border-top: 1px solid #d1d5db;
1793
1793
  }
1794
- html.dark .folded-tool-body { border-top-color: #166534; }
1794
+ html.dark .folded-tool-body { border-top-color: #4b5563; }
1795
1795
  .folded-tool-body .tool-input-pre {
1796
1796
  margin: 0;
1797
1797
  padding: 0.375rem 0.5rem;
@@ -2007,6 +2007,41 @@
2007
2007
  .tool-result-success .folded-tool-name { color: #166534; font-weight: 600; }
2008
2008
  html.dark .tool-result-success .folded-tool-name { color: #86efac; }
2009
2009
 
2010
+ /* --- Tool_use parent: has-success / has-error indicators on header --- */
2011
+ .folded-tool.has-success { background: #f0fdf4; }
2012
+ html.dark .folded-tool.has-success { background: #0a1f0f; }
2013
+ .folded-tool.has-success > .folded-tool-bar { background: #dcfce7; }
2014
+ html.dark .folded-tool.has-success > .folded-tool-bar { background: #0f2b1a; }
2015
+ .folded-tool.has-success > .folded-tool-bar:hover { background: #bbf7d0; }
2016
+ html.dark .folded-tool.has-success > .folded-tool-bar:hover { background: #14532d; }
2017
+ .folded-tool.has-success > .folded-tool-bar::before { color: #16a34a; }
2018
+ html.dark .folded-tool.has-success > .folded-tool-bar::before { color: #4ade80; }
2019
+ .folded-tool.has-success .folded-tool-icon { color: #16a34a; }
2020
+ html.dark .folded-tool.has-success .folded-tool-icon { color: #4ade80; }
2021
+ .folded-tool.has-success .folded-tool-name { color: #166534; }
2022
+ html.dark .folded-tool.has-success .folded-tool-name { color: #86efac; }
2023
+ .folded-tool.has-success .folded-tool-desc { color: #15803d; }
2024
+ html.dark .folded-tool.has-success .folded-tool-desc { color: #4ade80; }
2025
+ .folded-tool.has-success > .folded-tool-body { border-top-color: #bbf7d0; }
2026
+ html.dark .folded-tool.has-success > .folded-tool-body { border-top-color: #166534; }
2027
+
2028
+ .folded-tool.has-error { background: #fef2f2; }
2029
+ html.dark .folded-tool.has-error { background: #1c0f0f; }
2030
+ .folded-tool.has-error > .folded-tool-bar { background: #fee2e2; }
2031
+ html.dark .folded-tool.has-error > .folded-tool-bar { background: #2c1010; }
2032
+ .folded-tool.has-error > .folded-tool-bar:hover { background: #fecaca; }
2033
+ html.dark .folded-tool.has-error > .folded-tool-bar:hover { background: #451a1a; }
2034
+ .folded-tool.has-error > .folded-tool-bar::before { color: #ef4444; }
2035
+ html.dark .folded-tool.has-error > .folded-tool-bar::before { color: #f87171; }
2036
+ .folded-tool.has-error .folded-tool-icon { color: #ef4444; }
2037
+ html.dark .folded-tool.has-error .folded-tool-icon { color: #f87171; }
2038
+ .folded-tool.has-error .folded-tool-name { color: #991b1b; }
2039
+ html.dark .folded-tool.has-error .folded-tool-name { color: #fca5a5; }
2040
+ .folded-tool.has-error .folded-tool-desc { color: #b91c1c; }
2041
+ html.dark .folded-tool.has-error .folded-tool-desc { color: #f87171; }
2042
+ .folded-tool.has-error > .folded-tool-body { border-top-color: #fecaca; }
2043
+ html.dark .folded-tool.has-error > .folded-tool-body { border-top-color: #7f1d1d; }
2044
+
2010
2045
  /* --- Consecutive block joining --- */
2011
2046
  .folded-tool + .folded-tool,
2012
2047
  .block-tool-use + .block-tool-use {
@@ -611,6 +611,8 @@ class AgentGUIClient {
611
611
  if (chunk.block.type === 'tool_result') {
612
612
  const lastInFrag = bFrag.lastElementChild;
613
613
  if (lastInFrag?.classList?.contains('block-tool-use')) {
614
+ lastInFrag.classList.remove('has-success', 'has-error');
615
+ lastInFrag.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
614
616
  const parentIsOpen = lastInFrag.hasAttribute('open');
615
617
  const contextWithParent = { ...chunk, parentIsOpen };
616
618
  const el = this.renderer.renderBlock(chunk.block, contextWithParent, bFrag);
@@ -1141,7 +1143,7 @@ class AgentGUIClient {
1141
1143
  let html = '<div class="message-blocks">';
1142
1144
  if (content.blocks && Array.isArray(content.blocks)) {
1143
1145
  let pendingToolUseClose = false;
1144
- content.blocks.forEach(block => {
1146
+ content.blocks.forEach((block, blockIdx, blocks) => {
1145
1147
  if (block.type !== 'tool_result' && pendingToolUseClose) {
1146
1148
  html += '</details>';
1147
1149
  pendingToolUseClose = false;
@@ -1186,7 +1188,9 @@ class AgentGUIClient {
1186
1188
  const tTitle = hasRenderer && block.input ? StreamingRenderer.getToolTitle(tn, block.input) : '';
1187
1189
  const iconHtml = hasRenderer && this.renderer ? `<span class="folded-tool-icon">${this.renderer.getToolIcon(tn)}</span>` : '';
1188
1190
  const typeClass = hasRenderer && this.renderer ? this.renderer._getBlockTypeClass('tool_use') : 'block-type-tool_use';
1189
- html += `<details class="block-tool-use folded-tool ${typeClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}</summary>${inputHtml}`;
1191
+ const nextBlock = blocks[blockIdx + 1];
1192
+ const resultClass = nextBlock?.type === 'tool_result' ? (nextBlock.is_error ? 'has-error' : 'has-success') : '';
1193
+ html += `<details class="block-tool-use folded-tool ${typeClass} ${resultClass}"><summary class="folded-tool-bar">${iconHtml}<span class="folded-tool-name">${this.escapeHtml(dName)}</span>${tTitle ? `<span class="folded-tool-desc">${this.escapeHtml(tTitle)}</span>` : ''}</summary>${inputHtml}`;
1190
1194
  pendingToolUseClose = true;
1191
1195
  } else if (block.type === 'tool_result') {
1192
1196
  const content = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
@@ -1751,6 +1755,8 @@ class AgentGUIClient {
1751
1755
  const lastEl = blocksEl.lastElementChild;
1752
1756
  const toolUseEl = matchById || (lastEl?.classList?.contains('block-tool-use') ? lastEl : null);
1753
1757
  if (toolUseEl) {
1758
+ toolUseEl.classList.remove('has-success', 'has-error');
1759
+ toolUseEl.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
1754
1760
  const parentIsOpen = toolUseEl.hasAttribute('open');
1755
1761
  const contextWithParent = { ...chunk, parentIsOpen };
1756
1762
  const element = this.renderer.renderBlock(chunk.block, contextWithParent, blocksEl);
@@ -1811,6 +1817,8 @@ class AgentGUIClient {
1811
1817
  const lastEl = blocksEl.lastElementChild;
1812
1818
  const toolUseEl = matchById || (lastEl?.classList?.contains('block-tool-use') ? lastEl : null);
1813
1819
  if (toolUseEl) {
1820
+ toolUseEl.classList.remove('has-success', 'has-error');
1821
+ toolUseEl.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
1814
1822
  const parentIsOpen = toolUseEl.hasAttribute('open');
1815
1823
  const contextWithParent = { ...chunk, parentIsOpen };
1816
1824
  const el = this.renderer.renderBlock(chunk.block, contextWithParent, blocksEl);
@@ -2390,6 +2398,8 @@ class AgentGUIClient {
2390
2398
  if (chunk.block.type === 'tool_result') {
2391
2399
  const lastInFrag = blockFrag.lastElementChild;
2392
2400
  if (lastInFrag?.classList?.contains('block-tool-use')) {
2401
+ lastInFrag.classList.remove('has-success', 'has-error');
2402
+ lastInFrag.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
2393
2403
  const parentIsOpen = lastInFrag.hasAttribute('open');
2394
2404
  const contextWithParent = { ...chunk, parentIsOpen };
2395
2405
  const element = this.renderer.renderBlock(chunk.block, contextWithParent, blockFrag);
@@ -17,6 +17,7 @@
17
17
  var isLoadingHistory = false;
18
18
  var _lastVoiceBlockText = null;
19
19
  var _lastVoiceBlockTime = 0;
20
+ var _voiceBreakNext = false;
20
21
  var selectedVoiceId = localStorage.getItem('voice-selected-id') || 'default';
21
22
  var ttsAudioCache = new Map();
22
23
  var TTS_CLIENT_CACHE_MAX = 50;
@@ -549,7 +550,7 @@
549
550
  var emptyMsg = container.querySelector('.voice-empty');
550
551
  if (emptyMsg) emptyMsg.remove();
551
552
  var lastChild = container.lastElementChild;
552
- if (!isUser && lastChild && lastChild.classList.contains('voice-block') && !lastChild.classList.contains('voice-block-user')) {
553
+ if (!isUser && !_voiceBreakNext && lastChild && lastChild.classList.contains('voice-block') && !lastChild.classList.contains('voice-block-user')) {
553
554
  var contentSpan = lastChild.querySelector('.voice-block-content');
554
555
  if (contentSpan) {
555
556
  contentSpan.textContent += '\n' + stripHtml(text);
@@ -558,6 +559,7 @@
558
559
  return lastChild;
559
560
  }
560
561
  }
562
+ _voiceBreakNext = false;
561
563
  var div = document.createElement('div');
562
564
  div.className = 'voice-block' + (isUser ? ' voice-block-user' : '');
563
565
  if (isUser) {
@@ -666,6 +668,7 @@
666
668
  if (data.conversationId && data.conversationId !== currentConversationId) return;
667
669
  spokenChunks = new Set();
668
670
  renderedSeqs = new Set();
671
+ _voiceBreakNext = false;
669
672
  }
670
673
  });
671
674
  window.addEventListener('conversation-selected', function(e) {
@@ -682,7 +685,6 @@
682
685
  function handleVoiceBlock(block, isNew) {
683
686
  if (!block || !block.type) return;
684
687
  if (block.type === 'text' && block.text) {
685
- // Deduplicate: prevent rendering the same text block twice within 500ms
686
688
  var now = Date.now();
687
689
  if (_lastVoiceBlockText === block.text && (now - _lastVoiceBlockTime) < 500) {
688
690
  return;
@@ -697,7 +699,10 @@
697
699
  setTimeout(function() { div.classList.remove('speaking'); }, 2000);
698
700
  }
699
701
  } else if (block.type === 'result') {
702
+ _voiceBreakNext = true;
700
703
  addVoiceResultBlock(block, isNew);
704
+ } else {
705
+ _voiceBreakNext = true;
701
706
  }
702
707
  }
703
708
 
@@ -705,9 +710,9 @@
705
710
  var container = document.getElementById('voiceMessages');
706
711
  if (!container) return;
707
712
  container.innerHTML = '';
708
- // Reset dedup state when loading a new conversation
709
713
  _lastVoiceBlockText = null;
710
714
  _lastVoiceBlockTime = 0;
715
+ _voiceBreakNext = false;
711
716
  if (!conversationId) {
712
717
  showVoiceEmpty(container);
713
718
  return;
@@ -722,6 +727,7 @@
722
727
  return;
723
728
  }
724
729
  var hasContent = false;
730
+ _voiceBreakNext = false;
725
731
  data.chunks.forEach(function(chunk) {
726
732
  if (chunk.sequence !== undefined) renderedSeqs.add(chunk.sequence);
727
733
  var block = typeof chunk.data === 'string' ? JSON.parse(chunk.data) : chunk.data;
@@ -730,8 +736,11 @@
730
736
  addVoiceBlock(block.text, false);
731
737
  hasContent = true;
732
738
  } else if (block.type === 'result') {
739
+ _voiceBreakNext = true;
733
740
  addVoiceResultBlock(block, false);
734
741
  hasContent = true;
742
+ } else {
743
+ _voiceBreakNext = true;
735
744
  }
736
745
  });
737
746
  if (!hasContent) showVoiceEmpty(container);