agentgui 1.0.564 → 1.0.566

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.564",
3
+ "version": "1.0.566",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
@@ -564,7 +564,6 @@ class AgentGUIClient {
564
564
  if (outputEl) outputEl.innerHTML = '';
565
565
  if (this.ui.messageInput) {
566
566
  this.ui.messageInput.value = '';
567
- this.ui.messageInput.disabled = false;
568
567
  this.ui.messageInput.style.height = 'auto';
569
568
  }
570
569
  this.unlockAgentAndModel();
@@ -863,6 +862,9 @@ class AgentGUIClient {
863
862
  // Start polling for chunks from database
864
863
  this.startChunkPolling(data.conversationId);
865
864
 
865
+ // Show queue/steer UI when streaming starts (for busy prompt)
866
+ this.fetchAndRenderQueue(data.conversationId);
867
+
866
868
  this.disableControls();
867
869
  this.emit('streaming:start', data);
868
870
  }
@@ -982,6 +984,10 @@ class AgentGUIClient {
982
984
  // Stop polling for chunks
983
985
  this.stopChunkPolling();
984
986
 
987
+ // Clear queue indicator on error
988
+ const queueEl = document.querySelector('.queue-indicator');
989
+ if (queueEl) queueEl.remove();
990
+
985
991
  // If this is a premature ACP end, render distinct warning block
986
992
  if (data.isPrematureEnd) {
987
993
  this.renderer.queueEvent({
@@ -1033,6 +1039,10 @@ class AgentGUIClient {
1033
1039
 
1034
1040
  this.stopChunkPolling();
1035
1041
 
1042
+ // Clear queue indicator when streaming completes
1043
+ const queueEl = document.querySelector('.queue-indicator');
1044
+ if (queueEl) queueEl.remove();
1045
+
1036
1046
  const sessionId = data.sessionId || this.state.currentSession?.id;
1037
1047
  const streamingEl = document.getElementById(`streaming-${sessionId}`);
1038
1048
  if (streamingEl) {
@@ -2372,24 +2382,21 @@ class AgentGUIClient {
2372
2382
 
2373
2383
  /**
2374
2384
  * Disable UI controls during streaming
2375
- * NOTE: Only disables stop button visibility. Prompt, input, and inject remain enabled.
2385
+ * NOTE: Prompt area is IMMUTABLE - always enabled while connected.
2386
+ * Streaming state is rendered via queue/steer buttons, not input disabling.
2376
2387
  */
2377
2388
  disableControls() {
2378
- // Prompt area and inject button are NEVER disabled during streaming
2379
- // Only stop button behavior changes
2380
- const stopBtn = document.getElementById('stopBtn');
2381
- if (stopBtn) stopBtn.disabled = false;
2389
+ // IMMUTABLE: Prompt state managed only by syncPromptState() based on WebSocket connection
2390
+ // Never disable input during streaming - use queue/steer visibility instead
2382
2391
  }
2383
2392
 
2384
2393
  /**
2385
2394
  * Enable UI controls
2386
- * NOTE: Restores stop button visibility. Prompt area always stays enabled.
2395
+ * NOTE: Prompt area is always enabled when connected.
2387
2396
  */
2388
2397
  enableControls() {
2389
- // Prompt area and inject button are always enabled unless disconnected
2390
- // Only stop button behavior changes
2391
- const stopBtn = document.getElementById('stopBtn');
2392
- if (stopBtn) stopBtn.disabled = true;
2398
+ // IMMUTABLE: Prompt state managed only by syncPromptState() based on WebSocket connection
2399
+ // Never disable input during streaming - use queue/steer visibility instead
2393
2400
  }
2394
2401
 
2395
2402
  /**
@@ -2661,6 +2668,7 @@ class AgentGUIClient {
2661
2668
  }
2662
2669
  const element = this.renderer.renderBlock(chunk.block, chunk);
2663
2670
  if (!element) return;
2671
+ element.classList.add('block-loaded');
2664
2672
  blockFrag.appendChild(element);
2665
2673
  });
2666
2674
 
@@ -2674,6 +2682,7 @@ class AgentGUIClient {
2674
2682
  const contextWithParent = { ...chunk, parentIsOpen: lastBlock.hasAttribute('open') };
2675
2683
  const element = this.renderer.renderBlock(chunk.block, contextWithParent);
2676
2684
  if (element && element !== blockFrag.lastElementChild) {
2685
+ element.classList.add('block-loaded');
2677
2686
  lastBlock.appendChild(element);
2678
2687
  }
2679
2688
  }
@@ -2759,7 +2768,7 @@ class AgentGUIClient {
2759
2768
  if (!conversation || conversation.id !== conversationId) return;
2760
2769
 
2761
2770
  if (this.ui.messageInput) {
2762
- this.ui.messageInput.disabled = !this.wsManager.isConnected;
2771
+ this.ui.messageInput.disabled = false;
2763
2772
  }
2764
2773
  }
2765
2774
 
@@ -3034,14 +3043,11 @@ class AgentGUIClient {
3034
3043
  }
3035
3044
 
3036
3045
  /**
3037
- * Disable prompt area (input and inject button) only on disconnect
3046
+ * Disable prompt area - NEVER CALLED. Prompt must always be enabled.
3047
+ * Keeping method for backward compatibility but it does nothing.
3038
3048
  */
3039
3049
  disablePromptArea() {
3040
- if (this.ui.messageInput) {
3041
- this.ui.messageInput.disabled = true;
3042
- }
3043
- const injectBtn = document.getElementById('injectBtn');
3044
- if (injectBtn) injectBtn.disabled = true;
3050
+ // NEVER disable messageInput - prompt must always be writable
3045
3051
  }
3046
3052
 
3047
3053
  /**
@@ -751,6 +751,7 @@ class StreamingRenderer {
751
751
  if (block.id) details.dataset.toolUseId = block.id;
752
752
  details.classList.add(this._getBlockTypeClass('tool_use'));
753
753
  details.classList.add(this._getToolColorClass(toolName));
754
+ details.open = true;
754
755
  const summary = document.createElement('summary');
755
756
  summary.className = 'folded-tool-bar';
756
757
  const displayName = this.getToolUseDisplayName(toolName);
@@ -1226,6 +1227,20 @@ class StreamingRenderer {
1226
1227
  renderBlockToolResult(block, context) {
1227
1228
  const isError = block.is_error || false;
1228
1229
  const content = block.content || '';
1230
+ const toolName = block.tool_name || block.name || '';
1231
+
1232
+ // Special handling for TodoWrite: render directly without success wrapper
1233
+ if (toolName.includes('TodoWrite') && typeof content === 'object' && content.todos && Array.isArray(content.todos)) {
1234
+ const statusIcons = { completed: '✅', in_progress: '⚙', pending: '☐' };
1235
+ const completedCount = content.todos.filter(t => t.status === 'completed').length;
1236
+ const totalCount = content.todos.length;
1237
+ const items = content.todos.map(t => `<div class="todo-item"><span class="todo-status">${statusIcons[t.status] || '&#9744;'}</span><span class="todo-text">${this.escapeHtml(t.content || '')}</span></div>`).join('');
1238
+ const div = document.createElement('div');
1239
+ div.className = 'block-tool-result';
1240
+ div.innerHTML = `<details class="folded-tool" open><summary class="folded-tool-bar" style="cursor:pointer;padding:0.5rem;background:var(--color-bg-secondary);border-radius:0.25rem;user-select:none"><span style="font-weight:600;font-size:0.9rem">📋 Tasks</span><span style="margin-left:0.5rem;font-size:0.8rem;color:var(--color-text-secondary)">${completedCount}/${totalCount} complete</span></summary><div class="folded-tool-body tool-param-todos" style="padding:0.75rem">${items}</div></details>`;
1241
+ return div;
1242
+ }
1243
+
1229
1244
  const contentStr = typeof content === 'string' ? content : JSON.stringify(content, null, 2);
1230
1245
 
1231
1246
  const details = document.createElement('details');
@@ -2185,7 +2200,8 @@ class StreamingRenderer {
2185
2200
  summary.textContent = summaryText;
2186
2201
 
2187
2202
  const details = document.createElement('details');
2188
- details.className = `block-type-${block.type} ${this._getBlockTypeClass(block.type)}`;
2203
+ const className = `block-${block.type} block-type-${block.type} ${this._getBlockTypeClass(block.type)}`;
2204
+ details.className = className;
2189
2205
  details.setAttribute('data-block-type', block.type);
2190
2206
  details.setAttribute('data-lazy-load', 'pending');
2191
2207
  details.open = block.type === 'success' || (block.type === 'tool_result' && !block.is_error);