@monotykamary/pi-retry 0.8.6 → 0.10.0
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/README.md +84 -15
- package/package.json +2 -1
- package/retry.ts +410 -118
- package/src/child-retry.ts +573 -0
- package/src/config.ts +482 -0
- package/src/index.ts +1 -0
- package/src/retry-logic.ts +14 -0
- package/src/session-registry.ts +240 -0
package/README.md
CHANGED
|
@@ -21,11 +21,11 @@ This extension automatically detects and retries **all** errors by default, with
|
|
|
21
21
|
|
|
22
22
|
| Error Type | Retry Behavior | Use Case |
|
|
23
23
|
|------------|----------------|----------|
|
|
24
|
-
| **Any retryable error** (catch-all) | **
|
|
25
|
-
| HTTP 400/413 | **
|
|
26
|
-
| Credit / payment errors | **
|
|
24
|
+
| **Any retryable error** (catch-all) | **Capped exponential retry** | Everything else — provider hiccups, stream exhaustion, credit issues, unknown errors |
|
|
25
|
+
| HTTP 400/413 | **Capped** with exponential backoff, NO compaction | Transient context overflow that might resolve |
|
|
26
|
+
| Credit / payment errors | **Capped** with exponential backoff | "Not Enough Credits", insufficient balance, 402 — top up and the retry loop auto-resumes |
|
|
27
27
|
| **Quota / session-limit / budget exhaustion** | **Not retried** — notify + stop | "You've hit your limit", `insufficient_quota`, "out of budget", suspended accounts |
|
|
28
|
-
| Connection errors | **
|
|
28
|
+
| Connection errors | **Capped** with exponential backoff | Network hiccups, connection drops, socket errors, stream exhaustion |
|
|
29
29
|
| Max tokens (`stopReason: "length"`) | **Auto-continue** indefinitely with hidden continuation turns | Model hits output token limit mid-generation |
|
|
30
30
|
| Empty / think-only stop (`stopReason: "stop"` with no text or tool calls) | **Nudge once** with a hidden continuation, then give up | Model ends its turn with no usable output (Anthropic empty responses with end_turn, thinking-only turns) |
|
|
31
31
|
|
|
@@ -43,7 +43,7 @@ By default, pi has built-in retry for some errors (rate limits, 5xx, overloaded)
|
|
|
43
43
|
|
|
44
44
|
## The Solution
|
|
45
45
|
|
|
46
|
-
This extension provides
|
|
46
|
+
This extension provides automatic retry for all errors with configurable exponential backoff and a maximum-delay failure limit (2s → 4s → 8s → ... → 60s by default).
|
|
47
47
|
|
|
48
48
|
**Philosophy: retry EVERYTHING by default.** The only things we skip are a tiny blacklist of known permanent failures (invalid API key, model not found, unsupported model, etc.).
|
|
49
49
|
|
|
@@ -51,9 +51,9 @@ This extension provides **automatic** infinite retry with sensible exponential b
|
|
|
51
51
|
- **Catch-all retry** — Any `stopReason: "error"` is retried, regardless of error message
|
|
52
52
|
- Automatic detection of 400/413, connection, credit, and stream exhaustion errors
|
|
53
53
|
- **Auto-continuation** when the model hits its max output tokens (`stopReason: "length"`) — indefinite, no cap, hidden from the TUI
|
|
54
|
-
- **
|
|
54
|
+
- **Retry cutoff** — Keeps retrying until success, abort, or the configured number of failures at the maximum delay
|
|
55
55
|
- **Auto-stop on quota/budget exhaustion** — Session limits, plan quotas, and budget caps ("You've hit your limit", "out of budget", `insufficient_quota`, suspended accounts) are detected and **not** retried, with a notification explaining why
|
|
56
|
-
- Exponential backoff with cap
|
|
56
|
+
- Exponential backoff with configurable base delay, cap, multiplier, and maximum-delay failure count
|
|
57
57
|
- **Hidden triggers** — provider-valid custom messages use `display: false`, so retries do not add TUI clutter
|
|
58
58
|
- Manual controls via unified `/retry` command
|
|
59
59
|
- Non-retryable errors are explicitly logged so you know why we didn't retry
|
|
@@ -121,15 +121,35 @@ Once loaded, the extension **automatically** detects and retries all errors.
|
|
|
121
121
|
|
|
122
122
|
## Configuration
|
|
123
123
|
|
|
124
|
-
|
|
124
|
+
The extension reads a `piRetry` object from Pi's settings files:
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
- `~/.pi/agent/settings.json` applies globally.
|
|
127
|
+
- `.pi/settings.json` overrides matching global values for the current project.
|
|
128
|
+
|
|
129
|
+
```json
|
|
130
|
+
{
|
|
131
|
+
"piRetry": {
|
|
132
|
+
"baseDelayMs": 10000,
|
|
133
|
+
"maxDelayMs": 3600000,
|
|
134
|
+
"multiplier": 2,
|
|
135
|
+
"maxRetriesAtMaxDelay": 3
|
|
136
|
+
}
|
|
137
|
+
}
|
|
131
138
|
```
|
|
132
139
|
|
|
140
|
+
The example above waits 10 seconds before the first retry, doubles each delay, caps the delay at one hour, and stops after three failed retries at that cap. Supported values are:
|
|
141
|
+
|
|
142
|
+
| Setting | Default | Description |
|
|
143
|
+
|---------|---------|-------------|
|
|
144
|
+
| `baseDelayMs` | `2000` | Delay before the first retry, in milliseconds |
|
|
145
|
+
| `maxDelayMs` | `60000` | Maximum delay between retries, in milliseconds |
|
|
146
|
+
| `multiplier` | `2` | Exponential backoff multiplier; must be at least `1` |
|
|
147
|
+
| `maxRetriesAtMaxDelay` | `3` | Failed ordinary retries allowed after the delay reaches `maxDelayMs` |
|
|
148
|
+
|
|
149
|
+
`piRetry` is separate from Pi's built-in `retry` object so the two retry policies do not share ambiguous settings. The extension disables Pi's native retry scheduler while it is loaded, while preserving Pi's compaction handling, so only one retry loop owns the backoff schedule.
|
|
150
|
+
|
|
151
|
+
Settings are read when the extension starts. Restart pi or use `/reload` after editing them.
|
|
152
|
+
|
|
133
153
|
---
|
|
134
154
|
|
|
135
155
|
## How It Works
|
|
@@ -234,11 +254,13 @@ npm run lint:dead
|
|
|
234
254
|
.
|
|
235
255
|
├── retry.ts # Main unified extension
|
|
236
256
|
├── src/ # Shared utilities (testable, DRY)
|
|
257
|
+
│ ├── config.ts # Settings-backed retry configuration
|
|
237
258
|
│ ├── error-patterns.ts # Error pattern matching, custom types, hasMaxTokensStop
|
|
238
259
|
│ ├── retry-logic.ts # Retry utilities (calculateDelay, RetryState, ContinuationState, etc.)
|
|
239
260
|
│ └── index.ts # Barrel exports
|
|
240
261
|
├── __tests__/ # Unit tests
|
|
241
262
|
│ └── unit/
|
|
263
|
+
│ ├── config.test.ts
|
|
242
264
|
│ ├── error-patterns.test.ts
|
|
243
265
|
│ └── retry-logic.test.ts
|
|
244
266
|
├── vitest.config.ts # Test configuration
|
|
@@ -249,7 +271,7 @@ npm run lint:dead
|
|
|
249
271
|
|
|
250
272
|
```bash
|
|
251
273
|
# Run all quality checks
|
|
252
|
-
npm test #
|
|
274
|
+
npm test # 224 tests
|
|
253
275
|
npm run typecheck # TypeScript type checking
|
|
254
276
|
npm run lint:dead # Dead code detection with knip
|
|
255
277
|
```
|
|
@@ -302,7 +324,7 @@ pi install npm:@georgebashi/pi-retry
|
|
|
302
324
|
|
|
303
325
|
## Limitations
|
|
304
326
|
|
|
305
|
-
-
|
|
327
|
+
- Pi's native retry scheduler is disabled while this extension is loaded so native and extension retries cannot interleave; Pi's compaction check still runs normally
|
|
306
328
|
- Error messages remain in the session history (but are invisible to the LLM)
|
|
307
329
|
- May hit the same error repeatedly if the issue is persistent (use `Ctrl+C` to abort)
|
|
308
330
|
- **Warning**: Retrying 400/413 without reducing context may fail repeatedly if the payload is genuinely too large
|
|
@@ -319,3 +341,50 @@ pi install npm:@georgebashi/pi-retry
|
|
|
319
341
|
## License
|
|
320
342
|
|
|
321
343
|
MIT
|
|
344
|
+
|
|
345
|
+
## Subagent retry policies
|
|
346
|
+
|
|
347
|
+
Sessions that should retry with a different policy can be selected by matching
|
|
348
|
+
their effective system prompt. This is user configuration: the extension ships
|
|
349
|
+
no built-in marker or identity check for any specific subagent tool.
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"piRetry": {
|
|
354
|
+
"subagents": {
|
|
355
|
+
"enabled": true,
|
|
356
|
+
"match": {
|
|
357
|
+
"systemPromptRegex": [
|
|
358
|
+
{
|
|
359
|
+
"pattern": "^<active_agent name=\"[^\"\\r\\n]+\"/>$",
|
|
360
|
+
"flags": "m"
|
|
361
|
+
}
|
|
362
|
+
]
|
|
363
|
+
},
|
|
364
|
+
"baseDelayMs": 1000,
|
|
365
|
+
"maxDelayMs": 10000,
|
|
366
|
+
"maxRetriesAtMaxDelay": 2
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
Semantics:
|
|
373
|
+
|
|
374
|
+
- Sessions whose effective system prompt matches any listed rule use the
|
|
375
|
+
inline child policy; rules combine with OR.
|
|
376
|
+
- Omitted child fields inherit from the effective top-level policy, field by
|
|
377
|
+
field. Project matcher lists replace global ones.
|
|
378
|
+
- A matching rule with `enabled: false` leaves pi's native retry scheduler
|
|
379
|
+
untouched; a missing, empty, malformed, or nonmatching configuration keeps
|
|
380
|
+
the ordinary `pi-retry` takeover.
|
|
381
|
+
- Patterns are compiled when settings resolve. Invalid syntax, unsupported or
|
|
382
|
+
duplicate flags, and malformed groups warn and become no-match instead of
|
|
383
|
+
matching everything.
|
|
384
|
+
- The extension must also be loaded in the target session (for pi-subagents
|
|
385
|
+
that means listing it in the child's `subagentOnlyExtensions`), not only in
|
|
386
|
+
the parent.
|
|
387
|
+
|
|
388
|
+
The example pattern above matches the `<active_agent name="…"/>` line that
|
|
389
|
+
`pi-subagents` prefixes to native child system prompts; substitute your own
|
|
390
|
+
marker if you select child sessions differently.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@monotykamary/pi-retry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
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",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"@earendil-works/pi-tui": "*"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
|
+
"@earendil-works/pi-ai": "0.85.1",
|
|
48
49
|
"@earendil-works/pi-agent-core": "0.85.1",
|
|
49
50
|
"@earendil-works/pi-coding-agent": "0.85.1",
|
|
50
51
|
"@earendil-works/pi-tui": "0.85.1",
|