summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/executor/classifier.go3
-rw-r--r--internal/executor/claude.go4
-rw-r--r--internal/executor/executor.go20
-rw-r--r--internal/executor/ratelimit.go4
4 files changed, 22 insertions, 9 deletions
diff --git a/internal/executor/classifier.go b/internal/executor/classifier.go
index 123f92d..efd2acb 100644
--- a/internal/executor/classifier.go
+++ b/internal/executor/classifier.go
@@ -43,7 +43,8 @@ Gemini:
- gemini-2.5-pro (most powerful Gemini, larger context)
Selection Criteria:
-- Agent: You MUST prefer an agent that is NOT rate limited. If an agent is rate limited, do NOT select it unless all available agents are rate limited.
+- Agent: CRITICAL: You MUST select an agent where "Rate Limited: false". DO NOT select an agent where "Rate Limited: true" if any other agent is available and NOT rate limited.
+ Check the "System Status" section below. If it says "- Agent claude: ... Rate Limited: true", you MUST NOT select claude. Use gemini instead.
- Model: Select based on task complexity. Use powerful models (opus, pro, pro-preview) for complex reasoning/coding, flash-lite/flash/haiku for simple tasks.
Task:
diff --git a/internal/executor/claude.go b/internal/executor/claude.go
index ad1c4e3..9184333 100644
--- a/internal/executor/claude.go
+++ b/internal/executor/claude.go
@@ -433,7 +433,9 @@ func parseStream(r io.Reader, w io.Writer, logger *slog.Logger) (float64, error)
if info, ok := msg["rate_limit_info"].(map[string]interface{}); ok {
status, _ := info["status"].(string)
if status == "rejected" {
- streamErr = fmt.Errorf("claude rate limit reached: %v", msg)
+ streamErr = fmt.Errorf("claude rate limit reached (rejected): %v", msg)
+ // Immediately break since we can't continue anyway
+ break
}
}
case "assistant":
diff --git a/internal/executor/executor.go b/internal/executor/executor.go
index 1c9e667..4bb1f2c 100644
--- a/internal/executor/executor.go
+++ b/internal/executor/executor.go
@@ -234,14 +234,18 @@ func (p *Pool) executeResume(ctx context.Context, t *task.Task, exec *storage.Ex
exec.EndTime = time.Now().UTC()
if err != nil {
- if isRateLimitError(err) {
+ if isRateLimitError(err) || isQuotaExhausted(err) {
p.mu.Lock()
retryAfter := parseRetryAfter(err.Error())
if retryAfter == 0 {
- retryAfter = 1 * time.Minute
+ if isQuotaExhausted(err) {
+ retryAfter = 5 * time.Hour
+ } else {
+ retryAfter = 1 * time.Minute
+ }
}
p.rateLimited[agentType] = time.Now().Add(retryAfter)
- p.logger.Info("agent rate limited", "agent", agentType, "retryAfter", retryAfter)
+ p.logger.Info("agent rate limited", "agent", agentType, "retryAfter", retryAfter, "quotaExhausted", isQuotaExhausted(err))
p.mu.Unlock()
}
@@ -445,14 +449,18 @@ func (p *Pool) execute(ctx context.Context, t *task.Task) {
exec.EndTime = time.Now().UTC()
if err != nil {
- if isRateLimitError(err) {
+ if isRateLimitError(err) || isQuotaExhausted(err) {
p.mu.Lock()
retryAfter := parseRetryAfter(err.Error())
if retryAfter == 0 {
- retryAfter = 1 * time.Minute
+ if isQuotaExhausted(err) {
+ retryAfter = 5 * time.Hour
+ } else {
+ retryAfter = 1 * time.Minute
+ }
}
p.rateLimited[agentType] = time.Now().Add(retryAfter)
- p.logger.Info("agent rate limited", "agent", agentType, "retryAfter", retryAfter)
+ p.logger.Info("agent rate limited", "agent", agentType, "retryAfter", retryAfter, "quotaExhausted", isQuotaExhausted(err))
p.mu.Unlock()
}
diff --git a/internal/executor/ratelimit.go b/internal/executor/ratelimit.go
index deaad18..aa9df99 100644
--- a/internal/executor/ratelimit.go
+++ b/internal/executor/ratelimit.go
@@ -34,7 +34,9 @@ func isQuotaExhausted(err error) bool {
}
msg := strings.ToLower(err.Error())
return strings.Contains(msg, "hit your limit") ||
- strings.Contains(msg, "you've hit your limit")
+ strings.Contains(msg, "you've hit your limit") ||
+ strings.Contains(msg, "you have hit your limit") ||
+ strings.Contains(msg, "rate limit reached (rejected)")
}
// parseRetryAfter extracts a Retry-After duration from an error message.