@monotykamary/pi-retry 0.6.2 → 0.6.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 +9 -9
- package/retry.ts +36 -0
- package/src/error-patterns.ts +63 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monotykamary/pi-retry",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Extension suite for pi coding agent that handles 400/413 errors and connection errors with automatic retry",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"author": "Tom X Nguyen",
|
|
@@ -32,13 +32,6 @@
|
|
|
32
32
|
"src/",
|
|
33
33
|
"README.md"
|
|
34
34
|
],
|
|
35
|
-
"scripts": {
|
|
36
|
-
"test": "vitest run",
|
|
37
|
-
"test:watch": "vitest",
|
|
38
|
-
"test:coverage": "vitest run --coverage",
|
|
39
|
-
"typecheck": "tsc --noEmit",
|
|
40
|
-
"lint:dead": "knip --no-gitignore"
|
|
41
|
-
},
|
|
42
35
|
"devDependencies": {
|
|
43
36
|
"@earendil-works/pi-agent-core": "^0.79.8",
|
|
44
37
|
"@earendil-works/pi-coding-agent": "^0.79.8",
|
|
@@ -55,5 +48,12 @@
|
|
|
55
48
|
},
|
|
56
49
|
"overrides": {
|
|
57
50
|
"brace-expansion": "5.0.6"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"test": "vitest run",
|
|
54
|
+
"test:watch": "vitest",
|
|
55
|
+
"test:coverage": "vitest run --coverage",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"lint:dead": "knip --no-gitignore"
|
|
58
58
|
}
|
|
59
|
-
}
|
|
59
|
+
}
|
package/retry.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
isNonRetryableError,
|
|
10
10
|
isSilencedError,
|
|
11
11
|
hasMaxTokensStop,
|
|
12
|
+
isContextOverflowError,
|
|
12
13
|
isAssistantMessage,
|
|
13
14
|
getLastAssistantMessage,
|
|
14
15
|
calculateDelay,
|
|
@@ -290,6 +291,30 @@ export default function (pi: ExtensionAPI) {
|
|
|
290
291
|
return;
|
|
291
292
|
}
|
|
292
293
|
|
|
294
|
+
// Context overflow: defer to compaction. Do NOT retry here.
|
|
295
|
+
//
|
|
296
|
+
// triggerInvisibleContinue() calls agent.prompt([]) directly on the core
|
|
297
|
+
// Agent, bypassing AgentSession._handlePostAgentRun → _checkCompaction, so
|
|
298
|
+
// a pi-retry retry loop gets NO compaction. Retrying an overflow would
|
|
299
|
+
// re-send the same oversized context → overflow again → infinite loop
|
|
300
|
+
// (pi-retry's error loop is uncapped). Meanwhile pi-retry's _continueInProgress
|
|
301
|
+
// mutex would block pi-core's own compaction-retry (agent.continue()), so
|
|
302
|
+
// pi-core never gets to compact either.
|
|
303
|
+
//
|
|
304
|
+
// Instead, return without firing triggerInvisibleContinue. pi-core's
|
|
305
|
+
// _checkCompaction (which runs in _handlePostAgentRun regardless of
|
|
306
|
+
// extensions) detects the same overflow, compacts (statically via pi-vcc
|
|
307
|
+
// when installed), and retries once via agent.continue() with the reduced
|
|
308
|
+
// context. _continueInProgress stays false, so pi-core's continue is
|
|
309
|
+
// unblocked.
|
|
310
|
+
if (isContextOverflowError(lastAssistant)) {
|
|
311
|
+
ctx.ui.notify(
|
|
312
|
+
"Context overflow — deferring to compaction (auto-retry after compact).",
|
|
313
|
+
"info",
|
|
314
|
+
);
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
|
|
293
318
|
// Catch-all: retry ANY error except known permanent failures
|
|
294
319
|
if (hasRetryableError(lastAssistant)) {
|
|
295
320
|
const errorMsg = lastAssistant.errorMessage || "Unknown error";
|
|
@@ -429,6 +454,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
429
454
|
return;
|
|
430
455
|
}
|
|
431
456
|
|
|
457
|
+
// Context overflow: don't retry in place — reducing context is required.
|
|
458
|
+
// Compaction (pi-vcc / /compact) handles it and auto-retries. Retrying
|
|
459
|
+
// without compaction loops forever on a genuinely oversized payload.
|
|
460
|
+
if (isContextOverflowError(lastAssistant)) {
|
|
461
|
+
ctx.ui.notify(
|
|
462
|
+
"Context overflow — use /compact (or /pi-vcc) to reduce context. Compaction auto-retries.",
|
|
463
|
+
"info",
|
|
464
|
+
);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
|
|
432
468
|
// Auto-detect error type and trigger appropriate retry
|
|
433
469
|
if (has400or413Error(lastAssistant)) {
|
|
434
470
|
ctx.ui.notify("Manually retrying 400/413 error...", "info");
|
package/src/error-patterns.ts
CHANGED
|
@@ -64,6 +64,51 @@ const BUILTIN_HANDLED_PATTERNS = [
|
|
|
64
64
|
/retry\s*delay/i,
|
|
65
65
|
];
|
|
66
66
|
|
|
67
|
+
// Context-overflow error patterns. Mirrors pi-core's OVERFLOW_PATTERNS in
|
|
68
|
+
// @earendil-works/pi-ai/dist/utils/overflow.js so that pi-retry defers to
|
|
69
|
+
// compaction exactly when pi-core's _checkCompaction will detect overflow and
|
|
70
|
+
// compact + retry. kept in sync manually — pi-ai is not a direct dependency.
|
|
71
|
+
//
|
|
72
|
+
// Why these are NOT retried by pi-retry: retrying an overflow via prompt([])
|
|
73
|
+
// re-sends the same oversized context, so it overflows again → infinite loop
|
|
74
|
+
// (pi-retry's loop is uncapped for errors). pi-core instead compacts and
|
|
75
|
+
// retries once via agent.continue(); with static compaction (pi-vcc) that
|
|
76
|
+
// reliably reduces context, so the single retry succeeds.
|
|
77
|
+
const OVERFLOW_ERROR_PATTERNS = [
|
|
78
|
+
/prompt is too long/i,
|
|
79
|
+
/request_too_large/i,
|
|
80
|
+
/input is too long for requested model/i,
|
|
81
|
+
/exceeds the context window/i,
|
|
82
|
+
/exceeds (?:the )?(?:model'?s )?maximum context length(?: of [\d,]+ tokens?|\s*\([\d,]+\))/i,
|
|
83
|
+
/input token count.*exceeds the maximum/i,
|
|
84
|
+
/maximum prompt length is \d+/i,
|
|
85
|
+
/reduce the length of the messages/i,
|
|
86
|
+
/maximum context length is \d+ tokens/i,
|
|
87
|
+
/exceeds (?:the )?maximum allowed input length of [\d,]+ tokens?/i,
|
|
88
|
+
/input \(\d+ tokens\) is longer than the model'?s context length \(\d+ tokens\)/i,
|
|
89
|
+
/exceeds the limit of \d+/i,
|
|
90
|
+
/exceeds the available context size/i,
|
|
91
|
+
/greater than the context length/i,
|
|
92
|
+
/context window exceeds limit/i,
|
|
93
|
+
/exceeded model token limit/i,
|
|
94
|
+
/too large for model with \d+ maximum context length/i,
|
|
95
|
+
/model_context_window_exceeded/i,
|
|
96
|
+
/prompt too long; exceeded (?:max )?context length/i,
|
|
97
|
+
/context[_ ]length[_ ]exceeded/i,
|
|
98
|
+
/too many tokens/i,
|
|
99
|
+
/token limit exceeded/i,
|
|
100
|
+
/^4(?:00|13)\s*(?:status code)?\s*\(no body\)/i,
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
// Patterns that look like overflow but are actually rate limiting / throttling.
|
|
104
|
+
// Mirrors pi-core's NON_OVERFLOW_PATTERNS. Excluded from overflow detection so
|
|
105
|
+
// throttling errors are still retried (they are not context-size problems).
|
|
106
|
+
const NON_OVERFLOW_PATTERNS = [
|
|
107
|
+
/^(Throttling error|Service unavailable):/i,
|
|
108
|
+
/rate limit/i,
|
|
109
|
+
/too many requests/i,
|
|
110
|
+
];
|
|
111
|
+
|
|
67
112
|
// ── Blacklist: errors that are truly permanent and should NOT be retried ──
|
|
68
113
|
|
|
69
114
|
const NON_RETRYABLE_PATTERNS = [
|
|
@@ -109,6 +154,24 @@ export function hasConnectionError(message: AgentMessage): boolean {
|
|
|
109
154
|
return CONNECTION_ERROR_PATTERNS.some(p => p.test(message.errorMessage!));
|
|
110
155
|
}
|
|
111
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Returns true for an error assistant message whose errorMessage indicates a
|
|
159
|
+
* context-overflow (input exceeded the model's context window).
|
|
160
|
+
*
|
|
161
|
+
* Mirrors pi-core's isContextOverflow Case 1 (error-message patterns). The
|
|
162
|
+
* silent-overflow cases (stopReason "stop"/"length") are not errors and are
|
|
163
|
+
* never seen here — pi-core handles those in _checkCompaction directly.
|
|
164
|
+
*
|
|
165
|
+
* Callers should treat a true result as "defer to compaction, do NOT retry" —
|
|
166
|
+
* see OVERFLOW_ERROR_PATTERNS for rationale.
|
|
167
|
+
*/
|
|
168
|
+
export function isContextOverflowError(message: AgentMessage): boolean {
|
|
169
|
+
if (!isAssistantMessage(message)) return false;
|
|
170
|
+
if (message.stopReason !== "error" || !message.errorMessage) return false;
|
|
171
|
+
if (NON_OVERFLOW_PATTERNS.some(p => p.test(message.errorMessage!))) return false;
|
|
172
|
+
return OVERFLOW_ERROR_PATTERNS.some(p => p.test(message.errorMessage!));
|
|
173
|
+
}
|
|
174
|
+
|
|
112
175
|
// ── Universal retry check ──
|
|
113
176
|
|
|
114
177
|
/**
|