@hienlh/ppm 0.9.60 → 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
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
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
|
+
|
|
8
|
+
## [0.9.61] - 2026-04-08
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Telegram message clarity**: Thinking blocks now wrapped in `<blockquote>` (indented with vertical bar), tool calls in `<pre>` blocks (monospace background). Much easier to distinguish from actual response text.
|
|
12
|
+
|
|
3
13
|
## [0.9.60] - 2026-04-08
|
|
4
14
|
|
|
5
15
|
### 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)
|
|
@@ -66,7 +66,7 @@ function renderSegments(segments: Segment[]): string {
|
|
|
66
66
|
return segments
|
|
67
67
|
.map((s) => {
|
|
68
68
|
if (s.type === "md") return markdownToTelegramHtml(s.text);
|
|
69
|
-
if (s.type === "thinking") return `\n<
|
|
69
|
+
if (s.type === "thinking") return `\n<blockquote>💭 <i>${escapeHtml(s.text)}</i></blockquote>\n`;
|
|
70
70
|
return s.text;
|
|
71
71
|
})
|
|
72
72
|
.join("");
|
|
@@ -192,7 +192,7 @@ export async function streamToTelegram(
|
|
|
192
192
|
const inputPreview = formatToolInput(event.input);
|
|
193
193
|
appendHtml(
|
|
194
194
|
segments,
|
|
195
|
-
`\n
|
|
195
|
+
`\n<pre>🔧 ${escapeHtml(toolName)}: ${escapeHtml(inputPreview)}</pre>\n`,
|
|
196
196
|
);
|
|
197
197
|
await editCurrent();
|
|
198
198
|
}
|
|
@@ -203,7 +203,7 @@ export async function streamToTelegram(
|
|
|
203
203
|
if (config.showToolCalls && event.isError) {
|
|
204
204
|
appendHtml(
|
|
205
205
|
segments,
|
|
206
|
-
`\n
|
|
206
|
+
`\n<pre>⚠️ ${escapeHtml(event.output.slice(0, 200))}</pre>\n`,
|
|
207
207
|
);
|
|
208
208
|
await editCurrent();
|
|
209
209
|
}
|