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.
@@ -148,7 +148,7 @@ export async function restart(agentId) {
148
148
  }
149
149
 
150
150
  export async function queryModels(agentId) {
151
- const port = getPort(agentId);
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');
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.571",
3
+ "version": "1.0.573",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
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="Inject instructions into running agent" aria-label="Inject instructions">
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="M20 12l-1.41-1.41L13 16.17V4h-2v12.17l-5.58-5.59L4 12l8 8 8-8z"/>
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"/>
@@ -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 instructions = await window.UIDialog.prompt('Enter instructions to inject into the running agent:', '', 'Inject Instructions');
488
- if (!instructions) return;
489
- try {
490
- const data = await window.wsClient.rpc('conv.inject', { id: this.state.currentConversation.id, content: instructions });
491
- console.log('Inject response:', data);
492
- } catch (err) {
493
- console.error('Failed to inject:', err);
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, steer, and inject buttons on error
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, steerBtn, queueBtn, stopBtn].forEach(btn => {
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/steer buttons if disconnected
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/steer buttons when streaming (busy prompt state)
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/steer when agent streaming
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) {