@xn-intenton-z2a/agentic-lib 7.1.64 → 7.1.65
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
|
@@ -209,9 +209,44 @@ export function supportsReasoningEffort(model) {
|
|
|
209
209
|
return MODELS_SUPPORTING_REASONING_EFFORT.has(model);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Detect whether an error is an HTTP 429 Too Many Requests (rate limit) response.
|
|
214
|
+
* The Copilot SDK may surface this as an Error with a status, statusCode, or message.
|
|
215
|
+
*
|
|
216
|
+
* @param {unknown} err - The error to test
|
|
217
|
+
* @returns {boolean}
|
|
218
|
+
*/
|
|
219
|
+
export function isRateLimitError(err) {
|
|
220
|
+
if (!err || typeof err !== "object") return false;
|
|
221
|
+
const status = err.status ?? err.statusCode ?? err.code;
|
|
222
|
+
if (status === 429 || status === "429") return true;
|
|
223
|
+
const msg = (err.message || "").toLowerCase();
|
|
224
|
+
return msg.includes("429") || msg.includes("too many requests") || msg.includes("rate limit");
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Extract the retry delay in milliseconds from a rate-limit error.
|
|
229
|
+
* Tries the Retry-After header (seconds) first, then falls back to exponential backoff.
|
|
230
|
+
*
|
|
231
|
+
* @param {unknown} err - The error (may have headers or retryAfter)
|
|
232
|
+
* @param {number} attempt - Zero-based attempt number (0 = first retry)
|
|
233
|
+
* @param {number} [baseDelayMs=60000] - Base delay for exponential backoff (ms)
|
|
234
|
+
* @returns {number} Milliseconds to wait before retrying
|
|
235
|
+
*/
|
|
236
|
+
export function retryDelayMs(err, attempt, baseDelayMs = 60000) {
|
|
237
|
+
const retryAfterHeader =
|
|
238
|
+
err?.headers?.["retry-after"] ?? err?.retryAfter ?? err?.response?.headers?.["retry-after"];
|
|
239
|
+
if (retryAfterHeader != null) {
|
|
240
|
+
const parsed = Number(retryAfterHeader);
|
|
241
|
+
if (!isNaN(parsed) && parsed > 0) return parsed * 1000;
|
|
242
|
+
}
|
|
243
|
+
return baseDelayMs * Math.pow(2, attempt);
|
|
244
|
+
}
|
|
245
|
+
|
|
212
246
|
/**
|
|
213
247
|
* Run a Copilot SDK session and return the response.
|
|
214
248
|
* Handles the full lifecycle: create client → create session → send → stop.
|
|
249
|
+
* Retries automatically on HTTP 429 rate-limit responses (up to maxRetries times).
|
|
215
250
|
*
|
|
216
251
|
* @param {Object} options
|
|
217
252
|
* @param {string} options.model - Copilot SDK model name
|
|
@@ -221,6 +256,7 @@ export function supportsReasoningEffort(model) {
|
|
|
221
256
|
* @param {string} [options.githubToken] - Optional token; falls back to COPILOT_GITHUB_TOKEN env var.
|
|
222
257
|
* @param {Object} [options.tuning] - Tuning config (reasoningEffort, infiniteSessions)
|
|
223
258
|
* @param {string} [options.profileName] - Profile name for logging
|
|
259
|
+
* @param {number} [options.maxRetries=3] - Maximum number of retry attempts on rate-limit errors
|
|
224
260
|
* @returns {Promise<{content: string, tokensUsed: number}>}
|
|
225
261
|
*/
|
|
226
262
|
export async function runCopilotTask({
|
|
@@ -231,6 +267,38 @@ export async function runCopilotTask({
|
|
|
231
267
|
githubToken,
|
|
232
268
|
tuning,
|
|
233
269
|
profileName,
|
|
270
|
+
maxRetries = 3,
|
|
271
|
+
}) {
|
|
272
|
+
const profile = profileName || "unknown";
|
|
273
|
+
|
|
274
|
+
// Attempt 0 is the initial call; attempts 1..maxRetries are retries after 429s.
|
|
275
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
276
|
+
try {
|
|
277
|
+
return await _runCopilotTaskOnce({ model, systemMessage, prompt, writablePaths, githubToken, tuning, profileName: profile });
|
|
278
|
+
} catch (err) {
|
|
279
|
+
if (isRateLimitError(err) && attempt < maxRetries) {
|
|
280
|
+
const delayMs = retryDelayMs(err, attempt);
|
|
281
|
+
core.warning(
|
|
282
|
+
`[copilot] Rate limit (429) hit — waiting ${Math.round(delayMs / 1000)}s before retry ${attempt + 1}/${maxRetries}`,
|
|
283
|
+
);
|
|
284
|
+
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
285
|
+
} else {
|
|
286
|
+
throw err;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
// Unreachable: loop always returns or throws, but satisfies static analysis.
|
|
291
|
+
throw new Error("[copilot] runCopilotTask: all retry attempts exhausted");
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async function _runCopilotTaskOnce({
|
|
295
|
+
model,
|
|
296
|
+
systemMessage,
|
|
297
|
+
prompt,
|
|
298
|
+
writablePaths,
|
|
299
|
+
githubToken,
|
|
300
|
+
tuning,
|
|
301
|
+
profileName,
|
|
234
302
|
}) {
|
|
235
303
|
const profile = profileName || "unknown";
|
|
236
304
|
core.info(
|