agentgui 1.0.554 → 1.0.556

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.554",
3
+ "version": "1.0.556",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -1179,9 +1179,11 @@ class AgentGUIClient {
1179
1179
  outputEl.appendChild(queueEl);
1180
1180
  }
1181
1181
 
1182
+ const isStreaming = this.state.streamingConversations.has(conversationId);
1182
1183
  queueEl.innerHTML = queue.map((q, i) => `
1183
1184
  <div class="queue-item" data-message-id="${q.messageId}" style="padding:0.5rem 1rem;margin:0.5rem 0;border-radius:0.375rem;background:var(--color-warning);color:#000;font-size:0.875rem;display:flex;align-items:center;gap:0.5rem;">
1184
1185
  <span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${i + 1}. ${this.escapeHtml(q.content)}</span>
1186
+ ${isStreaming ? `<button class="queue-steer-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:#06b6d4;border:1px solid #0891b2;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;color:#fff;">Steer</button>` : ''}
1185
1187
  <button class="queue-edit-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:transparent;border:1px solid #000;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;">Edit</button>
1186
1188
  <button class="queue-delete-btn" data-index="${i}" style="padding:0.25rem 0.5rem;background:transparent;border:1px solid #000;border-radius:0.25rem;cursor:pointer;font-size:0.75rem;">Delete</button>
1187
1189
  </div>
@@ -1190,7 +1192,21 @@ class AgentGUIClient {
1190
1192
  if (!queueEl._listenersAttached) {
1191
1193
  queueEl._listenersAttached = true;
1192
1194
  queueEl.addEventListener('click', async (e) => {
1193
- if (e.target.classList.contains('queue-delete-btn')) {
1195
+ if (e.target.classList.contains('queue-steer-btn')) {
1196
+ const index = parseInt(e.target.dataset.index);
1197
+ const q = queue[index];
1198
+ try {
1199
+ const data = await window.wsClient.rpc('conv.steer', { id: conversationId, content: q.content });
1200
+ console.log('Steer response:', data);
1201
+ if (data.ok && data.steered) {
1202
+ // Remove from queue after successful steer
1203
+ await window.wsClient.rpc('q.del', { id: conversationId, messageId: q.messageId });
1204
+ }
1205
+ } catch (err) {
1206
+ console.error('Failed to steer:', err);
1207
+ this.showError('Failed to steer: ' + err.message);
1208
+ }
1209
+ } else if (e.target.classList.contains('queue-delete-btn')) {
1194
1210
  const index = parseInt(e.target.dataset.index);
1195
1211
  const msgId = queue[index].messageId;
1196
1212
  if (await window.UIDialog.confirm('Delete this queued message?', 'Delete Message')) {
@@ -2621,26 +2637,34 @@ class AgentGUIClient {
2621
2637
 
2622
2638
  const blocksEl = messageDiv.querySelector('.message-blocks');
2623
2639
  const blockFrag = document.createDocumentFragment();
2640
+ const toolResultBlocks = new Map();
2641
+
2624
2642
  sessionChunkList.forEach(chunk => {
2625
2643
  if (!chunk.block?.type) return;
2626
2644
  if (chunk.block.type === 'tool_result') {
2627
- const lastInFrag = blockFrag.lastElementChild;
2628
- if (lastInFrag?.classList?.contains('block-tool-use')) {
2629
- lastInFrag.classList.remove('has-success', 'has-error');
2630
- lastInFrag.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
2631
- const parentIsOpen = lastInFrag.hasAttribute('open');
2632
- const contextWithParent = { ...chunk, parentIsOpen };
2633
- const element = this.renderer.renderBlock(chunk.block, contextWithParent, blockFrag);
2634
- if (element) { lastInFrag.appendChild(element); }
2635
- return;
2636
- }
2645
+ toolResultBlocks.set(chunk.id, chunk);
2646
+ return;
2637
2647
  }
2638
- const element = this.renderer.renderBlock(chunk.block, chunk, blockFrag);
2648
+ const element = this.renderer.renderBlockHeader(chunk.block, chunk);
2639
2649
  if (!element) return;
2640
2650
  blockFrag.appendChild(element);
2641
2651
  });
2652
+
2642
2653
  blocksEl.appendChild(blockFrag);
2643
2654
 
2655
+ toolResultBlocks.forEach((chunk, chunkId) => {
2656
+ const lastBlock = blocksEl.lastElementChild;
2657
+ if (lastBlock?.classList?.contains('block-type-tool_use')) {
2658
+ lastBlock.classList.remove('has-success', 'has-error');
2659
+ lastBlock.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
2660
+ const contextWithParent = { ...chunk, parentIsOpen: lastBlock.hasAttribute('open') };
2661
+ const element = this.renderer.renderBlock(chunk.block, contextWithParent);
2662
+ if (element && element !== blockFrag.lastElementChild) {
2663
+ lastBlock.appendChild(element);
2664
+ }
2665
+ }
2666
+ });
2667
+
2644
2668
  if (isCurrentActiveSession) {
2645
2669
  const indicatorDiv = document.createElement('div');
2646
2670
  indicatorDiv.className = 'streaming-indicator';
@@ -2154,6 +2154,59 @@ class StreamingRenderer {
2154
2154
  }
2155
2155
  }
2156
2156
 
2157
+ /**
2158
+ * Render block header with lazy-loading placeholder for body
2159
+ * Returns a <details> element with just the summary, body content deferred
2160
+ */
2161
+ renderBlockHeader(block, context = {}) {
2162
+ if (!block || !block.type) return null;
2163
+
2164
+ const typeLabel = block.type.charAt(0).toUpperCase() + block.type.slice(1).replace(/_/g, ' ');
2165
+ const summary = document.createElement('summary');
2166
+ summary.style.cursor = 'pointer';
2167
+ summary.style.userSelect = 'none';
2168
+ summary.className = 'block-header-summary';
2169
+
2170
+ let summaryText = typeLabel;
2171
+ if (block.type === 'code' && block.language) {
2172
+ summaryText += ` (${block.language})`;
2173
+ } else if (block.type === 'bash' && block.source) {
2174
+ summaryText += ` - ${block.source}`;
2175
+ } else if (block.type === 'tool_use' && block.name) {
2176
+ summaryText += ` - ${block.name}`;
2177
+ } else if (block.type === 'text' && block.text) {
2178
+ const preview = block.text.substring(0, 60).replace(/\n/g, ' ');
2179
+ summaryText = preview + (block.text.length > 60 ? '...' : '');
2180
+ }
2181
+
2182
+ summary.textContent = summaryText;
2183
+
2184
+ const details = document.createElement('details');
2185
+ details.className = `block-type-${block.type}`;
2186
+ details.setAttribute('data-block-type', block.type);
2187
+ details.setAttribute('data-lazy-load', 'pending');
2188
+ details.appendChild(summary);
2189
+
2190
+ // Attach lazy loader on first open
2191
+ details.addEventListener('toggle', async (e) => {
2192
+ if (details.open && details.getAttribute('data-lazy-load') === 'pending') {
2193
+ details.setAttribute('data-lazy-load', 'loading');
2194
+ try {
2195
+ const body = this.renderBlock(block, context);
2196
+ if (body && body !== summary) {
2197
+ details.appendChild(body);
2198
+ }
2199
+ details.setAttribute('data-lazy-load', 'loaded');
2200
+ } catch (err) {
2201
+ console.error('Failed to lazy-load block:', err);
2202
+ details.setAttribute('data-lazy-load', 'failed');
2203
+ }
2204
+ }
2205
+ }, { once: false });
2206
+
2207
+ return details;
2208
+ }
2209
+
2157
2210
  /**
2158
2211
  * Cleanup resources
2159
2212
  */