@hienlh/ppm 0.9.61 → 0.9.62
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/CHANGELOG.md +5 -0
- package/package.json +1 -1
- package/src/providers/claude-agent-sdk.ts +39 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.9.62] - 2026-04-08
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
- **SDK subprocess crash auto-retry**: When the Claude Code subprocess crashes (exit code 1), PPM now automatically retries once with a fresh subprocess after a 1s delay, instead of immediately showing the error. Only surfaces the crash message if the retry also fails.
|
|
7
|
+
|
|
3
8
|
## [0.9.61] - 2026-04-08
|
|
4
9
|
|
|
5
10
|
### Fixed
|
package/package.json
CHANGED
|
@@ -702,6 +702,13 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
702
702
|
includePartialMessages: true,
|
|
703
703
|
};
|
|
704
704
|
|
|
705
|
+
// Crash retry: if subprocess exits with non-zero code before producing events,
|
|
706
|
+
// clean up and retry once with a fresh query before surfacing the error.
|
|
707
|
+
const MAX_CRASH_RETRIES = 1;
|
|
708
|
+
let crashRetryCount = 0;
|
|
709
|
+
|
|
710
|
+
crashRetryLoop: for (;;) {
|
|
711
|
+
try {
|
|
705
712
|
// Streaming input: create message channel and persistent query
|
|
706
713
|
const { generator: streamGen, controller: streamCtrl } = createMessageChannel();
|
|
707
714
|
const firstMsg = {
|
|
@@ -1373,28 +1380,45 @@ export class ClaudeAgentSdkProvider implements AIProvider {
|
|
|
1373
1380
|
}
|
|
1374
1381
|
break; // Exit retryLoop — normal completion
|
|
1375
1382
|
} // end retryLoop
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1383
|
+
break crashRetryLoop; // Normal completion — exit crash retry loop
|
|
1384
|
+
} catch (crashErr) {
|
|
1385
|
+
const crashMsg = (crashErr as Error).message ?? String(crashErr);
|
|
1386
|
+
console.error(`[sdk] session=${sessionId} cwd=${meta.projectPath} error: ${crashMsg}`);
|
|
1387
|
+
|
|
1388
|
+
// Clean up crashed subprocess before retry or error
|
|
1389
|
+
this.activeQueries.delete(sessionId);
|
|
1390
|
+
const ss = this.streamingSessions.get(sessionId);
|
|
1391
|
+
if (ss) { ss.controller.done(); ss.query.close(); this.streamingSessions.delete(sessionId); }
|
|
1392
|
+
console.log(`[sdk] session=${sessionId} streaming session ended`);
|
|
1393
|
+
|
|
1394
|
+
if (crashMsg.includes("abort") || crashMsg.includes("closed")) {
|
|
1380
1395
|
// User-initiated abort or WS closed — nothing to report
|
|
1381
|
-
} else if (
|
|
1382
|
-
// Subprocess crashed —
|
|
1383
|
-
|
|
1396
|
+
} else if (crashMsg.includes("exited with code") && crashRetryCount < MAX_CRASH_RETRIES) {
|
|
1397
|
+
// Subprocess crashed — auto-retry once before surfacing the error
|
|
1398
|
+
crashRetryCount++;
|
|
1399
|
+
console.warn(`[sdk] session=${sessionId} subprocess crashed: ${crashMsg} — auto-retrying (attempt ${crashRetryCount}/${MAX_CRASH_RETRIES})`);
|
|
1400
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
1401
|
+
continue crashRetryLoop;
|
|
1402
|
+
} else if (crashMsg.includes("exited with code")) {
|
|
1403
|
+
console.warn(`[sdk] session=${sessionId} subprocess crashed after retry: ${crashMsg}`);
|
|
1384
1404
|
yield { type: "error", message: `SDK subprocess crashed. Send another message to auto-recover.` };
|
|
1385
1405
|
} else {
|
|
1386
|
-
yield { type: "error", message: `SDK error: ${
|
|
1406
|
+
yield { type: "error", message: `SDK error: ${crashMsg}` };
|
|
1387
1407
|
}
|
|
1408
|
+
break crashRetryLoop; // Exit after error handling (non-retryable)
|
|
1409
|
+
}
|
|
1410
|
+
} // end crashRetryLoop
|
|
1411
|
+
|
|
1412
|
+
} catch (outerErr) {
|
|
1413
|
+
// Setup errors (account auth, env) — not retryable
|
|
1414
|
+
const msg = (outerErr as Error).message ?? String(outerErr);
|
|
1415
|
+
console.error(`[sdk] session=${sessionId} setup error: ${msg}`);
|
|
1416
|
+
yield { type: "error", message: `SDK error: ${msg}` };
|
|
1388
1417
|
} finally {
|
|
1418
|
+
// Final cleanup — ensure no leaked streaming session
|
|
1389
1419
|
this.activeQueries.delete(sessionId);
|
|
1390
|
-
// Properly close streaming session: terminate subprocess + generator
|
|
1391
1420
|
const ss = this.streamingSessions.get(sessionId);
|
|
1392
|
-
if (ss) {
|
|
1393
|
-
ss.controller.done();
|
|
1394
|
-
ss.query.close();
|
|
1395
|
-
this.streamingSessions.delete(sessionId);
|
|
1396
|
-
}
|
|
1397
|
-
console.log(`[sdk] session=${sessionId} streaming session ended`);
|
|
1421
|
+
if (ss) { ss.controller.done(); ss.query.close(); this.streamingSessions.delete(sessionId); }
|
|
1398
1422
|
}
|
|
1399
1423
|
|
|
1400
1424
|
// Final done event when query ends (crash, close, generator done)
|