@monotykamary/pi-retry 0.6.3 → 0.6.4

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 +3 -3
  2. package/retry.ts +30 -8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@monotykamary/pi-retry",
3
- "version": "0.6.3",
3
+ "version": "0.6.4",
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",
@@ -33,8 +33,8 @@
33
33
  "README.md"
34
34
  ],
35
35
  "devDependencies": {
36
- "@earendil-works/pi-agent-core": "^0.79.8",
37
- "@earendil-works/pi-coding-agent": "^0.79.8",
36
+ "@earendil-works/pi-agent-core": "^0.80.7",
37
+ "@earendil-works/pi-coding-agent": "^0.80.7",
38
38
  "@types/node": "25.9.1",
39
39
  "@vitest/coverage-v8": "4.1.7",
40
40
  "knip": "6.14.1",
package/retry.ts CHANGED
@@ -168,6 +168,8 @@ let _userAborted = false;
168
168
  // automatic retry) race through waitForIdle() and both call prompt([]),
169
169
  // producing "Agent is already processing".
170
170
  let _continueInProgress = false;
171
+ // Session generation that owns the retry mutex and its Escape handler.
172
+ let _continueGeneration: number | null = null;
171
173
 
172
174
  // Timestamp of the last completed triggerInvisibleContinue().
173
175
  // Used by the continue() monkey-patch to avoid double continuation when
@@ -179,6 +181,8 @@ let _lastInvisibleContinueTime = 0;
179
181
  // when it changes — this handles /new and other session switches.
180
182
  let _sessionGeneration = 0;
181
183
 
184
+ let _terminalInputUnsubscribe: (() => void) | null = null;
185
+
182
186
  // Interruptible sleep: polls _userAborted and _sessionGeneration every
183
187
  // 100ms. Returns true if interrupted (abort or session change), false if
184
188
  // the full delay elapsed normally.
@@ -501,7 +505,7 @@ export default function (pi: ExtensionAPI) {
501
505
  });
502
506
 
503
507
  // Initialize
504
- pi.on("session_start", async () => {
508
+ pi.on("session_start", async (_event, ctx) => {
505
509
  // Bump the generation counter so any in-flight retry loop from a
506
510
  // previous session exits on its next checkpoint (within 100ms during
507
511
  // backoff sleep, or immediately after prompt([]) returns).
@@ -513,11 +517,29 @@ export default function (pi: ExtensionAPI) {
513
517
  stateOther.reset();
514
518
  stateContinuation.reset();
515
519
  // Do NOT reset _continueInProgress here — the in-flight loop's
516
- // finally block handles it conditionally (only if the generation
517
- // hasn't changed since the loop started). Resetting it here would
518
- // break the mutex invariant and could allow a second loop to start.
520
+ // finally block releases its owner token. Resetting it here could allow
521
+ // a second loop to start before the old one has settled.
519
522
  _lastInvisibleContinueTime = 0;
520
523
  _userAborted = false;
524
+
525
+ _terminalInputUnsubscribe?.();
526
+ _terminalInputUnsubscribe = null;
527
+
528
+ if (ctx.mode === "tui") {
529
+ _terminalInputUnsubscribe = ctx.ui.onTerminalInput(data => {
530
+ if (
531
+ data !== "\x1b" ||
532
+ !_continueInProgress ||
533
+ _continueGeneration !== _sessionGeneration
534
+ ) {
535
+ return undefined;
536
+ }
537
+
538
+ _userAborted = true;
539
+ ctx.abort();
540
+ return { consume: true };
541
+ });
542
+ }
521
543
  });
522
544
 
523
545
  // Retry loop driver — the core of pi-retry.
@@ -548,6 +570,7 @@ export default function (pi: ExtensionAPI) {
548
570
  // Capture the current session generation. If /new fires while we're
549
571
  // looping, _sessionGeneration will increment and the loop will exit.
550
572
  const myGeneration = _sessionGeneration;
573
+ _continueGeneration = myGeneration;
551
574
 
552
575
  try {
553
576
  // Wait for the current run to finish (activeRun resolves in
@@ -602,11 +625,10 @@ export default function (pi: ExtensionAPI) {
602
625
  // Error again — loop back for another attempt.
603
626
  }
604
627
  } finally {
605
- // Only reset the mutex if the session hasn't changed since we
606
- // started. If /new fired, a new retry loop may already own the
607
- // mutex — resetting it here would clobber that.
608
- if (_sessionGeneration === myGeneration) {
628
+ // Release the mutex only if this loop still owns it.
629
+ if (_continueGeneration === myGeneration) {
609
630
  _continueInProgress = false;
631
+ _continueGeneration = null;
610
632
  }
611
633
  _lastInvisibleContinueTime = Date.now();
612
634
  }