karajan-code 1.25.2 → 1.25.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "1.25.2",
3
+ "version": "1.25.3",
4
4
  "description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -100,9 +100,10 @@ export async function runCoderStage({ coderRoleInstance, coderRole, config, logg
100
100
  action: "standby",
101
101
  standbyInfo: {
102
102
  agent: coderRole.provider,
103
- cooldownMs: rateLimitCheck.cooldownMs,
103
+ cooldownMs: rateLimitCheck.cooldownMs || (rateLimitCheck.isProviderOutage ? 30000 : null),
104
104
  cooldownUntil: rateLimitCheck.cooldownUntil,
105
- message: rateLimitCheck.message
105
+ message: rateLimitCheck.message,
106
+ isProviderOutage: rateLimitCheck.isProviderOutage || false
106
107
  }
107
108
  };
108
109
  }
@@ -169,9 +170,10 @@ export async function runRefactorerStage({ refactorerRole, config, logger, emitt
169
170
  action: "standby",
170
171
  standbyInfo: {
171
172
  agent: refactorerRole.provider,
172
- cooldownMs: rateLimitCheck.cooldownMs,
173
+ cooldownMs: rateLimitCheck.cooldownMs || (rateLimitCheck.isProviderOutage ? 30000 : null),
173
174
  cooldownUntil: rateLimitCheck.cooldownUntil,
174
- message: rateLimitCheck.message
175
+ message: rateLimitCheck.message,
176
+ isProviderOutage: rateLimitCheck.isProviderOutage || false
175
177
  }
176
178
  };
177
179
  }
@@ -738,9 +740,10 @@ export async function runReviewerStage({ reviewerRole, config, logger, emitter,
738
740
  action: "standby",
739
741
  standbyInfo: {
740
742
  agent: reviewerRole.provider,
741
- cooldownMs: rateLimitCheck.cooldownMs,
743
+ cooldownMs: rateLimitCheck.cooldownMs || (rateLimitCheck.isProviderOutage ? 30000 : null),
742
744
  cooldownUntil: rateLimitCheck.cooldownUntil,
743
- message: rateLimitCheck.message
745
+ message: rateLimitCheck.message,
746
+ isProviderOutage: rateLimitCheck.isProviderOutage || false
744
747
  }
745
748
  };
746
749
  }
@@ -485,10 +485,18 @@ async function handleStandbyResult({ stageResult, session, emitter, eventBase, i
485
485
  }
486
486
 
487
487
  const standbyRetries = session.standby_retry_count || 0;
488
+ const isOutage = stageResult.standbyInfo.isProviderOutage;
489
+ const pauseReason = isOutage
490
+ ? `Provider outage (${stageResult.standbyInfo.message || "5xx/connection error"}) — retried ${standbyRetries} times. This is NOT a KJ or code problem.`
491
+ : `Rate limit standby exhausted after ${standbyRetries} retries. Agent: ${stageResult.standbyInfo.agent}`;
492
+
488
493
  if (standbyRetries >= MAX_STANDBY_RETRIES) {
494
+ session.last_reviewer_feedback = isOutage
495
+ ? "IMPORTANT: The previous interruption was caused by a provider outage (API 500 error), NOT by a problem in your code or in Karajan. Continue from where you left off."
496
+ : session.last_reviewer_feedback;
489
497
  await pauseSession(session, {
490
- question: `Rate limit standby exhausted after ${standbyRetries} retries. Agent: ${stageResult.standbyInfo.agent}`,
491
- context: { iteration: i, stage, reason: "standby_exhausted" }
498
+ question: pauseReason,
499
+ context: { iteration: i, stage, reason: isOutage ? "provider_outage" : "standby_exhausted" }
492
500
  });
493
501
  emitProgress(emitter, makeEvent(`${stage}:rate_limit`, { ...eventBase, stage }, {
494
502
  status: "paused",
@@ -77,22 +77,35 @@ const RATE_LIMIT_PATTERNS = [
77
77
  { pattern: /resource exhausted/i, agent: "gemini" },
78
78
  { pattern: /quota exceeded/i, agent: "gemini" },
79
79
 
80
- // Generic (match any agent)
80
+ // Generic rate limits (match any agent)
81
81
  { pattern: /rate limit/i, agent: "unknown" },
82
82
  { pattern: /token limit reached/i, agent: "unknown" },
83
83
  { pattern: /\b429\b/, agent: "unknown" },
84
84
  { pattern: /too many requests/i, agent: "unknown" },
85
85
  { pattern: /throttl/i, agent: "unknown" },
86
+
87
+ // Provider outages / transient errors (treat like rate limits → retry)
88
+ { pattern: /\b500\b.*(?:internal server error|error)/i, agent: "unknown" },
89
+ { pattern: /\b502\b|bad gateway/i, agent: "unknown" },
90
+ { pattern: /\b503\b|service unavailable/i, agent: "unknown" },
91
+ { pattern: /\b504\b|gateway timeout/i, agent: "unknown" },
92
+ { pattern: /overloaded/i, agent: "unknown" },
93
+ { pattern: /ECONNREFUSED|ECONNRESET|ETIMEDOUT|socket hang up/i, agent: "unknown" },
94
+ { pattern: /network error|fetch failed/i, agent: "unknown" },
86
95
  ];
87
96
 
88
97
  export function detectRateLimit({ stderr = "", stdout = "" }) {
89
98
  const combined = `${stderr}\n${stdout}`;
90
99
 
100
+ const PROVIDER_OUTAGE_PATTERNS = /\b50[0-4]\b|bad gateway|service unavailable|gateway timeout|overloaded|ECONNREFUSED|ECONNRESET|ETIMEDOUT|socket hang up|network error|fetch failed/i;
101
+
91
102
  for (const { pattern, agent } of RATE_LIMIT_PATTERNS) {
92
103
  if (pattern.test(combined)) {
93
104
  const matchedLine = combined.split("\n").find((l) => pattern.test(l)) || combined.trim();
105
+ const isProviderOutage = PROVIDER_OUTAGE_PATTERNS.test(matchedLine);
94
106
  return {
95
107
  isRateLimit: true,
108
+ isProviderOutage,
96
109
  agent,
97
110
  message: matchedLine.trim(),
98
111
  ...parseCooldown(matchedLine)
@@ -100,5 +113,5 @@ export function detectRateLimit({ stderr = "", stdout = "" }) {
100
113
  }
101
114
  }
102
115
 
103
- return { isRateLimit: false, agent: "", message: "", cooldownUntil: null, cooldownMs: null };
116
+ return { isRateLimit: false, isProviderOutage: false, agent: "", message: "", cooldownUntil: null, cooldownMs: null };
104
117
  }