agentgui 1.0.563 → 1.0.565
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 +1 -1
- package/static/js/client.js +27 -26
- package/static/js/streaming-renderer.js +18 -1
package/package.json
CHANGED
package/static/js/client.js
CHANGED
|
@@ -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,7 +862,10 @@ class AgentGUIClient {
|
|
|
863
862
|
// Start polling for chunks from database
|
|
864
863
|
this.startChunkPolling(data.conversationId);
|
|
865
864
|
|
|
866
|
-
|
|
865
|
+
// Show queue/steer UI when streaming starts (for busy prompt)
|
|
866
|
+
this.fetchAndRenderQueue(data.conversationId);
|
|
867
|
+
|
|
868
|
+
// IMMUTABLE: Prompt area remains enabled - user can queue/steer messages
|
|
867
869
|
this.emit('streaming:start', data);
|
|
868
870
|
}
|
|
869
871
|
|
|
@@ -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:
|
|
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
|
|
2379
|
-
//
|
|
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:
|
|
2395
|
+
* NOTE: Prompt area is always enabled when connected.
|
|
2387
2396
|
*/
|
|
2388
2397
|
enableControls() {
|
|
2389
|
-
// Prompt
|
|
2390
|
-
//
|
|
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
|
/**
|
|
@@ -2659,8 +2666,9 @@ class AgentGUIClient {
|
|
|
2659
2666
|
toolResultBlocks.set(chunk.id, chunk);
|
|
2660
2667
|
return;
|
|
2661
2668
|
}
|
|
2662
|
-
const element = this.renderer.
|
|
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
|
}
|
|
@@ -2738,7 +2747,7 @@ class AgentGUIClient {
|
|
|
2738
2747
|
|
|
2739
2748
|
this.chunkPollState.lastFetchTimestamp = lastChunkTime;
|
|
2740
2749
|
this.startChunkPolling(conversationId);
|
|
2741
|
-
|
|
2750
|
+
// IMMUTABLE: Prompt remains enabled - syncPromptState will set correct state
|
|
2742
2751
|
this.syncPromptState(conversationId);
|
|
2743
2752
|
} else {
|
|
2744
2753
|
this.syncPromptState(conversationId);
|
|
@@ -2759,12 +2768,7 @@ class AgentGUIClient {
|
|
|
2759
2768
|
if (!conversation || conversation.id !== conversationId) return;
|
|
2760
2769
|
|
|
2761
2770
|
if (this.ui.messageInput) {
|
|
2762
|
-
|
|
2763
|
-
if (isStreaming) {
|
|
2764
|
-
this.ui.messageInput.disabled = true;
|
|
2765
|
-
} else {
|
|
2766
|
-
this.ui.messageInput.disabled = !this.wsManager.isConnected;
|
|
2767
|
-
}
|
|
2771
|
+
this.ui.messageInput.disabled = false;
|
|
2768
2772
|
}
|
|
2769
2773
|
}
|
|
2770
2774
|
|
|
@@ -3039,14 +3043,11 @@ class AgentGUIClient {
|
|
|
3039
3043
|
}
|
|
3040
3044
|
|
|
3041
3045
|
/**
|
|
3042
|
-
* Disable prompt area
|
|
3046
|
+
* Disable prompt area - NEVER CALLED. Prompt must always be enabled.
|
|
3047
|
+
* Keeping method for backward compatibility but it does nothing.
|
|
3043
3048
|
*/
|
|
3044
3049
|
disablePromptArea() {
|
|
3045
|
-
|
|
3046
|
-
this.ui.messageInput.disabled = true;
|
|
3047
|
-
}
|
|
3048
|
-
const injectBtn = document.getElementById('injectBtn');
|
|
3049
|
-
if (injectBtn) injectBtn.disabled = true;
|
|
3050
|
+
// NEVER disable messageInput - prompt must always be writable
|
|
3050
3051
|
}
|
|
3051
3052
|
|
|
3052
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] || '☐'}</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,9 +2200,11 @@ class StreamingRenderer {
|
|
|
2185
2200
|
summary.textContent = summaryText;
|
|
2186
2201
|
|
|
2187
2202
|
const details = document.createElement('details');
|
|
2188
|
-
|
|
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');
|
|
2207
|
+
details.open = block.type === 'success' || (block.type === 'tool_result' && !block.is_error);
|
|
2191
2208
|
details.appendChild(summary);
|
|
2192
2209
|
|
|
2193
2210
|
// Attach lazy loader on first open
|