agentgui 1.0.186 → 1.0.188

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.
@@ -107,11 +107,34 @@ class AgentRunner {
107
107
  const errorText = chunk.toString();
108
108
  console.error(`[${this.id}] stderr:`, errorText);
109
109
 
110
- const rateLimitMatch = errorText.match(/rate.?limit|429|too many requests|overloaded|throttl/i);
110
+ const rateLimitMatch = errorText.match(/rate.?limit|429|too many requests|overloaded|throttl|hit your limit/i);
111
111
  if (rateLimitMatch) {
112
112
  rateLimited = true;
113
113
  const retryMatch = errorText.match(/retry.?after[:\s]+(\d+)/i);
114
- if (retryMatch) retryAfterSec = parseInt(retryMatch[1], 10) || 60;
114
+ if (retryMatch) {
115
+ retryAfterSec = parseInt(retryMatch[1], 10) || 60;
116
+ } else {
117
+ const resetTimeMatch = errorText.match(/resets?\s+(?:at\s+)?(\d{1,2})(?::(\d{2}))?\s*(am|pm)?\s*\(?(UTC|[A-Z]{2,4})\)?/i);
118
+ if (resetTimeMatch) {
119
+ let hours = parseInt(resetTimeMatch[1], 10);
120
+ const minutes = resetTimeMatch[2] ? parseInt(resetTimeMatch[2], 10) : 0;
121
+ const period = resetTimeMatch[3]?.toLowerCase();
122
+ const tz = resetTimeMatch[4]?.toUpperCase() || 'UTC';
123
+
124
+ if (period === 'pm' && hours !== 12) hours += 12;
125
+ if (period === 'am' && hours === 12) hours = 0;
126
+
127
+ const now = new Date();
128
+ const resetTime = new Date(now);
129
+ resetTime.setUTCHours(hours, minutes, 0, 0);
130
+
131
+ if (resetTime <= now) {
132
+ resetTime.setUTCDate(resetTime.getUTCDate() + 1);
133
+ }
134
+
135
+ retryAfterSec = Math.max(60, Math.ceil((resetTime.getTime() - now.getTime()) / 1000));
136
+ }
137
+ }
115
138
  }
116
139
 
117
140
  if (onError) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentgui",
3
- "version": "1.0.186",
3
+ "version": "1.0.188",
4
4
  "description": "Multi-agent ACP client with real-time communication",
5
5
  "type": "module",
6
6
  "main": "server.js",
package/server.js CHANGED
@@ -352,7 +352,10 @@ const server = http.createServer(async (req, res) => {
352
352
  sendJSON(req, res, 201, { message, session, idempotencyKey });
353
353
 
354
354
  processMessageWithStreaming(conversationId, message.id, session.id, body.content, agentId)
355
- .catch(err => debugLog(`[messages] Uncaught error: ${err.message}`));
355
+ .catch(err => {
356
+ console.error(`[messages] Uncaught error for conv ${conversationId}:`, err.message);
357
+ debugLog(`[messages] Uncaught error: ${err.message}`);
358
+ });
356
359
  return;
357
360
  }
358
361
  }
@@ -837,6 +840,15 @@ function createChunkBatcher() {
837
840
 
838
841
  async function processMessageWithStreaming(conversationId, messageId, sessionId, content, agentId) {
839
842
  const startTime = Date.now();
843
+
844
+ const conv = queries.getConversation(conversationId);
845
+ if (!conv) {
846
+ console.error(`[stream] Conversation ${conversationId} not found, aborting`);
847
+ queries.updateSession(sessionId, { status: 'error', error: 'Conversation not found' });
848
+ queries.setIsStreaming(conversationId, false);
849
+ return;
850
+ }
851
+
840
852
  activeExecutions.set(conversationId, { pid: null, startTime, sessionId, lastActivity: startTime });
841
853
  queries.setIsStreaming(conversationId, true);
842
854
  queries.updateSession(sessionId, { status: 'active' });
@@ -845,7 +857,6 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
845
857
  try {
846
858
  debugLog(`[stream] Starting: conversationId=${conversationId}, sessionId=${sessionId}`);
847
859
 
848
- const conv = queries.getConversation(conversationId);
849
860
  const cwd = conv?.workingDirectory || STARTUP_CWD;
850
861
  const resumeSessionId = conv?.claudeSessionId || null;
851
862
 
@@ -1014,10 +1025,35 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
1014
1025
  });
1015
1026
 
1016
1027
  if (isRateLimit) {
1028
+ const existingState = rateLimitState.get(conversationId) || {};
1029
+ const retryCount = (existingState.retryCount || 0) + 1;
1030
+ const maxRateLimitRetries = 3;
1031
+
1032
+ if (retryCount > maxRateLimitRetries) {
1033
+ debugLog(`[rate-limit] Conv ${conversationId} hit rate limit ${retryCount} times, giving up`);
1034
+ broadcastSync({
1035
+ type: 'streaming_error',
1036
+ sessionId,
1037
+ conversationId,
1038
+ error: `Rate limit exceeded after ${retryCount} attempts. Please try again later.`,
1039
+ recoverable: false,
1040
+ timestamp: Date.now()
1041
+ });
1042
+ const errorMessage = queries.createMessage(conversationId, 'assistant', `Error: Rate limit exceeded after ${retryCount} attempts. Please try again later.`);
1043
+ broadcastSync({
1044
+ type: 'message_created',
1045
+ conversationId,
1046
+ message: errorMessage,
1047
+ timestamp: Date.now()
1048
+ });
1049
+ queries.setIsStreaming(conversationId, false);
1050
+ return;
1051
+ }
1052
+
1017
1053
  const cooldownMs = (error.retryAfterSec || 60) * 1000;
1018
1054
  const retryAt = Date.now() + cooldownMs;
1019
- rateLimitState.set(conversationId, { retryAt, cooldownMs });
1020
- debugLog(`[rate-limit] Conv ${conversationId} hit rate limit, retry in ${cooldownMs}ms`);
1055
+ rateLimitState.set(conversationId, { retryAt, cooldownMs, retryCount });
1056
+ debugLog(`[rate-limit] Conv ${conversationId} hit rate limit (attempt ${retryCount}/${maxRateLimitRetries}), retry in ${cooldownMs}ms`);
1021
1057
 
1022
1058
  broadcastSync({
1023
1059
  type: 'rate_limit_hit',
@@ -1025,14 +1061,18 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
1025
1061
  conversationId,
1026
1062
  retryAfterMs: cooldownMs,
1027
1063
  retryAt,
1064
+ retryCount,
1028
1065
  timestamp: Date.now()
1029
1066
  });
1030
1067
 
1031
1068
  batcher.drain();
1032
1069
 
1070
+ debugLog(`[rate-limit] Scheduling retry for conv ${conversationId} in ${cooldownMs}ms (attempt ${retryCount + 1})`);
1071
+
1033
1072
  setTimeout(() => {
1073
+ debugLog(`[rate-limit] Timeout fired for conv ${conversationId}, calling scheduleRetry`);
1034
1074
  rateLimitState.delete(conversationId);
1035
- debugLog(`[rate-limit] Conv ${conversationId} cooldown expired, restarting`);
1075
+ debugLog(`[rate-limit] Conv ${conversationId} cooldown expired, restarting (attempt ${retryCount + 1})`);
1036
1076
  broadcastSync({
1037
1077
  type: 'rate_limit_clear',
1038
1078
  conversationId,
@@ -1062,17 +1102,27 @@ async function processMessageWithStreaming(conversationId, messageId, sessionId,
1062
1102
  } finally {
1063
1103
  batcher.drain();
1064
1104
  activeExecutions.delete(conversationId);
1065
- queries.setIsStreaming(conversationId, false);
1066
1105
  if (!rateLimitState.has(conversationId)) {
1106
+ queries.setIsStreaming(conversationId, false);
1067
1107
  drainMessageQueue(conversationId);
1068
1108
  }
1069
1109
  }
1070
1110
  }
1071
1111
 
1072
1112
  function scheduleRetry(conversationId, messageId, content, agentId) {
1113
+ debugLog(`[rate-limit] scheduleRetry called for conv ${conversationId}, messageId=${messageId}`);
1114
+
1115
+ if (!content) {
1116
+ const conv = queries.getConversation(conversationId);
1117
+ const lastMsg = queries.getLastUserMessage(conversationId);
1118
+ content = lastMsg?.content || 'continue';
1119
+ debugLog(`[rate-limit] Recovered content from last message: ${content?.substring?.(0, 50)}...`);
1120
+ }
1121
+
1073
1122
  const newSession = queries.createSession(conversationId);
1074
1123
  queries.createEvent('session.created', { messageId, sessionId: newSession.id, retryReason: 'rate_limit' }, conversationId, newSession.id);
1075
1124
 
1125
+ debugLog(`[rate-limit] Broadcasting streaming_start for retry session ${newSession.id}`);
1076
1126
  broadcastSync({
1077
1127
  type: 'streaming_start',
1078
1128
  sessionId: newSession.id,
@@ -1082,8 +1132,12 @@ function scheduleRetry(conversationId, messageId, content, agentId) {
1082
1132
  timestamp: Date.now()
1083
1133
  });
1084
1134
 
1135
+ debugLog(`[rate-limit] Calling processMessageWithStreaming for retry`);
1085
1136
  processMessageWithStreaming(conversationId, messageId, newSession.id, content, agentId)
1086
- .catch(err => debugLog(`[retry] Error: ${err.message}`));
1137
+ .catch(err => {
1138
+ debugLog(`[rate-limit] Retry failed: ${err.message}`);
1139
+ console.error(`[rate-limit] Retry error for conv ${conversationId}:`, err);
1140
+ });
1087
1141
  }
1088
1142
 
1089
1143
  function drainMessageQueue(conversationId) {