agentgui 1.0.568 → 1.0.569
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/index.html +25 -0
- package/static/js/client.js +59 -34
- package/static/js/streaming-renderer.js +4 -3
package/package.json
CHANGED
package/static/index.html
CHANGED
|
@@ -948,6 +948,26 @@
|
|
|
948
948
|
.inject-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
949
949
|
.inject-btn.visible { display: flex; }
|
|
950
950
|
|
|
951
|
+
.queue-btn {
|
|
952
|
+
display: none;
|
|
953
|
+
align-items: center;
|
|
954
|
+
justify-content: center;
|
|
955
|
+
width: 40px;
|
|
956
|
+
height: 40px;
|
|
957
|
+
background-color: #8b5cf6;
|
|
958
|
+
color: white;
|
|
959
|
+
border: none;
|
|
960
|
+
border-radius: 0.5rem;
|
|
961
|
+
cursor: pointer;
|
|
962
|
+
flex-shrink: 0;
|
|
963
|
+
transition: background-color 0.15s;
|
|
964
|
+
margin-right: 0.25rem;
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
.queue-btn:hover:not(:disabled) { background-color: #7c3aed; }
|
|
968
|
+
.queue-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
969
|
+
.queue-btn.visible { display: flex; }
|
|
970
|
+
|
|
951
971
|
.steer-btn {
|
|
952
972
|
display: none;
|
|
953
973
|
align-items: center;
|
|
@@ -3185,6 +3205,11 @@
|
|
|
3185
3205
|
<path d="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>
|
|
3186
3206
|
</svg>
|
|
3187
3207
|
</button>
|
|
3208
|
+
<button class="queue-btn" id="queueBtn" title="Queue message for running agent" aria-label="Queue message">
|
|
3209
|
+
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3210
|
+
<path d="M15 13H1v-2h14v2zm0-4H1V7h14v2zM1 19h14v-2H1v2z"/>
|
|
3211
|
+
</svg>
|
|
3212
|
+
</button>
|
|
3188
3213
|
<button class="steer-btn" id="steerBtn" title="Steer running agent with message" aria-label="Steer agent">
|
|
3189
3214
|
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3190
3215
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm3.5-9c.83 0 1.5-.67 1.5-1.5S16.33 8 15.5 8 14 8.67 14 9.5s.67 1.5 1.5 1.5zm-7 0c.83 0 1.5-.67 1.5-1.5S9.33 8 8.5 8 7 8.67 7 9.5 7.67 11 8.5 11zm3.5 6.5c2.33 0 4.31-1.46 5.11-3.5H6.89c.8 2.04 2.78 3.5 5.11 3.5z"/>
|
package/static/js/client.js
CHANGED
|
@@ -458,6 +458,7 @@ class AgentGUIClient {
|
|
|
458
458
|
|
|
459
459
|
this.ui.stopButton = document.getElementById('stopBtn');
|
|
460
460
|
this.ui.injectButton = document.getElementById('injectBtn');
|
|
461
|
+
this.ui.queueButton = document.getElementById('queueBtn');
|
|
461
462
|
this.ui.steerButton = document.getElementById('steerBtn');
|
|
462
463
|
|
|
463
464
|
if (this.ui.stopButton) {
|
|
@@ -486,6 +487,28 @@ class AgentGUIClient {
|
|
|
486
487
|
});
|
|
487
488
|
}
|
|
488
489
|
|
|
490
|
+
if (this.ui.queueButton) {
|
|
491
|
+
this.ui.queueButton.addEventListener('click', async () => {
|
|
492
|
+
if (!this.state.currentConversation) return;
|
|
493
|
+
const message = this.ui.messageInput?.value || '';
|
|
494
|
+
if (!message.trim()) {
|
|
495
|
+
this.showError('Please enter a message to queue');
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
try {
|
|
499
|
+
const data = await window.wsClient.rpc('conv.queue', { id: this.state.currentConversation.id, content: message });
|
|
500
|
+
console.log('Queue response:', data);
|
|
501
|
+
if (this.ui.messageInput) {
|
|
502
|
+
this.ui.messageInput.value = '';
|
|
503
|
+
this.ui.messageInput.style.height = 'auto';
|
|
504
|
+
}
|
|
505
|
+
} catch (err) {
|
|
506
|
+
console.error('Failed to queue:', err);
|
|
507
|
+
this.showError('Failed to queue: ' + err.message);
|
|
508
|
+
}
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
489
512
|
if (this.ui.steerButton) {
|
|
490
513
|
this.ui.steerButton.addEventListener('click', async () => {
|
|
491
514
|
if (!this.state.currentConversation) return;
|
|
@@ -726,19 +749,13 @@ class AgentGUIClient {
|
|
|
726
749
|
if (this.state.currentConversation?.id !== data.conversationId) {
|
|
727
750
|
console.log('Streaming started for non-active conversation:', data.conversationId);
|
|
728
751
|
this.state.streamingConversations.set(data.conversationId, true);
|
|
752
|
+
this.updateBusyPromptArea(data.conversationId);
|
|
729
753
|
this.emit('streaming:start', data);
|
|
730
754
|
return;
|
|
731
755
|
}
|
|
732
756
|
|
|
733
|
-
// Show stop, steer, queue, and inject buttons when streaming starts for current conversation
|
|
734
|
-
if (this.ui.stopButton) this.ui.stopButton.classList.add('visible');
|
|
735
|
-
if (this.ui.injectButton) this.ui.injectButton.classList.add('visible');
|
|
736
|
-
if (this.ui.steerButton) this.ui.steerButton.classList.add('visible');
|
|
737
|
-
if (this.ui.queueButton) this.ui.queueButton.classList.add('visible');
|
|
738
|
-
if (this.ui.sendButton) this.ui.sendButton.style.display = 'none';
|
|
739
|
-
this.ensurePromptAreaAlwaysEnabled();
|
|
740
|
-
|
|
741
757
|
this.state.streamingConversations.set(data.conversationId, true);
|
|
758
|
+
this.updateBusyPromptArea(data.conversationId);
|
|
742
759
|
this.state.currentSession = {
|
|
743
760
|
id: data.sessionId,
|
|
744
761
|
conversationId: data.conversationId,
|
|
@@ -977,11 +994,13 @@ class AgentGUIClient {
|
|
|
977
994
|
if (conversationId && this.state.currentConversation?.id !== conversationId) {
|
|
978
995
|
console.log('Streaming error for non-active conversation:', conversationId);
|
|
979
996
|
this.state.streamingConversations.delete(conversationId);
|
|
997
|
+
this.updateBusyPromptArea(conversationId);
|
|
980
998
|
this.emit('streaming:error', data);
|
|
981
999
|
return;
|
|
982
1000
|
}
|
|
983
1001
|
|
|
984
1002
|
this.state.streamingConversations.delete(conversationId);
|
|
1003
|
+
this.updateBusyPromptArea(conversationId);
|
|
985
1004
|
|
|
986
1005
|
// Stop polling for chunks
|
|
987
1006
|
this.stopChunkPolling();
|
|
@@ -1021,23 +1040,19 @@ class AgentGUIClient {
|
|
|
1021
1040
|
console.log('Streaming completed:', data);
|
|
1022
1041
|
this._clearThinkingCountdown();
|
|
1023
1042
|
|
|
1024
|
-
// Hide stop, steer, and inject buttons when streaming completes
|
|
1025
|
-
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
1026
|
-
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
1027
|
-
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
1028
|
-
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
1029
|
-
|
|
1030
1043
|
const conversationId = data.conversationId || this.state.currentSession?.conversationId;
|
|
1031
1044
|
if (conversationId) this.invalidateCache(conversationId);
|
|
1032
1045
|
|
|
1033
1046
|
if (conversationId && this.state.currentConversation?.id !== conversationId) {
|
|
1034
1047
|
console.log('Streaming completed for non-active conversation:', conversationId);
|
|
1035
1048
|
this.state.streamingConversations.delete(conversationId);
|
|
1049
|
+
this.updateBusyPromptArea(conversationId);
|
|
1036
1050
|
this.emit('streaming:complete', data);
|
|
1037
1051
|
return;
|
|
1038
1052
|
}
|
|
1039
1053
|
|
|
1040
1054
|
this.state.streamingConversations.delete(conversationId);
|
|
1055
|
+
this.updateBusyPromptArea(conversationId);
|
|
1041
1056
|
|
|
1042
1057
|
this.stopChunkPolling();
|
|
1043
1058
|
|
|
@@ -2489,6 +2504,7 @@ class AgentGUIClient {
|
|
|
2489
2504
|
|
|
2490
2505
|
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
2491
2506
|
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
2507
|
+
if (this.ui.queueButton) this.ui.queueButton.classList.remove('visible');
|
|
2492
2508
|
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
2493
2509
|
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
2494
2510
|
|
|
@@ -2668,27 +2684,27 @@ class AgentGUIClient {
|
|
|
2668
2684
|
toolResultBlocks.set(chunk.id, chunk);
|
|
2669
2685
|
return;
|
|
2670
2686
|
}
|
|
2671
|
-
const element = this.renderer.renderBlock(chunk.block, chunk);
|
|
2687
|
+
const element = this.renderer.renderBlock(chunk.block, chunk, blockFrag);
|
|
2672
2688
|
if (!element) return;
|
|
2673
|
-
// Ensure CSS classes are always applied for styling consistency
|
|
2674
2689
|
element.classList.add('block-loaded');
|
|
2675
|
-
element.classList.add(`block-type-${chunk.block.type}`);
|
|
2676
2690
|
blockFrag.appendChild(element);
|
|
2677
2691
|
});
|
|
2678
2692
|
|
|
2679
2693
|
blocksEl.appendChild(blockFrag);
|
|
2680
2694
|
|
|
2681
|
-
toolResultBlocks.forEach((chunk
|
|
2682
|
-
const
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2695
|
+
toolResultBlocks.forEach((chunk) => {
|
|
2696
|
+
const toolUseId = chunk.block.tool_use_id;
|
|
2697
|
+
const toolUseEl = toolUseId
|
|
2698
|
+
? blocksEl.querySelector(`.block-tool-use[data-tool-use-id="${toolUseId}"]`)
|
|
2699
|
+
: blocksEl.lastElementChild?.classList?.contains('block-type-tool_use') ? blocksEl.lastElementChild : null;
|
|
2700
|
+
if (!toolUseEl) return;
|
|
2701
|
+
toolUseEl.classList.remove('has-success', 'has-error');
|
|
2702
|
+
toolUseEl.classList.add(chunk.block.is_error ? 'has-error' : 'has-success');
|
|
2703
|
+
const contextWithParent = { ...chunk, parentIsOpen: toolUseEl.hasAttribute('open') };
|
|
2704
|
+
const element = this.renderer.renderBlock(chunk.block, contextWithParent, toolUseEl);
|
|
2705
|
+
if (element) {
|
|
2706
|
+
element.classList.add('block-loaded');
|
|
2707
|
+
toolUseEl.appendChild(element);
|
|
2692
2708
|
}
|
|
2693
2709
|
});
|
|
2694
2710
|
|
|
@@ -2779,20 +2795,22 @@ class AgentGUIClient {
|
|
|
2779
2795
|
}
|
|
2780
2796
|
|
|
2781
2797
|
updateBusyPromptArea(conversationId) {
|
|
2798
|
+
if (this.state.currentConversation?.id !== conversationId) return;
|
|
2782
2799
|
const isStreaming = this.state.streamingConversations.has(conversationId);
|
|
2783
2800
|
const isConnected = this.wsManager?.isConnected;
|
|
2784
2801
|
|
|
2785
2802
|
const injectBtn = document.getElementById('injectBtn');
|
|
2786
2803
|
const steerBtn = document.getElementById('steerBtn');
|
|
2804
|
+
const queueBtn = document.getElementById('queueBtn');
|
|
2787
2805
|
const stopBtn = document.getElementById('stopBtn');
|
|
2788
2806
|
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2807
|
+
[injectBtn, steerBtn, queueBtn, stopBtn].forEach(btn => {
|
|
2808
|
+
if (!btn) return;
|
|
2809
|
+
btn.classList.toggle('visible', isStreaming);
|
|
2810
|
+
btn.disabled = !isConnected;
|
|
2811
|
+
});
|
|
2792
2812
|
|
|
2793
|
-
if (
|
|
2794
|
-
if (steerBtn) steerBtn.disabled = !isConnected;
|
|
2795
|
-
if (stopBtn) stopBtn.disabled = !isConnected;
|
|
2813
|
+
if (this.ui.sendButton) this.ui.sendButton.style.display = isStreaming ? 'none' : '';
|
|
2796
2814
|
}
|
|
2797
2815
|
|
|
2798
2816
|
removeScrollUpDetection() {
|
|
@@ -3070,6 +3088,9 @@ class AgentGUIClient {
|
|
|
3070
3088
|
if (this.ui.steerButton && this.ui.steerButton.classList.contains('visible')) {
|
|
3071
3089
|
this.ui.steerButton.disabled = !this.wsManager.isConnected;
|
|
3072
3090
|
}
|
|
3091
|
+
if (this.ui.queueButton && this.ui.queueButton.classList.contains('visible')) {
|
|
3092
|
+
this.ui.queueButton.disabled = !this.wsManager.isConnected;
|
|
3093
|
+
}
|
|
3073
3094
|
}
|
|
3074
3095
|
|
|
3075
3096
|
/**
|
|
@@ -3103,6 +3124,10 @@ class AgentGUIClient {
|
|
|
3103
3124
|
this.ui.steerButton.classList.add('visible');
|
|
3104
3125
|
this.ui.steerButton.disabled = !this.wsManager.isConnected;
|
|
3105
3126
|
}
|
|
3127
|
+
if (this.ui.queueButton) {
|
|
3128
|
+
this.ui.queueButton.classList.add('visible');
|
|
3129
|
+
this.ui.queueButton.disabled = !this.wsManager.isConnected;
|
|
3130
|
+
}
|
|
3106
3131
|
}
|
|
3107
3132
|
|
|
3108
3133
|
/**
|
|
@@ -1230,7 +1230,8 @@ class StreamingRenderer {
|
|
|
1230
1230
|
const toolName = block.tool_name || block.name || '';
|
|
1231
1231
|
|
|
1232
1232
|
// Special handling for TodoWrite: render directly without success wrapper
|
|
1233
|
-
|
|
1233
|
+
// Detect by tool name OR by content structure (todos array)
|
|
1234
|
+
if ((toolName.includes('TodoWrite') || (typeof content === 'object' && Array.isArray(content?.todos))) && typeof content === 'object' && content.todos && Array.isArray(content.todos)) {
|
|
1234
1235
|
const statusIcons = { completed: '✅', in_progress: '⚙', pending: '☐' };
|
|
1235
1236
|
const completedCount = content.todos.filter(t => t.status === 'completed').length;
|
|
1236
1237
|
const totalCount = content.todos.length;
|
|
@@ -2200,8 +2201,8 @@ class StreamingRenderer {
|
|
|
2200
2201
|
summary.textContent = summaryText;
|
|
2201
2202
|
|
|
2202
2203
|
const details = document.createElement('details');
|
|
2203
|
-
|
|
2204
|
-
details.
|
|
2204
|
+
details.className = `block-${block.type}`;
|
|
2205
|
+
details.classList.add(this._getBlockTypeClass(block.type));
|
|
2205
2206
|
details.setAttribute('data-block-type', block.type);
|
|
2206
2207
|
details.setAttribute('data-lazy-load', 'pending');
|
|
2207
2208
|
details.open = block.type === 'success' || (block.type === 'tool_result' && !block.is_error);
|