@monotykamary/pi-retry 0.6.8 → 0.6.10

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.
Files changed (2) hide show
  1. package/package.json +9 -3
  2. package/retry.ts +40 -19
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monotykamary/pi-retry",
3
- "version": "0.6.8",
3
+ "version": "0.6.10",
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,9 +32,15 @@
32
32
  "src/",
33
33
  "README.md"
34
34
  ],
35
+ "peerDependencies": {
36
+ "@earendil-works/pi-agent-core": "*",
37
+ "@earendil-works/pi-coding-agent": "*",
38
+ "@earendil-works/pi-tui": "*"
39
+ },
35
40
  "devDependencies": {
36
- "@earendil-works/pi-agent-core": "^0.83.0",
37
- "@earendil-works/pi-coding-agent": "^0.83.0",
41
+ "@earendil-works/pi-agent-core": "^0.84.0",
42
+ "@earendil-works/pi-coding-agent": "^0.84.0",
43
+ "@earendil-works/pi-tui": "^0.84.0",
38
44
  "@types/node": "25.9.1",
39
45
  "@vitest/coverage-v8": "4.1.7",
40
46
  "knip": "6.14.1",
package/retry.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
+ import { matchesKey } from "@earendil-works/pi-tui";
2
3
  import { Agent } from "@earendil-works/pi-agent-core";
3
4
  import { AgentSession } from "@earendil-works/pi-coding-agent";
4
5
  import {
@@ -102,9 +103,10 @@ const stateOther = new RetryState();
102
103
  // Max_tokens continuation state (indefinite — no cap needed)
103
104
  const stateContinuation = new ContinuationState();
104
105
 
105
- // Abort flag: set when turn_end reports stopReason "aborted", cleared on
106
- // session_start and on fresh user activity. Prevents triggerInvisibleContinue()
107
- // from starting a hidden retry turn after the user explicitly cancelled.
106
+ // Abort flag: set when Pi's active signal is aborted or turn_end reports
107
+ // stopReason "aborted", cleared on session_start and fresh user activity.
108
+ // Prevents triggerInvisibleContinue() from starting a hidden retry turn after
109
+ // the user explicitly cancelled, even if a tool/provider reports an error.
108
110
  let _userAborted = false;
109
111
 
110
112
  // Mutex: only one triggerInvisibleContinue may be in-flight at a time.
@@ -186,25 +188,32 @@ export default function (pi: ExtensionAPI) {
186
188
 
187
189
  pi.on("input", () => {
188
190
  _inputGeneration++;
191
+ // The generation change cancels any older loop; the new user request gets
192
+ // its own retry eligibility even if the previous request was aborted.
193
+ _userAborted = false;
189
194
  });
190
195
 
191
196
  // Reset retry counters on successful completion (not max_tokens, not error)
192
197
  pi.on("turn_end", async (event, ctx) => {
193
198
  const msg = event.message as any;
199
+ if (
200
+ ctx.signal?.aborted ||
201
+ (msg.role === "assistant" && msg.stopReason === "aborted")
202
+ ) {
203
+ // User cancelled — reset retry state so it doesn't leak into other
204
+ // branches of the session tree. The signal check matters for tool calls:
205
+ // some tools/providers finish with an error-shaped result after abort.
206
+ state400.reset();
207
+ stateCredit.reset();
208
+ stateConnection.reset();
209
+ stateOther.reset();
210
+ stateContinuation.endContinuation();
211
+ // Signal to any in-flight triggerInvisibleContinue or pending retry
212
+ // that the user has cancelled — do not queue another retry turn.
213
+ _userAborted = true;
214
+ return;
215
+ }
194
216
  if (msg.role === "assistant" && msg.stopReason !== "error") {
195
- if (msg.stopReason === "aborted") {
196
- // User cancelled — reset retry state so it doesn't leak into other
197
- // branches of the session tree.
198
- state400.reset();
199
- stateCredit.reset();
200
- stateConnection.reset();
201
- stateOther.reset();
202
- stateContinuation.endContinuation();
203
- // Signal to any in-flight triggerInvisibleContinue or pending retry
204
- // that the user has cancelled — do not queue another retry turn.
205
- _userAborted = true;
206
- return;
207
- }
208
217
  if (msg.stopReason !== "length") {
209
218
  // Normal completion — reset everything including continuation count
210
219
  state400.succeed();
@@ -230,6 +239,14 @@ export default function (pi: ExtensionAPI) {
230
239
  // triggerInvisibleContinue(), which owns the retry loop with backoff
231
240
  // sleeps that happen AFTER processEvents returns (outside the agent run).
232
241
  pi.on("agent_end", async (event, ctx) => {
242
+ // Prefer Pi's run signal over provider-specific stop-reason mapping. An
243
+ // Escape during a Fabric/core tool may settle as an error-shaped result,
244
+ // but it is still a user cancellation and must never schedule a retry.
245
+ if (ctx.signal?.aborted) {
246
+ _userAborted = true;
247
+ return;
248
+ }
249
+
233
250
  const entries = ctx.sessionManager.getEntries();
234
251
  const lastAssistant = getLastAssistantMessage(entries);
235
252
 
@@ -480,16 +497,20 @@ export default function (pi: ExtensionAPI) {
480
497
  if (ctx.mode === "tui") {
481
498
  _terminalInputUnsubscribe = ctx.ui.onTerminalInput(data => {
482
499
  if (
483
- data !== "\x1b" ||
500
+ !matchesKey(data, "escape") ||
484
501
  !_continueInProgress ||
485
502
  _continueGeneration !== _sessionGeneration
486
503
  ) {
487
504
  return undefined;
488
505
  }
489
506
 
507
+ // Cancel pi-retry's out-of-band loop, but let Pi's native interrupt
508
+ // handler receive the same key. Pi owns the active turn and queue: its
509
+ // handler aborts the current tool/model call and clears any queued
510
+ // steer/follow-up messages. Consuming Escape here bypasses that cleanup
511
+ // and can let AgentSession continue after the user asked it to stop.
490
512
  _userAborted = true;
491
- ctx.abort();
492
- return { consume: true };
513
+ return undefined;
493
514
  });
494
515
  }
495
516
  });