agentgui 1.0.502 → 1.0.504
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/CLAUDE.md +29 -0
- package/package.json +1 -1
- package/static/js/client.js +2 -2
- package/static/js/voice.js +43 -3
package/CLAUDE.md
CHANGED
|
@@ -84,6 +84,12 @@ All routes are prefixed with `BASE_URL` (default `/gm`).
|
|
|
84
84
|
- `GET /api/speech-status` - Speech model loading status
|
|
85
85
|
- `POST /api/folders` - Create folder
|
|
86
86
|
- `GET /api/tools` - List detected tools with installation status (via WebSocket tools.list handler)
|
|
87
|
+
- `GET /api/tools/:id/status` - Get tool installation status (version, installed_at, error_message)
|
|
88
|
+
- `POST /api/tools/:id/install` - Start tool installation (returns `{ success: true }` with background async install)
|
|
89
|
+
- `POST /api/tools/:id/update` - Start tool update (body: targetVersion)
|
|
90
|
+
- `GET /api/tools/:id/history` - Get tool install/update history (query: limit, offset)
|
|
91
|
+
- `POST /api/tools/update` - Batch update all tools with available updates
|
|
92
|
+
- `POST /api/tools/refresh-all` - Refresh all tool statuses from package manager
|
|
87
93
|
|
|
88
94
|
## Tool Detection System
|
|
89
95
|
|
|
@@ -95,6 +101,29 @@ The system auto-detects installed AI coding tools via `bunx` package resolution:
|
|
|
95
101
|
|
|
96
102
|
Tool package names are configured in `lib/tool-manager.js` TOOLS array (lines 6-11). Detection happens by spawning `bunx <package> --version` to check if tools are installed. Response from `/api/tools` includes: id, name, pkg, installed, status (one of: installed|needs_update|not_installed), isUpToDate, upgradeNeeded, hasUpdate. Frontend displays tools in UI and updates based on installation status.
|
|
97
103
|
|
|
104
|
+
### Tool Installation and Update UI Flow
|
|
105
|
+
|
|
106
|
+
When user clicks Install/Update button on a tool:
|
|
107
|
+
|
|
108
|
+
1. **Frontend** (`static/js/tools-manager.js`):
|
|
109
|
+
- Immediately updates tool status to 'installing'/'updating' and re-renders UI
|
|
110
|
+
- Sends POST request to `/api/tools/{id}/install` or `/api/tools/{id}/update`
|
|
111
|
+
- Adds toolId to `operationInProgress` to prevent duplicate requests
|
|
112
|
+
- Button becomes disabled showing progress indicator while install runs
|
|
113
|
+
|
|
114
|
+
2. **Backend** (`server.js` lines 1819-1851):
|
|
115
|
+
- Receives POST request, updates database status to 'installing'/'updating'
|
|
116
|
+
- Sends immediate response `{ success: true }`
|
|
117
|
+
- Asynchronously calls `toolManager.install/update()` in background
|
|
118
|
+
- Upon completion, broadcasts WebSocket event `tool_install_complete` or `tool_install_failed`
|
|
119
|
+
|
|
120
|
+
3. **Frontend WebSocket Handler** (`static/js/tools-manager.js` lines 138-151):
|
|
121
|
+
- Listens for `tool_install_complete` or `tool_install_failed` events
|
|
122
|
+
- Updates tool status and re-renders final state
|
|
123
|
+
- Removes toolId from `operationInProgress`, enabling button again
|
|
124
|
+
|
|
125
|
+
The UI shows progress in three phases: immediate "Installing" status, progress bar animation during install, and final "Installed"/"Failed" status when complete.
|
|
126
|
+
|
|
98
127
|
## WebSocket Protocol
|
|
99
128
|
|
|
100
129
|
Endpoint: `BASE_URL + /sync`
|
package/package.json
CHANGED
package/static/js/client.js
CHANGED
|
@@ -1890,7 +1890,7 @@ class AgentGUIClient {
|
|
|
1890
1890
|
if (this.ui.cliSelector) {
|
|
1891
1891
|
if (displayAgents.length > 0) {
|
|
1892
1892
|
this.ui.cliSelector.innerHTML = displayAgents
|
|
1893
|
-
.map(a => `<option value="${a.id}">${a.name}</option>`)
|
|
1893
|
+
.map(a => `<option value="${a.id}">${a.name.split(/[\s\-]+/)[0]}</option>`)
|
|
1894
1894
|
.join('');
|
|
1895
1895
|
this.ui.cliSelector.style.display = 'inline-block';
|
|
1896
1896
|
} else {
|
|
@@ -1923,7 +1923,7 @@ class AgentGUIClient {
|
|
|
1923
1923
|
const { subAgents } = await window.wsClient.rpc('agent.subagents', { id: cliAgentId });
|
|
1924
1924
|
if (subAgents && subAgents.length > 0 && this.ui.agentSelector) {
|
|
1925
1925
|
this.ui.agentSelector.innerHTML = subAgents
|
|
1926
|
-
.map(a => `<option value="${a.id}">${a.name}</option>`)
|
|
1926
|
+
.map(a => `<option value="${a.id}">${a.name.split(/[\s\-]+/)[0]}</option>`)
|
|
1927
1927
|
.join('');
|
|
1928
1928
|
this.ui.agentSelector.style.display = 'inline-block';
|
|
1929
1929
|
this.loadModelsForAgent(cliAgentId);
|
package/static/js/voice.js
CHANGED
|
@@ -508,6 +508,9 @@
|
|
|
508
508
|
var streamingSupported = true;
|
|
509
509
|
var streamingFailedAt = 0;
|
|
510
510
|
|
|
511
|
+
var pendingVoiceUpdates = [];
|
|
512
|
+
var MAX_PENDING_UPDATES = 100;
|
|
513
|
+
|
|
511
514
|
function optimizePromptForSpeech(text) {
|
|
512
515
|
var optimizationInstructions = ' [Optimize for speech: Keep it short. Use simple words. Use short sentences. Focus on clarity.]';
|
|
513
516
|
return text + optimizationInstructions;
|
|
@@ -813,22 +816,29 @@
|
|
|
813
816
|
if (data.type === 'sync_connected') {
|
|
814
817
|
sendVoiceToServer();
|
|
815
818
|
}
|
|
819
|
+
if (data.type === 'streaming_progress' || data.type === 'message_created' || data.type === 'streaming_start') {
|
|
820
|
+
if (data.conversationId && data.conversationId !== currentConversationId) return;
|
|
821
|
+
if (!voiceActive) {
|
|
822
|
+
pendingVoiceUpdates.push(data);
|
|
823
|
+
if (pendingVoiceUpdates.length > MAX_PENDING_UPDATES) {
|
|
824
|
+
pendingVoiceUpdates.shift();
|
|
825
|
+
}
|
|
826
|
+
return;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
816
829
|
if (!voiceActive) return;
|
|
817
830
|
if (data.type === 'streaming_progress' && data.block) {
|
|
818
|
-
if (data.conversationId && data.conversationId !== currentConversationId) return;
|
|
819
831
|
if (data.seq !== undefined && renderedSeqs.has(data.seq)) return;
|
|
820
832
|
if (data.seq !== undefined) renderedSeqs.add(data.seq);
|
|
821
833
|
handleVoiceBlock(data.block, true, data.blockRole);
|
|
822
834
|
}
|
|
823
835
|
if (data.type === 'message_created' && data.message) {
|
|
824
|
-
if (data.conversationId && data.conversationId !== currentConversationId) return;
|
|
825
836
|
var message = data.message;
|
|
826
837
|
if (message.role === 'user' && message.content) {
|
|
827
838
|
handleVoiceBlock({ type: 'text', text: message.content }, true, 'user');
|
|
828
839
|
}
|
|
829
840
|
}
|
|
830
841
|
if (data.type === 'streaming_start') {
|
|
831
|
-
if (data.conversationId && data.conversationId !== currentConversationId) return;
|
|
832
842
|
spokenChunks = new Set();
|
|
833
843
|
renderedSeqs = new Set();
|
|
834
844
|
_voiceBreakNext = false;
|
|
@@ -838,13 +848,16 @@
|
|
|
838
848
|
var newConversationId = e.detail.conversationId;
|
|
839
849
|
if (currentConversationId && currentConversationId !== newConversationId) {
|
|
840
850
|
unsubscribeFromConversation();
|
|
851
|
+
pendingVoiceUpdates = [];
|
|
841
852
|
}
|
|
842
853
|
currentConversationId = newConversationId;
|
|
843
854
|
stopSpeaking();
|
|
844
855
|
spokenChunks = new Set();
|
|
845
856
|
renderedSeqs = new Set();
|
|
846
857
|
if (voiceActive) {
|
|
858
|
+
subscribeToConversation(currentConversationId);
|
|
847
859
|
loadVoiceBlocks(currentConversationId);
|
|
860
|
+
processPendingUpdates();
|
|
848
861
|
}
|
|
849
862
|
});
|
|
850
863
|
}
|
|
@@ -941,7 +954,9 @@
|
|
|
941
954
|
function activate() {
|
|
942
955
|
voiceActive = true;
|
|
943
956
|
if (currentConversationId) {
|
|
957
|
+
subscribeToConversation(currentConversationId);
|
|
944
958
|
loadVoiceBlocks(currentConversationId);
|
|
959
|
+
processPendingUpdates();
|
|
945
960
|
} else {
|
|
946
961
|
var container = document.getElementById('voiceMessages');
|
|
947
962
|
if (container && !container.hasChildNodes()) {
|
|
@@ -950,10 +965,35 @@
|
|
|
950
965
|
}
|
|
951
966
|
}
|
|
952
967
|
|
|
968
|
+
function processPendingUpdates() {
|
|
969
|
+
if (!voiceActive) return;
|
|
970
|
+
var updates = pendingVoiceUpdates.splice(0, pendingVoiceUpdates.length);
|
|
971
|
+
for (var i = 0; i < updates.length; i++) {
|
|
972
|
+
var data = updates[i];
|
|
973
|
+
if (data.type === 'streaming_progress' && data.block) {
|
|
974
|
+
if (data.seq !== undefined && renderedSeqs.has(data.seq)) continue;
|
|
975
|
+
if (data.seq !== undefined) renderedSeqs.add(data.seq);
|
|
976
|
+
handleVoiceBlock(data.block, true, data.blockRole);
|
|
977
|
+
}
|
|
978
|
+
if (data.type === 'message_created' && data.message) {
|
|
979
|
+
var message = data.message;
|
|
980
|
+
if (message.role === 'user' && message.content) {
|
|
981
|
+
handleVoiceBlock({ type: 'text', text: message.content }, true, 'user');
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
if (data.type === 'streaming_start') {
|
|
985
|
+
spokenChunks = new Set();
|
|
986
|
+
renderedSeqs = new Set();
|
|
987
|
+
_voiceBreakNext = false;
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
953
992
|
function deactivate() {
|
|
954
993
|
voiceActive = false;
|
|
955
994
|
stopSpeaking();
|
|
956
995
|
unsubscribeFromConversation();
|
|
996
|
+
pendingVoiceUpdates = [];
|
|
957
997
|
}
|
|
958
998
|
|
|
959
999
|
function escapeHtml(text) {
|