agentgui 1.0.571 → 1.0.573
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/lib/acp-manager.js +1 -1
- package/lib/claude-runner.js +16 -0
- package/package.json +1 -1
- package/server.js +29 -1
- package/static/index.html +2 -26
- package/static/js/client.js +33 -45
package/lib/acp-manager.js
CHANGED
|
@@ -148,7 +148,7 @@ export async function restart(agentId) {
|
|
|
148
148
|
}
|
|
149
149
|
|
|
150
150
|
export async function queryModels(agentId) {
|
|
151
|
-
const port =
|
|
151
|
+
const port = await ensureRunning(agentId);
|
|
152
152
|
if (!port) return [];
|
|
153
153
|
try {
|
|
154
154
|
const res = await fetch('http://127.0.0.1:' + port + '/models');
|
package/lib/claude-runner.js
CHANGED
|
@@ -105,6 +105,8 @@ class AgentRunner {
|
|
|
105
105
|
let sessionId = null;
|
|
106
106
|
let rateLimited = false;
|
|
107
107
|
let retryAfterSec = 60;
|
|
108
|
+
let authError = false;
|
|
109
|
+
let authErrorMessage = '';
|
|
108
110
|
|
|
109
111
|
const timeoutHandle = setTimeout(() => {
|
|
110
112
|
timedOut = true;
|
|
@@ -151,6 +153,12 @@ class AgentRunner {
|
|
|
151
153
|
const errorText = chunk.toString();
|
|
152
154
|
console.error(`[${this.id}] stderr:`, errorText);
|
|
153
155
|
|
|
156
|
+
const authMatch = errorText.match(/401|unauthorized|invalid.*auth|invalid.*token|auth.*failed|permission denied|access denied/i);
|
|
157
|
+
if (authMatch) {
|
|
158
|
+
authError = true;
|
|
159
|
+
authErrorMessage = errorText.trim();
|
|
160
|
+
}
|
|
161
|
+
|
|
154
162
|
const rateLimitMatch = errorText.match(/rate.?limit|429|too many requests|overloaded|throttl|hit your limit/i);
|
|
155
163
|
if (rateLimitMatch) {
|
|
156
164
|
rateLimited = true;
|
|
@@ -190,6 +198,14 @@ class AgentRunner {
|
|
|
190
198
|
clearTimeout(timeoutHandle);
|
|
191
199
|
if (timedOut) return;
|
|
192
200
|
|
|
201
|
+
if (authError) {
|
|
202
|
+
const err = new Error(`Authentication failed: ${authErrorMessage || 'Invalid credentials or unauthorized access'}`);
|
|
203
|
+
err.authError = true;
|
|
204
|
+
err.nonRetryable = true;
|
|
205
|
+
reject(err);
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
193
209
|
if (rateLimited) {
|
|
194
210
|
const err = new Error(`Rate limited - retry after ${retryAfterSec}s`);
|
|
195
211
|
err.rateLimited = true;
|
package/package.json
CHANGED
package/server.js
CHANGED
|
@@ -22,7 +22,7 @@ import { register as registerConvHandlers } from './lib/ws-handlers-conv.js';
|
|
|
22
22
|
import { register as registerSessionHandlers } from './lib/ws-handlers-session.js';
|
|
23
23
|
import { register as registerRunHandlers } from './lib/ws-handlers-run.js';
|
|
24
24
|
import { register as registerUtilHandlers } from './lib/ws-handlers-util.js';
|
|
25
|
-
import { startAll as startACPTools, stopAll as stopACPTools, getStatus as getACPStatus, getPort as getACPPort, queryModels as queryACPModels, touch as touchACP } from './lib/acp-manager.js';
|
|
25
|
+
import { startAll as startACPTools, stopAll as stopACPTools, getStatus as getACPStatus, getPort as getACPPort, ensureRunning, queryModels as queryACPModels, touch as touchACP } from './lib/acp-manager.js';
|
|
26
26
|
import { installGMAgentConfigs } from './lib/gm-agent-configs.js';
|
|
27
27
|
import * as toolManager from './lib/tool-manager.js';
|
|
28
28
|
import { pm2Manager } from './lib/pm2-manager.js';
|
|
@@ -480,6 +480,7 @@ async function getModelsForAgent(agentId) {
|
|
|
480
480
|
} else {
|
|
481
481
|
const agent = discoveredAgents.find(a => a.id === agentId);
|
|
482
482
|
if (agent?.protocol === 'acp') {
|
|
483
|
+
await ensureRunning(agentId);
|
|
483
484
|
try { models = await queryACPModels(agentId); } catch (_) {}
|
|
484
485
|
}
|
|
485
486
|
}
|
|
@@ -3862,6 +3863,9 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
3862
3863
|
return;
|
|
3863
3864
|
}
|
|
3864
3865
|
|
|
3866
|
+
const isAuthError = error.authError || error.nonRetryable ||
|
|
3867
|
+
/401|unauthorized|invalid.*auth|invalid.*token|auth.*failed|permission denied|access denied/i.test(error.message);
|
|
3868
|
+
|
|
3865
3869
|
const isRateLimit = error.rateLimited ||
|
|
3866
3870
|
/rate.?limit|429|too many requests|overloaded|throttl/i.test(error.message);
|
|
3867
3871
|
|
|
@@ -3871,6 +3875,30 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
|
|
|
3871
3875
|
completed_at: Date.now()
|
|
3872
3876
|
});
|
|
3873
3877
|
|
|
3878
|
+
if (isAuthError) {
|
|
3879
|
+
debugLog(`[auth-error] Auth error for conv ${conversationId}: ${error.message}`);
|
|
3880
|
+
broadcastSync({
|
|
3881
|
+
type: 'streaming_error',
|
|
3882
|
+
sessionId,
|
|
3883
|
+
conversationId,
|
|
3884
|
+
error: `Authentication failed: ${error.message}. Please check your API credentials.`,
|
|
3885
|
+
recoverable: false,
|
|
3886
|
+
isAuthError: true,
|
|
3887
|
+
timestamp: Date.now()
|
|
3888
|
+
});
|
|
3889
|
+
const errorMessage = queries.createMessage(conversationId, 'assistant', `Error: Authentication failed. ${error.message}. Please update your credentials and try again.`);
|
|
3890
|
+
broadcastSync({
|
|
3891
|
+
type: 'message_created',
|
|
3892
|
+
conversationId,
|
|
3893
|
+
message: errorMessage,
|
|
3894
|
+
timestamp: Date.now()
|
|
3895
|
+
});
|
|
3896
|
+
queries.setIsStreaming(conversationId, false);
|
|
3897
|
+
batcher.drain();
|
|
3898
|
+
activeExecutions.delete(conversationId);
|
|
3899
|
+
return;
|
|
3900
|
+
}
|
|
3901
|
+
|
|
3874
3902
|
if (isRateLimit) {
|
|
3875
3903
|
const existingState = rateLimitState.get(conversationId) || {};
|
|
3876
3904
|
const retryCount = (existingState.retryCount || 0) + 1;
|
package/static/index.html
CHANGED
|
@@ -968,25 +968,6 @@
|
|
|
968
968
|
.queue-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
969
969
|
.queue-btn.visible { display: flex; }
|
|
970
970
|
|
|
971
|
-
.steer-btn {
|
|
972
|
-
display: none;
|
|
973
|
-
align-items: center;
|
|
974
|
-
justify-content: center;
|
|
975
|
-
width: 40px;
|
|
976
|
-
height: 40px;
|
|
977
|
-
background-color: #06b6d4;
|
|
978
|
-
color: white;
|
|
979
|
-
border: none;
|
|
980
|
-
border-radius: 0.5rem;
|
|
981
|
-
cursor: pointer;
|
|
982
|
-
flex-shrink: 0;
|
|
983
|
-
transition: background-color 0.15s;
|
|
984
|
-
margin-right: 0.25rem;
|
|
985
|
-
}
|
|
986
|
-
|
|
987
|
-
.steer-btn:hover:not(:disabled) { background-color: #0891b2; }
|
|
988
|
-
.steer-btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
989
|
-
.steer-btn.visible { display: flex; }
|
|
990
971
|
|
|
991
972
|
/* ===== OVERLAY for mobile sidebar ===== */
|
|
992
973
|
.sidebar-overlay {
|
|
@@ -3200,9 +3181,9 @@
|
|
|
3200
3181
|
</svg>
|
|
3201
3182
|
</button>
|
|
3202
3183
|
</div>
|
|
3203
|
-
<button class="inject-btn" id="injectBtn" title="
|
|
3184
|
+
<button class="inject-btn" id="injectBtn" title="Steer or inject into running agent" aria-label="Steer/Inject">
|
|
3204
3185
|
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3205
|
-
<path d="
|
|
3186
|
+
<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"/>
|
|
3206
3187
|
</svg>
|
|
3207
3188
|
</button>
|
|
3208
3189
|
<button class="queue-btn" id="queueBtn" title="Queue message for running agent" aria-label="Queue message">
|
|
@@ -3210,11 +3191,6 @@
|
|
|
3210
3191
|
<path d="M15 13H1v-2h14v2zm0-4H1V7h14v2zM1 19h14v-2H1v2z"/>
|
|
3211
3192
|
</svg>
|
|
3212
3193
|
</button>
|
|
3213
|
-
<button class="steer-btn" id="steerBtn" title="Steer running agent with message" aria-label="Steer agent">
|
|
3214
|
-
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
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"/>
|
|
3216
|
-
</svg>
|
|
3217
|
-
</button>
|
|
3218
3194
|
<button class="stop-btn" id="stopBtn" title="Stop running agent (emergency)" aria-label="Stop agent">
|
|
3219
3195
|
<svg viewBox="0 0 24 24" fill="currentColor" width="18" height="18">
|
|
3220
3196
|
<path d="M6 6h12v12H6z"/>
|
package/static/js/client.js
CHANGED
|
@@ -467,7 +467,6 @@ class AgentGUIClient {
|
|
|
467
467
|
this.ui.stopButton = document.getElementById('stopBtn');
|
|
468
468
|
this.ui.injectButton = document.getElementById('injectBtn');
|
|
469
469
|
this.ui.queueButton = document.getElementById('queueBtn');
|
|
470
|
-
this.ui.steerButton = document.getElementById('steerBtn');
|
|
471
470
|
|
|
472
471
|
if (this.ui.stopButton) {
|
|
473
472
|
this.ui.stopButton.addEventListener('click', async () => {
|
|
@@ -484,13 +483,34 @@ class AgentGUIClient {
|
|
|
484
483
|
if (this.ui.injectButton) {
|
|
485
484
|
this.ui.injectButton.addEventListener('click', async () => {
|
|
486
485
|
if (!this.state.currentConversation) return;
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
const
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
486
|
+
const isStreaming = this.state.streamingConversations.has(this.state.currentConversation.id);
|
|
487
|
+
|
|
488
|
+
if (isStreaming) {
|
|
489
|
+
const message = this.ui.messageInput?.value || '';
|
|
490
|
+
if (!message.trim()) {
|
|
491
|
+
this.showError('Please enter a message to steer');
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
try {
|
|
495
|
+
const data = await window.wsClient.rpc('conv.steer', { id: this.state.currentConversation.id, content: message });
|
|
496
|
+
console.log('Steer response:', data);
|
|
497
|
+
if (this.ui.messageInput) {
|
|
498
|
+
this.ui.messageInput.value = '';
|
|
499
|
+
this.ui.messageInput.style.height = 'auto';
|
|
500
|
+
}
|
|
501
|
+
} catch (err) {
|
|
502
|
+
console.error('Failed to steer:', err);
|
|
503
|
+
this.showError('Failed to steer: ' + err.message);
|
|
504
|
+
}
|
|
505
|
+
} else {
|
|
506
|
+
const instructions = await window.UIDialog.prompt('Enter instructions to inject into the running agent:', '', 'Inject Instructions');
|
|
507
|
+
if (!instructions) return;
|
|
508
|
+
try {
|
|
509
|
+
const data = await window.wsClient.rpc('conv.inject', { id: this.state.currentConversation.id, content: instructions });
|
|
510
|
+
console.log('Inject response:', data);
|
|
511
|
+
} catch (err) {
|
|
512
|
+
console.error('Failed to inject:', err);
|
|
513
|
+
}
|
|
494
514
|
}
|
|
495
515
|
});
|
|
496
516
|
}
|
|
@@ -517,28 +537,6 @@ class AgentGUIClient {
|
|
|
517
537
|
});
|
|
518
538
|
}
|
|
519
539
|
|
|
520
|
-
if (this.ui.steerButton) {
|
|
521
|
-
this.ui.steerButton.addEventListener('click', async () => {
|
|
522
|
-
if (!this.state.currentConversation) return;
|
|
523
|
-
const message = this.ui.messageInput?.value || '';
|
|
524
|
-
if (!message.trim()) {
|
|
525
|
-
this.showError('Please enter a message to steer');
|
|
526
|
-
return;
|
|
527
|
-
}
|
|
528
|
-
try {
|
|
529
|
-
const data = await window.wsClient.rpc('conv.steer', { id: this.state.currentConversation.id, content: message });
|
|
530
|
-
console.log('Steer response:', data);
|
|
531
|
-
if (this.ui.messageInput) {
|
|
532
|
-
this.ui.messageInput.value = '';
|
|
533
|
-
this.ui.messageInput.style.height = 'auto';
|
|
534
|
-
}
|
|
535
|
-
} catch (err) {
|
|
536
|
-
console.error('Failed to steer:', err);
|
|
537
|
-
this.showError('Failed to steer: ' + err.message);
|
|
538
|
-
}
|
|
539
|
-
});
|
|
540
|
-
}
|
|
541
|
-
|
|
542
540
|
if (this.ui.messageInput) {
|
|
543
541
|
this.ui.messageInput.addEventListener('keydown', (e) => {
|
|
544
542
|
if (e.key === 'Enter' && e.ctrlKey) {
|
|
@@ -990,10 +988,9 @@ class AgentGUIClient {
|
|
|
990
988
|
console.error('Streaming error:', data);
|
|
991
989
|
this._clearThinkingCountdown();
|
|
992
990
|
|
|
993
|
-
// Hide stop
|
|
991
|
+
// Hide stop and inject buttons on error
|
|
994
992
|
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
995
993
|
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
996
|
-
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
997
994
|
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
998
995
|
|
|
999
996
|
const conversationId = data.conversationId || this.state.currentSession?.conversationId;
|
|
@@ -2515,7 +2512,6 @@ class AgentGUIClient {
|
|
|
2515
2512
|
if (this.ui.stopButton) this.ui.stopButton.classList.remove('visible');
|
|
2516
2513
|
if (this.ui.injectButton) this.ui.injectButton.classList.remove('visible');
|
|
2517
2514
|
if (this.ui.queueButton) this.ui.queueButton.classList.remove('visible');
|
|
2518
|
-
if (this.ui.steerButton) this.ui.steerButton.classList.remove('visible');
|
|
2519
2515
|
if (this.ui.sendButton) this.ui.sendButton.style.display = '';
|
|
2520
2516
|
|
|
2521
2517
|
var prevId = this.state.currentConversation?.id;
|
|
@@ -2831,11 +2827,10 @@ class AgentGUIClient {
|
|
|
2831
2827
|
const isConnected = this.wsManager?.isConnected;
|
|
2832
2828
|
|
|
2833
2829
|
const injectBtn = document.getElementById('injectBtn');
|
|
2834
|
-
const steerBtn = document.getElementById('steerBtn');
|
|
2835
2830
|
const queueBtn = document.getElementById('queueBtn');
|
|
2836
2831
|
const stopBtn = document.getElementById('stopBtn');
|
|
2837
2832
|
|
|
2838
|
-
[injectBtn,
|
|
2833
|
+
[injectBtn, queueBtn, stopBtn].forEach(btn => {
|
|
2839
2834
|
if (!btn) return;
|
|
2840
2835
|
btn.classList.toggle('visible', isStreaming);
|
|
2841
2836
|
btn.disabled = !isConnected;
|
|
@@ -3112,13 +3107,10 @@ class AgentGUIClient {
|
|
|
3112
3107
|
if (this.ui.sendButton) {
|
|
3113
3108
|
this.ui.sendButton.disabled = !this.wsManager.isConnected;
|
|
3114
3109
|
}
|
|
3115
|
-
// Also disable queue
|
|
3110
|
+
// Also disable queue and inject buttons if disconnected
|
|
3116
3111
|
if (this.ui.injectButton && this.ui.injectButton.classList.contains('visible')) {
|
|
3117
3112
|
this.ui.injectButton.disabled = !this.wsManager.isConnected;
|
|
3118
3113
|
}
|
|
3119
|
-
if (this.ui.steerButton && this.ui.steerButton.classList.contains('visible')) {
|
|
3120
|
-
this.ui.steerButton.disabled = !this.wsManager.isConnected;
|
|
3121
|
-
}
|
|
3122
3114
|
if (this.ui.queueButton && this.ui.queueButton.classList.contains('visible')) {
|
|
3123
3115
|
this.ui.queueButton.disabled = !this.wsManager.isConnected;
|
|
3124
3116
|
}
|
|
@@ -3144,17 +3136,13 @@ class AgentGUIClient {
|
|
|
3144
3136
|
}
|
|
3145
3137
|
|
|
3146
3138
|
/**
|
|
3147
|
-
* Show queue/
|
|
3139
|
+
* Show queue/inject buttons when streaming (busy prompt state)
|
|
3148
3140
|
*/
|
|
3149
3141
|
showStreamingPromptButtons() {
|
|
3150
3142
|
if (this.ui.injectButton) {
|
|
3151
3143
|
this.ui.injectButton.classList.add('visible');
|
|
3152
3144
|
this.ui.injectButton.disabled = !this.wsManager.isConnected;
|
|
3153
3145
|
}
|
|
3154
|
-
if (this.ui.steerButton) {
|
|
3155
|
-
this.ui.steerButton.classList.add('visible');
|
|
3156
|
-
this.ui.steerButton.disabled = !this.wsManager.isConnected;
|
|
3157
|
-
}
|
|
3158
3146
|
if (this.ui.queueButton) {
|
|
3159
3147
|
this.ui.queueButton.classList.add('visible');
|
|
3160
3148
|
this.ui.queueButton.disabled = !this.wsManager.isConnected;
|
|
@@ -3162,7 +3150,7 @@ class AgentGUIClient {
|
|
|
3162
3150
|
}
|
|
3163
3151
|
|
|
3164
3152
|
/**
|
|
3165
|
-
* Ensure prompt area is always enabled and shows queue/
|
|
3153
|
+
* Ensure prompt area is always enabled and shows queue/inject when agent streaming
|
|
3166
3154
|
*/
|
|
3167
3155
|
ensurePromptAreaAlwaysEnabled() {
|
|
3168
3156
|
if (this.ui.messageInput) {
|